I have a situation where I need to add a new item to a property for a group of objects that has a many-to-many relationship. Is there any way to do this in bulk using EntityFramework.Extended? Something like...
Ctx.Foos
.Where(f => fooIds.Contains(f.FooId))
.Update(f => f.Bars.Add(bar)) // <-- what would go here???
Obviously, the Update() part is not correct. For the time being, I've worked around it by retrieving the set and looping through. Just wondering if there is a better way.
The short answer is: NO
The Update() method allows only to update properties directly related to the entity. Since you want to update a many or many relations (not a property), this library does not allow to do it.
Disclaimer: I'm the owner of the project Entity Framework Extensions
The best & fastest way if you need performance is by using the Entity Framework Extensions library with the BulkSaveChanges method.
Ctx.Foos
.Where(f => fooIds.Contains(f.FooId))
.ToList()
.ForEach(f => f.Bars.Add(bar))
Ctx.BulkSaveChanges();
Related
how to make relation between two tables one to many and required in two sides
for example
i have project and image table
every image has project And every project has at least one image
i make this
every image has project
public class ImageMap : EntityTypeConfiguration<Image>
{
public ImageMap()
{
this.ToTable("ProjectImage");
this.HasKey<int>(i => i.Id);
this.Property(i => i.ImagePath).IsRequired().HasMaxLength(2000);
this.HasRequired<Project>(i => i.Project).WithMany(p => p.Images)
.HasForeignKey(i => i.ProjectId);
}
}
how make every project has at least one image or many ??? (Entity Framwork version 6)
You can't ensure a "at least one" condition in the "many" part of a one to many relationship. This is not something you can manage in a db, without a check constraint, for example (you could also do a validation in your application to manage that).
One way to achieve that could be to create a one-to-one relationship AND a one-to-many, both pointing on the same table / entity.
So your Project would have a MandatoryImage property, for example, and a AlternativeImages (collection) property.
I think that is something you should control in your Business Logic or Data Access layer before call SaveChanges method of your context. As far I know there is no way to configure that using Fluent API. So you could do something like this:
if(project.Images.Count()>0)// Or project.Images.Any()
{
context.SaveChanges();
}
In my School EF Model, I have Kids and Tutorials in many-to-many relationship.
Let's assume both Kids and Tutorials have existing items in them, now we just want to change their existing relationships. That is, to add/delete some tutorials from a kid.
var kid; //the request target to modify relationships
//kid.Tutorials has the old existing relationships to be modified by add/del
var tutorialsToAdd; //the request to add relationships
var tutorialsToDel; //the request to del relationships
using (var conn = new SchoolEFModels(efConnectionStr)) {
conn.Kids.Attach(kid);
kid.Tutorials.ForEach(t => conn.Tutorials.Attach(t));
kid.Tutorials.AddRange(tutorialsToAdd); //simple add extension in batch
kid.Tutorials.RemoveRange(tutorialsToDel); //simple del extension in batch
conn.SaveChanges();
}
When I do this, I got an exception saying:
"Cannot insert duplicate key in object 'dbo.Tutorials'. The duplicate key value is (10)."
I can see EF is trying to create new Tutorial items instead of updating the existing relationship for me. Which is what I don't want. You misunderstood me EF!
What is wrong with my code? How do I make it update Many-to-Many relationships?
I figured it out.
Adding/removing it will make EntityState turn to Added/Deleted. Therefore, causing it to reinsert existing Ids as the article mentioned, thank Gert there for the link.
So, if you modify each of the conn.entry(kid/tutorials).State to EntityState.Modified and then call conn.ChangeTracker.DetectChanges(); then conn.SaveChanges(); it will only update the many-to-many table as expected.
UPDATE:
One thing you need to be careful tho. If the in-memory objects list of Kids and Tutorials are linked to each other. e.g. Kids[0].Tutorials[0] == Tutorials[0] && Tutorial[0].Kids[0] == Kids[0] EF will not be able to handle this dead loop for you. You need to break this circular link first.
To do so, my approach is to open a Connection and read the Kid out Includes(Tutorials), and then use the result to update many to many relationship, but not to use the in-memory objects.
I’m trying to copy/clone entity graph with EF6.1 and getting duplicate entities.
Below is a piece of my model which consist of a Template that I want to modify, copy and assign to different users, something like Save As function.
Here is my entities model:
What I’m doing is:
var newTemplate = ctx.Templates
.Where(t => t.TemplateId == SelectedTemplate.TemplateId)
.Include(t => t.Properties.Select(p => p.PropertyCollections))
.Include(t => t.Properties.Select(p => p.Values))
.AsNoTracking()
.First();
newTemplate.TemplateName = newTemplateName;
ctx.Templates.Add(newTemplate);
ctx.SaveChanges();
And what I get is shown below where “Template1” is the source and “Template2” is the copy in which every ‘PropertyCollection’ has a duplicated entry for each ‘Property’.
Result after copy:
I understood that with AsNoTracking there is no identity mapping which is the reason behind this but I can’t find even a custom solution.
I didn't really test your code, but I think your Entities might really get messed up when doing it that way. Maybe this attempt would work for you. It's for EF4 but might work.
You are adding the whole graph, so EF is inserting everything. You are using AsNoTracking to "trick" EF instead of its original purpose.
I would suggest you to write a few lines of code to actually implement your business requirement, which is create a new Template based on another one.
So, get the template (without the AsNoTracking), and create a new template initializing the properties based on the original template values. Then add the new template to the context. EF will insert the new template and reference the existing dependent entities.
This is also a safer way to implement this, as in the future you might require to set some properties with different values in the new template.
Having the DB below I would like to retrieve all bricks in C# and include the Workshops on those BrickBacks that has any.
I managed to retrieve all the Bricks and include the BrickBacks by simply doing
context.Bricks.Include(b=>b.Back).ToList()
But in this case BrickBack is an abstract class which its subclass may contain a Workshop but this is not always the case.
Unfortunately I can't just do
context.Bricks.Include(b=>b.Back).Include(b=>b.Back.Workshop).ToList()
How can this be done?
This could work context.Bricks.Include("Back").Include("Workshop").ToList()
WorkShop will be null if Workshop_Id is null in the database.
Not possible. Maybe you can approach it from a different angle:
ConcreteBacks.Include(b => b.WorkShop)
.Include(b => b.Bricks)
.AsEnumerable()
.SelectMany(b => b.Bricks)
This will pull all ConcreteBacks and the included data from the database and then return the Bricks in a flattened list.
The AsEnumerable() is necessary because EF only includes data off the root entities in the result set. Without AsEnumerable() this would be Brick and the ConcreteBacks would be ignored. But now EF only knows about the part before AsEnumerable(), so it includes everything off ConcreteBack.
I am using EF4, and I using the System.Data.Entity dll in my class. However, I can't see the load method of my navigation property of my entity.
How can I access to this method?
What I am doing is create e 4.0 project of .NET, create my edmx from a database, right click in the model edmx and I generate a DBContext.
However, I can access to the local property of my context, that I think that is a feature of EF4.
Thanks.
Daimroc.
For the DbContext approach, the IsLoaded property and Load method have been moved:
context.Entry(yourEntity)
.Collection(entity => entity.NavigationProperty)
.IsLoaded;
context.Entry(yourEntity)
.Collection(entity => entity.NavigationProperty)
.Load();
Entry method: http://msdn.microsoft.com/en-us/library/gg696578(v=vs.103).aspx
Collection method: http://msdn.microsoft.com/en-us/library/gg671324(v=vs.103).aspx
IsLoaded property: http://msdn.microsoft.com/en-us/library/gg696146(v=vs.103).aspx
Load method: http://msdn.microsoft.com/en-us/library/gg696220(v=vs.103).aspx
When you write the query (all this also works with lambda syntax as well btw)
From a in context.EntitySet.Include("Navigation Property NAme")
select a
and all will be there ;) .. or more to the point the navigation properties will be populated.
can be wierd with lots of joins though
you can also
From a in context.EntitySet.Include("Navigation1.Navigation2.Navigation2")
.Include("SomeOtherNavigation")
select a
where navigation1 and SomeOtherNavigation are navigation properties on the entity set
Failing that you can turn on lazy loading in the context. But its evil imo.
Also it doesnt really scale well when you go for an SOA approach as you need to include prior to knowing about the lazy load in your repository.
Ie you run the query in your repository, send stuff over the service and then want the navigation properties in the front end ... by which time its too late to lazy load ... EG you are using a wcf service that doesn't use data services.
Important to note that you ahve to use Include on the EntitySet, it is not on an IQueryable<>.
A real example:
var csg = from ucsg in c.UserCarShareGroups.Include("UserCarShareGroups.User.UserVehicles")
where ucsg.User.UserName.ToLower() == userName.ToLower()
&& ucsg.CarShareGroup.Carpark.Name.ToLower().Equals(carparkName.ToLower())
&& ucsg.DeletedBy == null
select ucsg.CarShareGroup;
(the database has case sensitive collation for some reason)
An alternate approach (and maybe more relevant is here)
Entity Framework - Trouble with .Load()
However I like doing it the way I said because it is explicit and I know exactly what is being pulled out. Especially when dealing with large datasets.
An answer to a question that combines my concerns with Load() is here:
Entity Framework 4 load and include combine