Hi i just start working on a project. I saw a context query line. It includes entity then includes again.
I don't understand whats what mean. I created 2 diffrent var object. when removed the self include line then debugging. I saw same thing.
This is the code
using (var context = new DbContext())
{
var entity = context.Set<Book>()
..
...
.Include(x => x.Book)
.Include(x => x.BookAuthor).ThenInclude(x => x.Author)
..
... goes on
return entity.ToList()
}
I thought this is the same thing
using (var context = new DbContext())
{
var entity = context.Set<Book>()
..
...
//remove .Include(x => x.Book)
.Include(x => x.BookAuthor).ThenInclude(x => x.Author)
..
... goes on
return entity.ToList()
}
other example code is
.Include(x => x.CategoryBranches).ThenInclude(x => x.Category)
.Include(x => x.CategoryBranches).ThenInclude(x => x.Category).ThenInclude(x => x.BookCategories);
Can anyone explain the self include again. I saw it everywhere
Thank you
If the Book has a Navigation Property called Book (that doesnt make sense with that name semantically, but technically it does), you have to include it to load it.
You can have a reference to yourself, just like to any other table via a foreign key, and have a navigation property for that.
Consider this example that makes more sense semantically:
public class Person
{
public int Id { get; set; }
public int ParentId { get; set; }
public Person Parent { get; set; } // This is a self referencing navigation property
}
So now to use the parent you'd have to:
persons.Include(p => p.Parent). ... ;
to access the parent.
Lazy loading means related entities in a model class are loaded automatically.
The parent class provided by Mafii does not use lazy loading. That's why he had to use the Include method to load (or include) the parent entity (the related entity). If he didn't do it this way, any details under the Parent navigation will not be loaded. To load a related entity automatically, simply enable lazy loading by using the virtual keyword in the navigation property of the entity. This way, you don't have to use Include anymore.
public class Person
{
public int Id { get; set; }
public int ParentId { get; set; }
public virtual Parent Parent { get; set; }
}
Note: I think there is a performance downside to this. So, if you do this, you need to be sure you actually need those entities loaded.
Related
I have entities set up something like this:
public class MyThing
{
public int Id { get; set; }
public virtual MyOtherThing { get;set; }
}
public class MyOtherThing
{
public int Id { get; set; }
public virtual MyThing MyThing { get; set; }
}
My intention is that 'MyThing' can have one or none of MyOtherThing, and I also want a navigation link from MyOtherThing to it's parent.
I have configured the following EntityBaseConfiguration for the 'MyOtherThing' entity:
this.HasOptional(x => x.MyThing)
.WithOptionalPrincipal(x => x.MyOtherThing);
I can assign and modify MyOtherThing to MyThing no problem, but when I want to unassign 'MyOtherThing' from 'MyThing', how do I do this?
I tried the following:
myThing.MyOtherThing = null;
and then editing the entity by setting the EntityState.Modified state, but this didn't remove the association between the entities.
I tried adding the following to my MyThing entity, but this resulted in an EF 'Multiplicity is not valid' error when updating my database model:
public int? MyOtherThingId{ get; set; }
Thanks in advance!
I tried the following:
myThing.MyOtherThing = null;
If you want to remove an optional dependent entity (here: MyOtherThing) from a principal entity (here MyThing) by setting it to null, you have to pull the entity from the database with the dependent entity included, for example:
var mything = context.MyThings.Include(m => m.MyOtherThing)
.Single(t => t.Id == idValue);
(It's also OK when the belonging MyOtherThing is loaded into the context later, for example by lazy loading).
Without Include, myThing.MyOtherThing already is null and EF doesn't detect any change. Note that the statement myThing.MyOtherThing = null; doesn't execute lazy loading, which is a bit confusing because with collections the behavior is different.
By the way, the dependent entity can also be removed from the database directly, which is more efficient.
var ot = context.Set<MyOtherThing>().Find(idValue);
context.Set<MyOtherThing>().Remove(ot);
context.SaveChanges();
I have an EF Core 2.1 Code First model with a "parent-child" type relationship between two classes:
class Parent
{
public int Id {get; set;}
public string Name {get; set;}
}
class Child
{
public int Id {get; set;}
public string Description { get; set; }
public Parent Parent { get; set; }
}
I want to load a certain Parent, and make sure that all its Child entities are loaded too. However, there is no navigation property to Child, and I cannot modify the classes, so I can't add one.
dbContext.Parents
.Include(p => p.???)
.Find(1);
I suppose I could do a second query where I look everything up in reverse:
dbContext.Children.Where(c => c.Parent.Id == loadedParent.Id)
but that does not seem very efficient, especially when you load multiple parents and do something horrible like this:
var parentIds = loadedParents.Select(p => p.Id);
var children = dbContext.Children.Where(c => parentIds.Contains(c.Parent.Id));
Is there a way to make sure entities are loaded when you only have a "child-to-parent" navigation property?
make sure that all its Child entities are loaded too
So load the Child entities:
var children = dbContext.Children.Include(c => c.Parent)
.Where(c => c.Parent.Id == 1).ToList();
Or use wider selection criteria than c.Parent.Id == 1 if you want to get multiple parents.
If necessary you can list the loaded parents by accessing the Local collection:
va parents = dbContext.Parents.Local;
What I would like to do is duplicate/copy my School object and all of its children/associations in EF Core
I have something like the following:
var item = await _db.School
.AsNoTracking()
.Include(x => x.Students)
.Include(x => x.Teachers)
.Include(x => x.StudentClasses)
.ThenInclude(x => x.Class)
.FirstOrDefaultAsync(x => x.Id == schoolId);
I have been reading up on deep cloning and it seems that I should be able to do just add the entity...so pretty much the next line.
await _db.AddAsync(item);
Then EF should be smart enough to add that entity as a NEW entity. However, right off the bat I get a conflict that says "the id {schoolId} already exists" and will not insert. Even if I reset the Id of the new item I am trying to add, I still get conflicts with the Ids of the associations/children of the school iteam.
Is anyone familiar with this and what I might be doing wrong?
I had the same problem too, but in my case EF core was throwing exception "the id already exists".
Following the answer of #Irikos so I have created method which clones my objects.
Here's example
public class Parent
{
public int Id { get; set; }
public string SomeProperty { get; set; }
public virtual List<Child> Templates { get; set; }
public Parent Clone()
{
var output = new Parent() { SomeProperty = SomeProperty };
CloneTemplates(output);
return output;
}
private void CloneTemplates(Parent parentTo, Child oldTemplate = null, Child newTemplate = null)
{
//find old related Child elements
var templates = Templates.Where(c => c.Template == oldTemplate);
foreach (var template in templates)
{
var newEntity = new Child()
{
SomeChildProperty = template.SomeChildProperty,
Template = newTemplate,
Parent = parentTo
};
//find recursivly all related Child elements
CloneTemplates(parentTo, template, newEntity);
parentTo.Templates.Add(newEntity);
}
}
}
public class Child
{
public int Id { get; set; }
public int ParentId { get; set; }
public virtual Parent Parent { get; set; }
public int? TemplateId { get; set; }
public virtual Child Template { get; set; }
public string SomeChildProperty { get; set; }
}
Then I just call DbContext.Parents.Add(newEntity) and DbContext.SaveChanges()
That worked for me. Maybe this will be useful for someone.
I had the same problem, but in my case, ef core was smart enough save them as new entities even with existing id. However, before realising that, I just made a copy constructor for all the items, created a local task variable containing only the desired properties and returned the copy.
Remove certain properties from object upon query EF Core 2.1
Let's say I have 2 entity models -
public class Blog
{
[Key]
public int BlogId { get; set; }
public string BlogName { get; set; }
public virtual ICollection<Post> Posts { get; set; }
}
public class Post
{
[Key]
public int PostId { get; set; }
public string PostName { get; set; }
public int BlogId { get; set; }
[ForeignKey("BlogId")]
public virtual Blog Blog { get; set; }
}
Let's get a blog-
var blog = dbContext.Blogs.Where(r => r.BlogId == 1).FirstOrDefault();
The code retrieves the blog and all the posts as well. But if there is thousands of posts under this blog? It would be a performance issue.
Is there any trick not to load posts by default? I don't want to remove Posts property from Blog.
Thanks.
You can achieve this by two ways.
Lazy Loading
Explicit Loading
Lazy Loading - DbContext has a configuration setting that enables lazy loading DbContext.Configuration.LazyLoadingEnabled.
This setting is true by default and therefore if you have not changed the default, the dynamic proxy will perform lazy loading. For using lazy loading it need to have two things implemented in your POCO classes.
Your POCO classes must be public and not sealed.
The navigation properties that you want to be lazy loaded must also be marked as virtual (which you have already done) so that Entity Framework can override the properties to include the lazy loading logic.
2- Explicit Loading - Explicit loading is like lazy loading in that related data is loaded separately, after the main data has been loaded. However, unlike lazy loading, it doesn’t automatically happen for you; you need to call a method to load the data.
Explicit loading is achieved using the DbContext.Entry method. The Entry method gives you access to all the information that the DbContext has about an entity. This goes beyond the values that are stored in the properties of the actual entity and includes things such as the state of the entity and the original values for each property when it was retrieved from the database.
Ex:
var blog = dbContext.Blogs.Where(r => r.BlogId == 1).FirstOrDefault();
context.Entry(blog)
.Collection(d => d.Posts)
.Load();
You can control which children load with a projection:
var blogWithSomeChildren = dbContext.Blogs
.Select(b => new
{
blog = b,
selectedPosts = b.Posts.Where(p => p.PostId > 500) // or whatever criteria you want...
})
.Single(b => b.BlogId ==1);
Problem Summary: I have a Master and Detail entities. When I initialize a Master (myMaster), it creates an instance of Details (myMaster.Detail) and both appear to persist in the database when myMaster is added. However, when I reload the context and access myMasterReloaded.detail its properties are not initialized. However, if I pull the detail from the context directly, then this magically seems to initialize myMasterReloaded.detail. I've distilled this down with a minimal unit test example below. Is this a "feature" or am I missing some important conceptual detail?
//DECLARE CLASSES
public class Master
{
[Key, DatabaseGenerated(DatabaseGenerationOption.Identity)]
public Guid MasterId { get; set; }
public Detail Detail { get; set; }
public Master() { Detail = new Detail(); }
}
public class Detail
{
[Key, DatabaseGenerated(DatabaseGenerationOption.Identity)]
public Guid DetailId { get; set; }
public Master MyMaster{ get; set; }
}
public class MyDbContext : DbContext
{
public DbSet<Master> Masters { get; set; }
public DbSet<Detail> Details { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Master>()
.HasOptional(x => x.Detail)
.WithOptionalPrincipal(x => x.MyMaster)
.WillCascadeOnDelete(true);
}
}
//PERFORM UNIT TEST
[TestMethod]
public void UnitTestMethod()
{
//Start with fresh DB
var context = new MyDbContext();
context.Database.Delete();
context.Database.CreateIfNotExists();
//Create and save entities
var master = context.Masters.Create();
context.Masters.Add(master);
context.SaveChanges();
//Reload entity
var contextReloaded = new MyDbContext();
var masterReloaded = contextReloaded.Masters.First();
//This should NOT Pass but it does..
Assert.AreNotEqual(master.Detail.DetailId, masterReloaded.Detail.DetailId);
//Let's say 'hi' to the instance of details in the db without using it.
contextReloaded.Details.First();
//By simply referencing the instance above, THIS now passes, contracting the first Assert....WTF??
Assert.AreEqual(master.Detail.DetailId, masterReloaded.Detail.DetailId);
}
(This is the sticking point for a more sophisticated entity set. I've simply distilled this down to its simplest case I can't simply replace details with a complex type).
Cheers,
Rob
I think it's because when you first reload the Master, you have not eager-loaded the Detail, so the Detail entity will not be in the Entity Framework "graph" (internal memory). The only thing in the graph will be a single Master entity.
But when you query the Detail ("Let's say hi"), it is loaded into the graph and the reference was resolved based on the FK association, therefore your final test passes as the Master is now related to the Detail.
I could be wrong though - but that's what it sounds like.
Do you have lazy-loading enabled? If not, you need to eager-load the relationships you need.
Instead of this:
var masterReloaded = contextReloaded.Masters.First();
Try this:
var masterReloaded = contextReloaded.Masters.Include(x => x.Detail).First();
Matt Hamilton was right (See above). The problem was:
The Detail property should not be instantiated within the constructor, nor through the getters/setters via a backing member. If it's convenient to instantiate a Entity containing Properties in your new instance, then it may be helpful to have a separate initialize method which will not be automatically executed by the Entity Framework as it reconstructs objects from the database.
The Detail property needs to be declared virtual in the Master class for this to work properly.
The following WILL PASS (As expected/hope)
public class Master
{
[Key, DatabaseGenerated(DatabaseGenerationOption.Identity)]
public Guid MasterId { get; set; }
//Key new step: Detail MUST be declared VIRTUAL
public virtual Detail Detail { get; set; }
}
public class Detail
{
[Key, DatabaseGenerated(DatabaseGenerationOption.Identity)]
public Guid DetailId { get; set; }
//Set this to be VIRTUAL as well
public virtual Master MyMaster { get; set; }
}
public class MyDbContext : DbContext
{
public DbSet<Master> Masters { get; set; }
public DbSet<Detail> Details { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
//This sets up a BI-DIRECTIONAL relationship
modelBuilder.Entity<Master>()
.HasOptional(x => x.Detail)
.WithOptionalPrincipal(x => x.MyMaster)
.WillCascadeOnDelete(true);
}
}
[TestMethod]
public void UnitTestMethod()
{
var context = new MyDbContext();
context.Database.Delete();
context.Database.CreateIfNotExists();
//Create and save entities
var master = context.Masters.Create();
//Key new step: Detail must be instantiated and set OUTSIDE of the constructor
master.Detail = new Detail();
context.Masters.Add(master);
context.SaveChanges();
//Reload entity
var contextReloaded = new MyDbContext();
var masterReloaded = contextReloaded.Masters.First();
//This NOW Passes, as it should
Assert.AreEqual(master.Detail.DetailId, masterReloaded.Detail.DetailId);
//This line is NO LONGER necessary
contextReloaded.Details.First();
//This shows that there is no change from accessing the Detail from the context
Assert.AreEqual(master.Detail.DetailId, masterReloaded.Detail.DetailId);
}
Finally, it is also not necessary to have a bidirectional relationship. The reference to "MyMaster" can be safely removed from the Detail class and the following mapping can be used instead:
modelBuilder.Entity<Master>()
.HasOptional(x => x.Detail)
.WithMany()
.IsIndependent();
With the above, performing context.Details.Remove(master.Detail), resulted in master.Detail == null being true (as you would expect/hope).
I think some of the confusion emerged from the X-to-many mapping where you can initialize a virtual list of entities in the constructor (For instance, calling myDetails = new List(); ), because you are not instantiating the entities themselves.
Incidentally, in case anyone is having some difficulties with a one-to-many unidirectional map from Master to a LIST of Details, the following worked for me:
modelBuilder.Entity<Master>()
.HasMany(x => x.Details)
.WithMany()
.Map((x) =>
{
x.MapLeftKey(m => m.MasterId, "MasterId");
x.MapRightKey(d => d.DetailId, "DetailId");
});
Cheers, Rob