one to many relationship in entity framewrok 4.1 code first - asp.net-mvc-3

I am initializing the entities like this:
TasksListDB db = new TasksListDB();
public ActionResult Index()
{
var tasks1 = new List<Task>()
{
new Task { Id=1, Name = "Task 1", Difficulty = 1, DateCreated = DateTime.Parse("1/12/2011") , IsDone= true },
new Task { Id=2, Name = "Task 2", Difficulty = 2, DateCreated = DateTime.Parse("11/2/2011") , IsDone = false}
};
var tasks2 = new List<Task>()
{
new Task { Id=3, Name = "Task 3", Difficulty = 3, DateCreated = DateTime.Parse("11/2/2011") , IsDone = false},
new Task { Id=4, Name = "Task 4", Difficulty = 5, DateCreated = DateTime.Parse("1/2/2010") , IsDone= true }
};
tasks1.ForEach(t => db.Tasks.Add(t));
tasks2.ForEach(t => db.Tasks.Add(t));
var Persons = new List<Person> {
new Person { Id= 1 , Age= 10, EmailAddress= "asif_hameed_37#hotmail.com", FirstName= "Asif", LastName="Hameed" },
new Person { Id= 2 , Age= 10, EmailAddress= "asif_hameed_37#hotmail.com", FirstName= "Asif", LastName="Hameed" },
new Person { Id= 3 , Age= 10, EmailAddress= "asif_hameed_37#hotmail.com", FirstName= "Asif", LastName="Hameed" },
new Person { Id= 4 , Age= 10, EmailAddress= "asif_hameed_37#hotmail.com", FirstName= "Asif", LastName="Hameed" }};
Persons.ForEach(p => db.Persons.Add(p));
return View(db.Tasks);
}
my db context looks like this:
public class TasksListDB : DbContext
{
public DbSet<Person> Persons { get; set; }
public DbSet<Task> Tasks { get; set; }
}
I want to assign tasks to persons how can I do this ?
My model looks like this :
public class Person
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public int Age { get; set; }
public string EmailAddress { get; set; }
public virtual ICollection<Task> Tasks { get; set; }
}
public class Task
{
public int Id { get; set; }
public string Name { get; set; }
[Required]
public DateTime DateCreated { get; set; }
[Required]
[Range(1,5,ErrorMessage="Difficulty must be between 1 and 5")]
public int Difficulty { get; set; }
public bool IsDone { get; set; }
public virtual Person Person { get; set; }
}
When I run my mvc application, Person and Task tables are created but juntion table PersonTask not created.
[EDIT]
I am assigning tasks like this in initializer class:
var t1 = new Task { Id = 1, Name = "Urgent Task", DateCreated = DateTime.Now, Difficulty = 2, IsDone = true };
var t2 = new Task { Id = 2, Name = "Business Task", DateCreated = DateTime.Now, Difficulty = 1, IsDone = true };
var t3 = new Task { Id = 3, Name = "Home Task", DateCreated = DateTime.Now, Difficulty = 2, IsDone = false };
context.Tasks.Add(t1);
context.Tasks.Add(t2);
context.Tasks.Add(t3);
var Persons = new List<Person> {
new Person { Id= 1 , Age= 40, EmailAddress= "asif_hameed_37#hotmail.com", FirstName= "Asif", LastName="Hameed", Tasks= new List<Task> { t1,t2 }},
new Person { Id= 2 , Age= 30, EmailAddress= "asif_hameed_37#hotmail.com", FirstName= "Asif", LastName="Hameed" , Tasks= new List<Task> { t1} },
new Person { Id= 3 , Age= 29, EmailAddress= "asif_hameed_37#hotmail.com", FirstName= "Asif", LastName="Hameed" , Tasks= new List<Task> { t3,t2 } },
new Person { Id= 4 , Age= 35, EmailAddress= "asif_hameed_37#hotmail.com", FirstName= "Asif", LastName="Hameed" , Tasks= new List<Task> { t1,t3 } }};
context.Persons.Add(Persons[0]);
context.Persons.Add(Persons[1]);
context.Persons.Add(Persons[2]);
context.Persons.Add(Persons[3]);
context.SaveChanges();
but when i see the task table in sql server, it looks like this:
Id Name DateCreated Difficulty IsDone Person_Id
1 Urgent Task 2011-04-24 19:32:03.990 2 1 4
2 Business Task 2011-04-24 19:32:03.990 1 1 3
3 Home Task 2011-04-24 19:32:03.990 2 0 4
I assume that person table should have tasks not the task table should have person Id
Please suggest solution.

Junction table will not be created because you have defined one-to-many relation not many-to-many. The Task entity in your model can be assigned only to single Person not to many persons.
If you want to assing Task to Person simply run:
person.Tasks.Add(task);

Related

Getting all elements concatenate in a string with LINQ

I have the following classes:
public class People
{
public int ID { get; set; }
public List<Right> Rights { get; set; }
}
public class Right
{
public int ID { get; set; }
public int Val { get; set; }
}
With the following values:
People: ID: 1
Right:
ID: 1
Val: 5
Right:
ID: 2
Val: 4
I would like to retrieve all Val rights (in a single string) for the people.
So for people with ID 1 : getting "5,4".
List<People> list = ...;
string result = string.Join(",", (people.First(p => p.ID == 1)).Rights.Select(r => r.Val));
Example :
List<People> people = new List<People>()
{
new People()
{
ID = 1, Rights = new List<Right>()
{
new Right() { ID = 1, Val = 5 },
new Right() { ID = 2, Val = 10 },
}
},
new People()
{
ID = 2, Rights = new List<Right>()
{
new Right() { ID = 1, Val = 6 },
new Right() { ID = 2, Val = 11 },
}
}
};
string result = string.Join(",", (people.First(p => p.ID == 1)).Rights.Select(r => r.Val));
Console.WriteLine(result);
Output: 5, 10
You can use SelectMany to flatten a structure, so something like:
var vals = people.Where(p => p.ID == 1)
.SelectMany(p => p.Rights.Select(r => Val));
var str = String.Join(",", vals.Select(v => v.ToString());

How to use foreach inside webgrid + MVC 3

I have webgrid as below in MVC:how to show list of values in a single row in column in webgrid.I am using foreach inside webgrid column but it is unrecognized
Col1 Col2 Col3
---------------
1 | zzz | t
| xxx |
| yyy |
---------------
2 | aaa | P
| bbb |
| ccc |
Above grid is just sample Example..i want to show data.
In 2nd column i want to show list of values in single row.Below code I tried,But i get Error foreach is invalid in webgrid.
In Model:
public class Employee
{
public string EmployeeId { get; set; }
public string EmployeeName { get; set; }
public string RowId { get; set; }
public string Error { get; set; }
public List<string> tbldata { get; set; }
public List<Employee> lstEmp { get; set; }
}
In Controller:
public ActionResult Index()
{
Employee emp = new Employee();
List<string> tbldata3 = new List<string>();
tbldata3.Add("xxx");
tbldata3.Add("yyy");
tbldata3.Add("zzz");
List<string> tbldata1 = new List<string>();
tbldata1.Add("gggh");
tbldata1.Add("hhhh");
tbldata1.Add("ffff");
List<string> tbldata2 = new List<string>();
tbldata2.Add("ppp");
tbldata2.Add("oooo");
tbldata2.Add("iii");
List<Employee> lstempo = new List<Employee>();
lstempo.Add(new Employee { EmployeeId = "100", EmployeeName = "aaa", RowId = "111", Error = "3434", tbldata = tbldata1 });
lstempo.Add(new Employee { EmployeeId = "101", EmployeeName = "BB", RowId = "222", Error = "6767", tbldata = tbldata2 });
lstempo.Add(new Employee { EmployeeId = "102", EmployeeName = "ccc", RowId = "333", Error = "898", tbldata = tbldata3 });
emp.lstEmp = lstempo;
return View(emp);
}
In View:
#{
var grid = new WebGrid(source: Model.lstEmp,
canSort: true,
rowsPerPage: 10,
ajaxUpdateContainerId: "grdCurrentReqDetails"
);
}
#grid.GetHtml(
tableStyle: "webGrid",
headerStyle: "gridHead",
alternatingRowStyle: "alt",
columns: grid.Columns
(
grid.Column("EmployeeId", header: "EmployeeId"),
grid.Column("EmployeeName", header: "EmployeeName"),
grid.Column("RowId", header: "RowId"),
grid.Column("Error", header: "Error"),
grid.Column("Checks", format: (item) => { return new HtmlString(
"<table><tr><td>"+
foreach(var t in item.tbldata)
{
t.ToString();
}
+"</td></tr></table>"
); })
)
)

MVC 3, code-first, and sample data

I'm using code-first, and am trying to generate some sample data, but am stuck.
var ts = new List<VehicleType>
{ new VehicleType { TypeName = "Car" },
new VehicleType { TypeName = "Truck" },
new VehicleType { TypeName = "Van" },
new VehicleType { TypeName = "SUV" },
new VehicleType { TypeName = "RV" }
};
var mr = new List<Manufacturer>{
new Manufacturer { ManufacturerName = "Ford" },
new Manufacturer { ManufacturerName = "Dodge" },
new Manufacturer { ManufacturerName = "Hyundai" },
new Manufacturer { ManufacturerName = "Mazda" },
new Manufacturer { ManufacturerName = "Honda" },
new Manufacturer { ManufacturerName = "Lexus" },
new Manufacturer { ManufacturerName = "Cadillac" },
new Manufacturer { ManufacturerName = "Jayco" }
};
var vm = new List<VehicleModel>{
new VehicleModel { ModelName = "Focus" },
new VehicleModel { ModelName = "Elantra" },
new VehicleModel { ModelName = "3" },
new VehicleModel { ModelName = "4X4" },
new VehicleModel { ModelName = "CRX" },
new VehicleModel { ModelName = "Element" },
new VehicleModel { ModelName = "SE 541" },
new VehicleModel { ModelName = "LE 4X" }
};
new List<Inventory>
{
new Inventory {VehicleModel = vm.Single(g => g.ModelName == "Focus"), Manufacturer = mr.Single(g => g.ManufacturerName == "Ford"), VehicleType = ts.Single(g => g.TypeName == "Car"), Price = 10999, Mileage = 241092, Year = 2001, Description = "Includes all G37x AWD Sport standard equipment FORD", Colour = "Tan", CarImageUrl = "/Content/Images/car1.jpg", DateReceived = "01/06/2011"},
}.ForEach(a => context.Inventorys.Add(a));
This works fine. My Inventory table in populated with the data from the other tables (VehicleType, Manufacturer, and VehicleModel). Here are their models:
public class Manufacturer
{
[Key]
public int ManufacturerId { get; set; }
public string ManufacturerName { get; set; }
}
public class VehicleModel
{
[Key]
public int ModelID { get; set; }
public string ModelName { get; set; }
}
public class VehicleType
{
[Key]
public int TypeId { get; set; }
public string TypeName { get; set; }
}
Now lets say the Manufacturer model had another attribute besides ManufacturerName, let's say it also has ManufacturerLocation. When I try to add that attribute into the Inventory list:
new List<Inventory>
{
new Inventory {VehicleModel = vm.Single(g => g.ModelName == "Focus"), Manufacturer = mr.Single(g => g.SomeAttributeFromManufacturerTable)
All attributes from the Manufacturer table will appear, but after I use one of those attributes, there doesn't seem to be an opportunity to enter in the rest of the attributes. Is this because it expects me to enter in the ManufacturerId attribute, and it will automatically associate the rest on the attributes through its primary key? I hope I'm making sense, I find this hard to explain.

How can I select data from multiple tables?

In my MVC project, I have some tables like this:
Form (FormID, SectionID)
Section (SectionID, SectionName)
SectionQuestion (SectionID, QuestionID)
Question (QuestionID, Content)
A form has multiple sections, and a section has some questions.
I can get all question for the FormID. But I want to get a list of sections (which contain questions) for the model.
It means in the view I want to do something like this:
#Model IEnumerable<MedialForm.Models.Sections>
foreach (var section in Model)
{
//Show questions
}
Can you guys help me? :)
You will not see a list of sections for a form, since the Form can only have 1 section. (i.e. the SectionID is defined in Form, and not FormID in Section). However the following Linq query will return the section and the associated questions for a specified FormID:
void Main()
{
var sections =
new []
{
new Section { SectionID = 1, SectionName = "SectionName1" },
new Section { SectionID = 2, SectionName = "SectionName2" }
};
var forms =
new []
{
new Form { FormID = 1, SectionID = 1 },
new Form { FormID = 2, SectionID = 1 },
new Form { FormID = 3, SectionID = 2 },
new Form { FormID = 4, SectionID = 2 }
};
var questions =
new[]
{
new Question { QuestionID = 1, Content = "Question1" },
new Question { QuestionID = 2, Content = "Question2" }
};
var sectionQuestions =
new[]
{
new SectionQuestion { SectionID = 1, QuestionID = 1 },
new SectionQuestion { SectionID = 2, QuestionID = 1 },
new SectionQuestion { SectionID = 2, QuestionID = 2 }
};
var formId = 4;
var result = forms
.Join(
sections,
f => f.SectionID,
s => s.SectionID,
(f, s) => new { Form = f, Section = s })
.Join(
sectionQuestions,
jfs => jfs.Section.SectionID,
sq => sq.SectionID,
(jfs, sq) => new { Form = jfs.Form, Section = jfs.Section, sq.QuestionID })
.Join(
questions,
jfsq => jfsq.QuestionID,
q => q.QuestionID,
(jfsq, q) => new { Form = jfsq.Form, Section = jfsq.Section, Question = q })
.Where(f => f.Form.FormID == formId)
.GroupBy(f => f.Section.SectionID)
.Select(grp => new { SectionID = grp.Key, Questions = grp.Select(g => g.Question)});
Console.WriteLine($"For Form: {formId} the following sections with their questions were found: {String.Join(", ", result.Select(r => $"SectionID: {r.SectionID}, QuestionIDs: [{String.Join(", ", r.Questions.Select(q => q.QuestionID))}]"))}");
}
public class Form
{
public Int32 FormID { get; set; }
public Int32 SectionID { get; set; }
}
public class Section
{
public Int32 SectionID { get; set; }
public String SectionName { get; set; }
}
public class SectionQuestion
{
public Int32 SectionID { get; set; }
public Int32 QuestionID { get; set; }
}
public class Question
{
public Int32 QuestionID { get; set; }
public String Content { get; set; }
}
This will return the following result:
For Form: 4 the following sections with their questions were found: SectionID: 2, QuestionIDs: [1, 2]

Using the ALL operator in linq to filter child items of EntitySet

I have a two objects as follows:
public class Item
{
public int ItemId {get;set;}
public string ItemName {get;set;}
public List<Tag> ItemTags {get;set;}
public DateTime DateCreated {get;set;}
}
public class Tag
{
public int TagId {get;set;}
public string TagName {get;set;}
}
These are LINQ-to-SQL objects, so the ItemTags will be an EntitySet.
I am trying to perform a search query where a user can provide a comma delimited list of tags as a search filter.
How do I filter my list of items to those which contains all of the tags in the comma delimited list.
EDIT2
e.g.
Item1 has tags of Apple, Banana, Orange
Item2 has tags of Banana, Orange
Item3 has tags of Pineapple, Orange
If the tag filter is "Banana, Orange" I need the results to be Item1 and Item2.
/EDIT2
This is what I have tried thus far:
string tags = "Manchester United,European Cup,2008";
List<string> tagsList = tags.Trim().ToLower()
.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
.Distinct(StringComparer.CurrentCultureIgnoreCase)
.ToList();
List<Item> itemList = ItemRepository.FetchAll();
var query = itemList
.OrderByDescending(p => p.DateCreated)
.ToList();
if (tagsList.Count() > 0)
{
query = query
.Where(p => p.ItemTags
.Select(q => q.TagName.ToLower())
.All(r => tagsList.Contains(r)))
.ToList();
}
However, this doesn't seem to work. Any ideas on what I am doing wrong please?
EDIT1: tags are trimmed and are 'lowercased'.
That because you're puting the tags from the items to lowercase, but not the searched tags.
With this modification it should work:
List<string> tagsList = tags
.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
.Select(s => s.ToLower())
.Distinct()
.ToList();
EDIT: OK, I see what the problem is: you're doing it backwards. You're searching for items that have only the tags that you're looking for.
Try that instead:
query =
(from item in query
let itemTags = p.ItemTags.Select(it => it.TagName.ToLower())
where tags.All(t => itemTags.Contains(t))
select item).ToList();
UPDATE: here's a version with the lambda syntax. It's pretty ugly because of the temporary anonymous type, but that's how the let clause translates to lambda...
query =
query.Select(item => new { item, itemTags = item.ItemTags.Select(it => it.TagName.ToLower()) })
.Where(x => tagsList.All(t => x.itemTags.Contains(t)))
.Select(x => x.item)
.ToList();
I think you need to do something like this:
var query = itemList.OrderByDescending(p => p.DateCreated).ToList();
var results = query.Where(i => i.ItemTags
.All(it => tagsList.Contains(it.TagName.ToLower())));
Then results should then be a list of matching items.
PS. Your code shows you fetching itemList as a List from your repository and then sorting by date created. This means the sorting isn't being done in the database. Once you turn something into a List you give up the benefits of deferred execution as you will bring back the entire collection into memory.
EDIT: Here's the test code to prove it works in Linq to Objects:
public class Item
{
public int ItemId { get; set; }
public string ItemName { get; set; }
public List<Tag> ItemTags { get; set; }
public DateTime DateCreated { get; set; }
}
public class Tag
{
public int TagId { get; set; }
public string TagName { get; set; }
}
class Program
{
static void Main(string[] args)
{
RunTags();
}
private static void RunTags()
{
Item i1 = new Item()
{
ItemId = 1,
ItemName = "Item1",
ItemTags = new List<Tag>() { new Tag { TagId = 1, TagName = "2008" }, new Tag { TagId = 2, TagName = "Donkey" } }
};
Item i2 = new Item()
{
ItemId = 2,
ItemName = "Item2",
ItemTags = new List<Tag>() { new Tag { TagId = 4, TagName = "Cat" }, new Tag { TagId = 2, TagName = "Donkey" }, new Tag { TagId = 3, TagName = "Seattle" } }
};
Item i3 = new Item()
{
ItemId = 3,
ItemName = "Item3",
ItemTags = new List<Tag>() { new Tag { TagId = 523, TagName = "Manchester united" }, new Tag { TagId = 10, TagName = "European Cup" }, new Tag { TagId = 1, TagName = "2008" } }
};
Item i4 = new Item()
{
ItemId = 4,
ItemName = "Item4",
ItemTags = new List<Tag>() { new Tag { TagId = 05, TagName = "Banana" }, new Tag { TagId = 140, TagName = "Foo" }, new Tag { TagId = 4, TagName = "Cat" } }
};
Item i5 = new Item()
{
ItemId = 5,
ItemName = "Item5",
ItemTags = new List<Tag>() { new Tag { TagId = 05, TagName = "Banana" }, new Tag { TagId = 140, TagName = "Foo" } }
};
List<Item> itemList = new List<Item>() { i1, i2, i3, i4, i5 };
string tags = "Manchester United,European Cup,2008";
List<string> tagsList = tags.Trim().ToLower()
.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
.Distinct(StringComparer.CurrentCultureIgnoreCase)
.ToList();
var query = itemList
.OrderByDescending(p => p.DateCreated).ToList();
var results = query.Where(i => i.ItemTags.All(it => tagsList.Contains(it.TagName.ToLower())));
foreach (var item in results)
{
Console.WriteLine(item.ItemName); // Should return "Item3"
}
Console.ReadLine();
}
If you want to match any of the tags in the Item's ItemTag list then just change All to Any i.e.
var results = query.Where(i => i.ItemTags.Any(it => tagsList.Contains(it.TagName.ToLower())));

Resources