Entity Framework - many to many relationship - add reference with id - entity-framework

I use entity framework and I have many to many relationship between 2 objects.
Lets say class "question" and class "tag", when I create a new question with many tags, I have list of selected tags in viewmodel. I need to create the question so these tags are added to question but I do not want to reload all the tags.
I mean :
foreach(var id in tagUIds)
{Tag tag = gettag(id);
question.tags.add(tag); }
is not very efficient, is there a way to avoid loading the tag and adding the reference just by id of tag?
Thanks

You can create intermediate entity TagToQuestionLink, map it to many-to-many table and use Collection of this entity in question instead of collection of Tags:
public class TagToQuestionLink
{
TagId{get;set;}
QuestionId{get;set;}
}
Then you can use:
foreach(var id in tagUIds)
{
var tagLink = new TagToQuestionLink{QuestionId = question.Id, TagId = id};
question.tags.add(tagLink);
}
Do not forget, that in this solution question.tags should be ICollection<TagToQuestionLink>.

Related

Entity Framework add assocations to many to many by key only

Suppose you have one of the simplest text book models:
Product {
Categories
}
Where a product can be associated to 0 to many categories.
Suppose category looks similar to
Category {
int Id { get; set; }
string Name {get; set; }
}
Now I want to associate my product to (existing) categories with ids 1, 2, and 3. Is there anyway to create this association without loading categories 1, 2, and 3 into memory?
I know this would be possible with a single Category where my Product model had Category and CategoryId on it. Is there a similar convention for bonding multiple items?
To reiterate, my purpose is to avoid querying categories. I already have the identifiers. With direct sql I could easily establish these relationships by key only (in fact the association table is literally just the keys). I want to know if there's an "entity framework way" of doing this or whether direct sql is the only option.
You could create category instances with just the id and attach them to the context. Then you could add to the product, without having to pull the categories from the database.
For example:-
var category = new Category { Id = 1 };
db.Categories.Attach(category);
product.Categories.Add(category);
db.SaveChanges();

How to obtain grandchildren from grandparent primary key in Entity Framework

In Entity Framework, three entities have 1 to many relationships as grandparent, children and grandchildren. How do you obtain an object list of all grandchildren given the grandparent's primary key?
Thank you,
Newby to EF
Well if you have two one-to-many relationships between those three entities, I guess you could do something like this:
int grandparentId=1;
using(var ctx=new YourContext())
{
var grandparent=ctx.GrandParents.FirstOrDefault(gp=>gp.Id==grandparentId);
if(grandparent!=null)
{
// a list with all the grandchildren
var grandchildren=grandparent.Children.SelectMany(c=>c.GrandChildren).ToList();
}
}
And, if you are not using lazy loading then you need to use the Include extension method:
int grandparentId=1;
using(var ctx=new YourContext())
{
var grandparent=ctx.GrandParents.Include(gp=>gp.Children.Select(c=>c.GrandChildren)).FirstOrDefault(gp=>gp.Id==grandparentId);
if(grandparent!=null)
{
// a list with all the grandchildren
var grandchildren=grandparent.Children.SelectMany(c=>c.GrandChildren).ToList();
}
}
But, as #ErikPhilips said, you need to give more information about your model. Without that information it's difficult to give a concrete answer to your real problem.

Entity Framework: linking entity A to an existing entity B

I am rather new to the Entity Framework, so I am probably overlooking something simple here.
In my controller class I am adding a new Category entity to the database, next I am using that entity as a property on a Course entity. When I save the Course entity, the Category is saved to the database AGAIN, while I was hoping the new Course would reference the Category that was already inserted.
The (simplified) controller code that saves the first Category:
// Create and save the category
Category category = new Category {Name = "Test category"};
category = context.Categories.Add(category);
context.SaveChanges(); // The category object now has a CategoryId (the pk of the record)
// Create and save the course
Course course = new Course {
FullDescription = "This is a new course",
Name = "My new course",
Category = category // Hoping this will have EF make a link to the just inserted category
};
context.Courses.Add(course);
context.SaveChanges(); // Saves the Course AND creates a **new** Category in the db
The problem seems to be that I call saveChanges() twice. What works is removing the first call to context.saveChanges(), BUT, this is not my actual code. In my application I use a repository pattern and adding a category is done by calling categoryRepository.AddCategory(Category category). And saving the Course is done in exactly the same way, by calling courseRepo.AddCourse(Course course) that also contains a call to saveChanges().
public Category AddCategory(Category category)
{
category = context.Categories.Add(category);
context.SaveChanges();
return category;
}
I don't want to remove the calls to saveChanges() in AddCourse() and AddCategory(), because I want these to be atomic operations.
I was hoping that returning the category and subsequently using the category as a property on a new Course would link that course to the category, but apparantly that is not the case. How do I link my Course to a category that is already present in the database?
I'm not sure how your data model is structured but you could do something like this.
course.CategoryId = category.CategoryId;
That way you map the actual foreign key in the relationship and it does the same thing.
Category category = new Category {Name = "Test category"};
Course course = new Course {
FullDescription = "This is a new course",
Name = "My new course",
Category = category
};
courseRepo.AddCourse(course);
You can use only AddCourse for adding both entities, if yours repositories has the same context. If each repository has their own context, you should attach category to the courseRepo context or load entity into it (but I suppose it not suitable for you because you have different repositories).

ASP.NET MVC 3: ViewModel that deals with a list of lists

I'm trying to put together a ViewModel that will have a list of users and each user will have a list of locations.
The User table and Location table are joined together through another table that holds each respective ID and some other information. This table is essentially a many to many join table.
I've tried a few different viewModel approaches and they we're severely lacking... What would be the best approach for displaying this type of information?
I assume that the issue is that you want to be able to access the collection by either User or Location. One approach could be to use ILookup<> classes. You'd start with the many-to-many collection and produce the lookups like this:
var lookupByUser = userLocations.ToLookup(ul => ul.User);
var lookupByLocation = userLocations.ToLookup(ul => ul.Location);
Update:
Per your description, it seems like you don't really need to have a full many-to-many relationship in your ViewModel. Rather, your ViewModel could have a structure like this:
public class YourViewModel
{
public IEnumerable<UserViewModel> Users { get; set; }
}
public class UserViewModel
{
// User-related stuff
public IEnumerable<LocationViewModel> Locations { get; set; }
}
If you wanted to avoid redundant LocationViewModel objects, you could pre-build a mapping between your Model and ViewModel objects:
var locationViewModels = myLocations.ToDictionary(
loc => loc, loc => CreateLocationViewModel(loc));
And then reuse these objects when populating your page's ViewModel.

Entity Framework - Clear a Child Collection

I have run into an interesting problem with Entity Framework and based on the code I had to use to tackle it I suspect my solution is less than ideal. I have a 1-to-Many relationship between Table A and Table B where entities in TableB have a reference to TableA. I have a scenario where I want to simultaneously delete all children of a row in TableA and I thought this could be achieve by simply clearing the collection:
Entity.Children.Clear()
Unfortunately, when I attempted to save changes this produced as a Foreign Key violation.
A relationship is being added or
deleted from an AssociationSet
'FK_EntityB_EntityA'. With cardinality
constraints, a corresponding 'EntityB'
must also be added or deleted.
The solution I came up with was to manually delete object via the entity context's DeleteObject(), but I just know this logic I am using has got to be wrong.
while (collection.Any())
Entities.DeleteObject(collection.First());
For one, the fact that I had to use a Where() loop seems far less than ideal, but I suppose that's purely a semantic assessment on my part. In any case, is there something wrong with how I am doing this, or is there perhaps a better way to clear a child entity collection of an entity such that Entity Framework properly calls a data store delete on all of the removed objects?
Clear() removes the reference to the entity, not the entity itself.
If you intend this to be always the same operation, you could handle AssociationChanged:
Entity.Children.AssociationChanged +=
new CollectionChangeEventHandler(EntityChildrenChanged);
Entity.Children.Clear();
private void EntityChildrenChanged(object sender,
CollectionChangeEventArgs e)
{
// Check for a related reference being removed.
if (e.Action == CollectionChangeAction.Remove)
{
Context.DeleteObject(e.Element);
}
}
You can build this in to your entity using a partial class.
You can create Identifying relationship between parent and child entities and EF will delete child entity when you delete it from parent's collection.
public class Parent
{
public int ParentId {get;set;}
public ICollection<Child> Children {get;set;}
}
public class Child
{
public int ChildId {get;set;}
public int ParentId {get;set;}
}
Mapping configuration:
modelBuilder.Entity<Child>().HasKey(x => new { x.ChildId, x.ParentId });
modelBuilder.Entity<Parent>().HasMany(x => x.Children).WithRequired().HasForeignKey(x => x.ParentId);
Trick: When setting up the relationship between Parent and Child, you'll HAVE TO create a "composite" key on the child. This way, when you tell the Parent to delete 1 or all of its children, the related records will actually be deleted from the database.
To configure composite key using Fluent API:
modelBuilder.Entity<Child>().HasKey(t => new { t.ParentId, t.ChildId });
Then, to delete the related children:
var parent = _context.Parents.SingleOrDefault(p => p.ParentId == parentId);
var childToRemove = parent.Children.First(); // Change the logic
parent.Children.Remove(childToRemove);
// you can delete all children if you want
// parent.Children.Clear();
_context.SaveChanges();
Done!