Entity Framework Code First and populating join tables - asp.net-mvc-3

I been practicing with EF Code First, SQL Express, and ASP.Net MVC3.
When I run the website first the correct tables are generated by the FooInitializer and Student and Image are populated but for some reason the join table (StudentImages) is not being populated.
What could be the issue?
Tables: Student, Image, and StudentImages
public class Student
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Image> Images { get; set; }
}
public class Image
{
public int Id { get; set; }
public string Filename { get; set; }
public string Extension { get; set; }
public virtual ICollection<Student> Students { get; set; }
}
public class FooInitializer : DropCreateDatabaseIfModelChanges<DBContext>
{
protected override void Seed(DBContext context)
{
var students = new List<Student> {
new Student { Id = 1, Name = "John" },
new Student { Id = 2, Name = "Jane" }
};
students.ForEach(s => context.Students.Add(s));
context.SaveChanges();
var images = new List<Image> {
new Image { Id = 1, Filename = "IMG_4596.JPG", Extension = ".jpg" },
new Image { Id = 2, Filename = "IMG_4600.JPG", Extension = ".jpg" }
};
images.ForEach(i => context.Images.Add(i));
students[0].Images.Add(images[0]);
context.SaveChanges();
}
}

From what I can tell your Image class does not have a reference to the StudentID. Try adding:
public int StudentID { get; set; }
to the Image class maybe?
Also having an ICollection would mean that one image could have multiple students - is this correct? Maybe it should be a public virtual Student Student {...}
EDIT: Also I found this, with a many to many relationship (if thats what you need):
In your OnModelCreating() Method:
modelBuilder.Entity<Student>()
.HasMany(c => c.Images).WithMany(i => i.Students)
.Map(t => t.MapLeftKey("StudentId")
.MapRightKey("ImageID")
.ToTable("StudentImages"));
taken from this link that states:
A many-to-many relationship between the Instructor and Course
entities. The code specifies the table and column names for the join
table. Code First can configure the many-to-many relationship for you
without this code, but if you don't call it, you will get default
names such as InstructorInstructorID for the InstructorID column.
EDIT: Here is the code I used the other night, with my implementation of the code first MVC site:
var users = new List<User>
{
new User { UserID = new Guid(), Email = "me#me.com", LastOnline = DateTime.Now, Password = "pword", RegistrationDate = DateTime.Now, SecurityAnswer = "me", SecurityQuestion = "who?", Roles = new List<Role>() },
};
users.ForEach(s => context.Users.Add(s));
context.SaveChanges();
var roles = new List<Role>
{
new Role { RoleID = "Admin", Description = "Administration Users", Users = new List<User>() }
};
roles.ForEach(r => context.Roles.Add(r));
users[0].Roles.Add(roles[0]);
context.SaveChanges();
var userLicense = new List<UserLicense>
{
new UserLicense { AddDateTime = DateTime.Now, LicenseType = "Farmer", Manufacturer = "Motorola", Model = "Droid", PhoneIdentifier = "c0e4223a910f", UserID = users[0].UserID, User = new User() }
};
userLicense[0].User = users[0];
userLicense.ForEach(u => context.UserLicenses.Add(u));
context.SaveChanges();
userLicense[0].User = users[0];
context.SaveChanges();
Notice in each instantiated item, I am also instantiating a new referenced item within the parent object.
EDIT:
Ok try this:
var students = new List<Student> {
new Student { Id = 1, Name = "John", Images = new List<Image>() },
new Student { Id = 2, Name = "Jane", Images = new List<Image>() }
};
students.ForEach(s => context.Students.Add(s));
context.SaveChanges();
var images = new List<Image> {
new Image { Id = 1, Filename = "IMG_4596.JPG", Extension = ".jpg", Students = new List<Student>() },
new Image { Id = 2, Filename = "IMG_4600.JPG", Extension = ".jpg", Students = new List<Student>() }
};
images.ForEach(i => context.Images.Add(i));
students[0].Images.Add(images[0]);
students[1].Images.Add(images[1]);
context.SaveChanges();

Try adding this before saving changes for each student:
foreach (Image i in s1.Images)
context.ObjectStateManager.ChangeObjectState(i, System.Data.EntityState.Added);
Also try with System.Data.EntityState.Modified.
Hope this works...

Related

Linq - Join collection with child collection of other collection

I have a collection of users, each with a child collection of memberships. And then I also have a club collection. The child collection of each user is populated with memberships containing a club ID only. What I want is for each membership to also contain the club entity matching the club ID.
My entities:
public class User
{
public string Name { get; set; }
public IEnumerable<Membership> Memberships { get; set; }
}
public class Membership
{
public Guid ClubId { get; set; }
public Club Club { get; set; }
}
public class Club
{
public Guid Id { get; set; }
public string Name { get; set; }
}
Populated with data:
var club1Id = new Guid("FFEB77B9-1616-463B-B36E-F95F7E255FDE");
var club2Id = new Guid("7A6ECD38-5AEB-418B-9DD5-FBD777CE190C");
var users = new List<User>
{
new User
{
Name = "Patrick Stewart",
Memberships = new List<Membership>
{
new Membership { ClubId = club1Id },
new Membership { ClubId = club2Id }
}
},
new User
{
Name = "Brent Spiner",
Memberships = new List<Membership>
{
new Membership { ClubId = club1Id }
}
},
new User
{
Name = "Wesley Crusher",
Memberships = null
}
};
var clubs = new List<Club>
{
new Club
{
Id = club1Id,
Name = "Officers Club"
},
new Club
{
Id = club2Id,
Name = "Captains Club"
}
};
And then I need a LINQ query that adds the matched Club objects to the Membership objects.
So that when I print out users like this:
foreach (var user in users)
{
Console.WriteLine($"User: {user.Name}");
if (user.Memberships != null)
{
foreach (var membership in user.Memberships)
{
Console.WriteLine($" Membership: {membership.Club?.Name}");
}
}
}
It will look like this:
User: Patrick Stewart
Membership: Officers Club
Membership: Captains Club
User: Brent Spiner
Membership: Officers Club
User: Wesley Crusher
I want to do it in one go with a LINQ expression, so that I don't have to involve for-loops.
I ended up solving it with this LINQ expression:
users = users.Select(user => new {
user,
Memberships = user.Memberships?
.GroupJoin(clubs, membership => membership.ClubId, club => club.Id, (membership, club) => new { membership, club})
.SelectMany(mc => mc.club.DefaultIfEmpty(), (mc, club) =>
{
mc.membership.Club = club;
return mc.membership;
})
})
.Select(x =>
{
x.user.Memberships = x.Memberships;
return x.user;
})
.ToList();
LINQ is not intended to be used for modifying data, rather querying data and/or creating new data from data.
Instead, use a nested foreach loop.
First, create a Dictionary to make finding Club objects easier:
var clubDict = clubs.ToDictionary(c => c.Id);
Then, use the clubDict to modify each Membership to include the matching Club entity:
foreach (var user in users.Where(u => u.Memberships != null))
foreach (var membership in user.Memberships)
membership.Club = clubDict[membership.ClubId];

LINQ Query for populating collection inside another collection

I am looking forward to get a linq query for populating list of teachers and their respective divisons.
Here I have 2 classes Teacher and Division which are related by DivisionGroupID - GroupID
public class Teacher
{
public int ID { get; set; }
public string Name { get; set; }
public List<Division> lstDivison {get;set;}
public int DivisionGroupID { get; set; }
}
public class Division
{
public int GroupID { get; set; }
public string Name { get; set; }
public string Description { get; set; }
}
In main method List of both Teacher and Division will be populated
static void Main(string[] args)
{
Teacher obj = new Teacher { ID = 1, DivisionGroupID = 11, Name = "abcd" };
Teacher obj1 = new Teacher { ID = 2, DivisionGroupID = 12, Name = "efgh" };
List<Teacher> objList = new List<Teacher>();
objList.Add(obj);
objList.Add(obj1);
Division dv = new Division { GroupID = 11 ,Name="Division1",Description="first" };
Division dv1 = new Division { GroupID = 11, Name = "Division2", Description = "second" };
Division dv2 = new Division { GroupID = 11, Name = "Division3", Description = "third" };
Division dv3 = new Division { GroupID = 12, Name = "Division4", Description = "fourth" };
Division dv4 = new Division { GroupID = 12, Name = "Division5", Description = "fifth" };
Division dv5 = new Division { GroupID = 12, Name = "Division6", Description = "sixth" };
List<Division> lstDiv = new List<Division>();
lstDiv.Add(dv);
lstDiv.Add(dv1);
lstDiv.Add(dv2);
lstDiv.Add(dv3);
lstDiv.Add(dv4);
lstDiv.Add(dv5);
}
The requirement here is to get the list of teachers and populate the sublist of divisions each teachers holding. I got the solution based on 2 approaches.
Using sub query approach :
var upd = from teacher in objList
select new Teacher
{
ID = teacher.ID,
Name = teacher.Name,
lstDivison = (from div in lstDiv
where div.GroupID == teacher.DivisionGroupID
select new Division
{
Name = div.Name,
Description = div.Description
}).ToList()
};
Using Foeach loop through Teacher collection(objList) and updating the lstDivision
objList.ForEach(x => x.lstDivison = lstDiv
.Where(y => y.GroupID == x.DivisionGroupID)
.Select(p => new Division { Name = p.Name, Description = p.Description })
.ToList());
Both of these approaches will give me the result. But i am looking forward a better approach in as part of my project requirement which has to improve the query performance. Could you please suggest which is the best approach to handle this situation?
use yours teacher object to populate list of divisions under it. as my understanding that how it was designed class structure.
//populate property in object
objList.ForEach(x => {
x.lstDivison = lstDiv.Where(w=> w.GroupID == x.DivisionGroupID).ToList();
});
objList.Dump();

"Operation is not valid due to the current state of the object." Exception, when I want to retrieve Items

I use this method to retrieve Items for a Tree component in Blazor Serverside, In the DAL I have:
public List<TreeItem> GetTreeItems()
{
var tree = new List<TreeItem>();
TreeItem item = new TreeItem()
{
DepartmentID = 0,
CategoryID = 0,
Text = "Root",
Childs = context.Departments.OrderBy(d => d.Order).Select(d => new TreeItem()
{
DepartmentID = d.Id,
CategoryID = 0,
Text = d.Title,
Childs = d.Categories.OrderBy(c => c.Order).Select(c => new TreeItem()
{
DepartmentID = d.Id,
CategoryID = c.Id,
Text = c.Title
}).ToList()
}).ToList()
};
tree.Add(item);
return tree;
}
The TreeItem class is the following, (The model shared by the blazor Component and Dal Class):
public class TreeItem
{
public int DepartmentID { get; set; }
public int CategoryID { get; set; }
public string Text { get; set; }
public List<TreeItem> Childs { get; set; }
}
But when I was to retrieve Items for the tree in the blazor component I get the exception: Operation is not valid due to the current state of the object., admin is the DAL class I inject to Blazor component as follows:
private void GetTreeModel()
{
try
{
Items = admin.GetTreeItems();
TreeSuccess = true;
TreeMessage = "Success";
return;
}
catch (Exception ex) // Error here
{
TreeSuccess = false;
TreeMessage = "Can not load tree items";
return;
}
}
What is this error and How to solve it?
I solved my problem using First loading entities and then using Linq to Objects, Like this:
var tree = new List<TreeItem>();
var departments = context.Departments.OrderBy(d => d.Order).ToList();
var categories = context.Categories.OrderBy(c => c.Order).ToList();
TreeItem item = new TreeItem()
{
DepartmentID = 0,
CategoryID = 0,
Text = "Root",
Childs = departments.Select(d => new TreeItem()
{
DepartmentID = d.Id,
CategoryID = 0,
Text = d.Title,
Childs = categories.Where(c => c.DepartmentID == d.Id).OrderBy(c => c.Order).Select(c => new TreeItem()
{
DepartmentID = d.Id,
CategoryID = c.Id,
Text = c.Title
}).ToList()
}).ToList()
};
tree.Add(item);
return tree;
}

Linq Split properties of Class and assign it to another Custom Class

I have a Complex Situation now and i am terribly stuck. Kindly Let me know if you can share some light to it.
I have a
List Which will have the Following properties
public class Categories
{
public string DisplayName { get; set; }
public string ValueCode { get; set; }
public string Count { get; set; }
}
This will have Values like
Category1/SubCategory1
cat1/sc1
5
Category1/SubCategory2
cat1/sc2
4
Category 2/Subcategory1
cat2/sc1
5
Category 2/Subcategory2
cat2/sc2
23
I created a Custom Class to fill in the values
public class JobCateogry
{
public string DisplayName { get; set; }
public string ValueCode { get; set; }
public string Count { get; set; }
public List<JobCateogry> SubCategories { get; set; }
}
I have to Split the String in the Code Value and assign it to the SubCategory.
Like My Final out of jobCategory would be
Category1
Cat1
9
SubCategory1
sub1
5
SubCateogry2
sub2
4
I tried to Split the string and assign it to the new class in two step first by splitting and then by assiging. But i am sure i am doing it the wrong way, because the moment i split, i loose the count .
var lstCategory = Categories
.Where(i => i.count > 0)
.Select(item => item.valueCode.Split('/')
.Select(k =>(k)).ToList();
List<JobCategories> jobcategories = lstCategory
.Select(item => item.Split(QueryStringConstants.CAT_SEPERATOR.ToCharArray()[0]))
.GroupBy(tokens => tokens[0].Trim(), tokens => tokens[1])
.Select(g => new JobCategories(g.Key, g.DisplayName,g.ToList(),)).ToList();
Can you please help?
A bit weird task
It might not be the best solution and it only works with the two layers :-), and i tried keeping a lot of linq for the fun of it
anyway hope it can get you moving forward.
full code snippet https://gist.github.com/cbpetersen/db698def9a04ebb2abbc
static void Main(string[] args)
{
var cats = new[]
{
new Categories { Count = "5", ValueCode = "cat1/sc1", DisplayName = "Category1/SubCategory1" },
new Categories { Count = "4", ValueCode = "cat1/sc2", DisplayName = "Category1/SubCategory2" },
new Categories { Count = "5", ValueCode = "cat2/sc1", DisplayName = "Category2/Subcategory1" },
new Categories { Count = "23", ValueCode = "cat2/sc2", DisplayName = "Category2/Subcategory2" }
};
var categories = cats.Select(x => x.DisplayName.Split('/')[0]).Distinct();
var list = new List<JobCateogries>();
foreach (var category in categories)
{
var a = new JobCateogries
{
ValueCode = cats.Where(x => x.DisplayName.Split('/')[0] == category)
.Select(x => x.ValueCode.Split('/')[0]).FirstOrDefault(),
DisplayName = category,
SubCategories = cats.Where(x => x.DisplayName.Split('/')[0] == category)
.Select(x => new JobCateogries
{
SubCategories = new List<JobCateogries>(),
Count = x.Count,
DisplayName = x.DisplayName.Split('/')[1],
ValueCode = x.ValueCode.Split('/')[1]
}).ToList(),
};
a.Count = a.SubCategories.Select(x => int.Parse(x.Count)).Sum().ToString();
list.Add(a);
}
list.ForEach(x => Print(x));
Console.ReadKey();
}
public static void Print(JobCateogries category, int indent = 0)
{
var prefix = string.Empty.PadLeft(indent);
Console.WriteLine(prefix + category.DisplayName);
Console.WriteLine(prefix + category.ValueCode);
Console.WriteLine(prefix + category.Count);
category.SubCategories.ForEach(x => Print(x, indent + 4));
}

MVC populate dropdown from foreign key

I have been struggling with this for several days. I need to populate a dropdownlistfor with genres.
My MovieRepository to grab the genres:
public IQueryable<Movies> MoviesAndGenres
{
get { return db.Movies.Include(m => m.parentGenre); }
}
My movie model
public virtual Genres parentGenre { get; set; }
Genre Model:
public class Genres
{
public Genres()
{
this.movies = new HashSet<Movies>();
}
[Key]
public int genreId { get; set; }
[Required(ErrorMessage = "A genre name is required")]
[StringLength(25)]
public String genreName { get; set; }
public ICollection<Movies> movies { get; set; }
}
I am trying to pass in the genres with a select list, but I am getting a LINQ to Entities does not recognize the System.String To String() Method, and this method cannot be translated to a stored expression.
Movies Controller, addMovie action:
ViewBag.Genres = movieRepository.MoviesAndGenres.Select(m => new SelectListItem
{
Text = m.parentGenre.genreName,
Value = m.parentGenre.genreId.ToString()
}).ToList();
return View();
View:
#Html.DropDownListFor(m => m.parentGenre, (SelectList)ViewBag.Genres)
Any help would be greatly appreciated!
Update:
Repository:
public IQueryable<Genres> MoviesAndGenres
{
get { return db.Genres; }
}
Controller:
var x = movieRepository.MoviesAndGenres.Select(m => new
{
Text = m.genreName,
Value = m.genreId
});
ViewBag.Genres = new SelectList(x);
return View();
View:
#Html.DropDownListFor(m => m.parentGenre, (SelectList)ViewBag.Genres)
Since you're retrieving all of the records anyways, you can just do this.
ViewBag.Genres = movieRepository.MoviesAndGenres.AsEnumerable()
.Select(m => new SelectListItem
{
Text = m.parentGenre.genreName,
Value = m.parentGenre.genreId.ToString()
});
You would also need to change your view to:
#Html.DropDownListFor(m => m.parentGenre, new SelectList(ViewBag.Genres))
Actually, a better approach would probably be this, since then it only retrieves the specific columns you need:
var x = movieRepository.MoviesAndGenres.Select(m => new
{
Text = m.parentGenre.genreName,
Value = m.parentGenre.genreId
});
ViewBag.Genres = new SelectList(x)
Also, the ToList() is no longer required because it's already in a an immediate state.

Resources