I have two entities in 1:n relationship: Link, Category. At the first I get all categories and Links and put them to list. Next I fill the links of every category manually but category.Links.Add(link) connect to db and get the link again and it cause double data in the result. I know this action is because of lazy loading. the lazy loading is true and I do not want to disable it. How can I fill links of every category manually without connecting to db again?
Please do not offer Eager loading or disabeling lazy load.
var categories = categoryRepository.GetCategories().ToList();
var allLinks = linkRepository.GetLinks().ToList();
foreach (var category in categories)
{
var links = allLinks.Where(l => l.CategoryID == category.CategoryID);
foreach (var link in links)
{
category.Links.Add(link);
}
}
Even you mentioned that you don't want to turn off the lazy loading I insists you to do that because lazy loading will be triggered always when you access the navigation property first time. The only way to avoid this is:
Turning lazy loading off. The disadvantage is that you must turn lazy loading off for whole processing of categories. Because once you turn it on it will immediately reload Links during next access to the property. The reason is that EntityCollection used for handling Links has property IsLoaded which is false (and cannot be modified except by calling Load).
Removing virtual keyword from Links collection. This will disallow wrapping only Category by dynamic proxy and because of that it will not allow lazy loading. It will be even more interesting because the only needed code in your loading should be first two lines - links will be populated to categories automatically during second query execution. The problem here is that removing virtual keyword from auto generated entities is hard (it must be hard coded to T4 template) or you must use own entity.
Somehow cheating EF by replacing EntityCollection in Links property with another collection. This is not easy because you can't just assign new collection to Links - dynamic proxy will throw InvalidOperationException. You must create second partial part of your entity and add some code which will modify directly private field holding the collection (bypassing the property). Even such approach can have some bad consequences. Default code uses some fixups etc.
Turning lazy loading off will not break your functionality - actually EF is doing this quite often internally. You just need to do:
context.ContextOptions.LazyLoadingEnabled = false;
Related
I have a project which uses Entity Framework extensively. And it all works fine.
However, there is one update that I am trying to make and it does not update, nor is an error thrown.
The basic process is that I take an existing entity, work with it to create a new (and different type) of entity. The new entity saves just fine, but the existing one does not.
Typically when I have encountered this, it means that the entity is not attached to the context. However, it is, and I have tried attaching it again as well as "Adding" it, but I have had no luck.
if (fileRowEntity != null)
{
this.Context.FileRowEntities.Attach(fileRowEntity);
fileRowEntity.FileRowStatusId = (int)FileRowStatus.Converted;
fileRowEntity.EdiDocument = ediDocument;
}
ediDocument.InsertedDate = DateTime.Now;
ediDocument.EdiDocumentGuid = Guid.NewGuid();
ediDocument.DocumentMetatdata = null;
this.Context.EdiDocuments.Add(ediDocument);
var count = this.Context.SaveChanges();
The ediDocument is saved, but the fileRowEntity is not saved.
I am tearing my hair out trying to figure this out. I have tried a second explicit save on the fileRowEntity but it comes back with zero changes saved:
if (fileRowEntity != null)
{
fileRowEntity.EdiDocumentId = ediDocument.EdiDocumentId;
fileRowEntity.Column100 = "X";
this.Context.FileRowEntities.Attach(fileRowEntity);
count = this.Context.SaveChanges();
}
count is always zero, and the database is not updated.
I don't know what else to try to debug this.
Attaching an entity to a context does not mark it as modified.
In your case, the presumption is that the context instance where you are calling SaveChanges() on, is not the same that retrieved the fileRowEntity object, so it doesn't know that it was modified (or what was modified).
When a DbContext retrieves an object from the store, it stores a copy of its original values (unless you used AsNoTracking() on that query), and whenever it detects changes by a call to DetectChanges(), which you can make explicitly, but the normal DbContext implementation will call it by itself at many points), it'll store the new object values. If there are differences between those, the next call to SaveChanges will update the entity in the store/database.
When you attach an entity but don't mark it as modified, it doesn't know anything has changed, so you can explicitly mark the entity as modified:
Using Context.Entry(fileRowEntity).State = EntityState.Modified; should tell EF that your entity was modified, and it'll generate the update command when you call SaveChanges() for the whole entity (it'll send all the field values in the UPDATE SQL). Note that doing this also attaches the entity if it was not attached to the context (no need to attach it and then set its state).
You can also mark only the properties that were modified (or change the entity OriginalValues), this way your query will be optimized (it'll only update the fields that have actually changed). It's a bit cumbersome to track those changes yourself, but if you need that extra optimization, it's worth a shot (personally, unless there's a critical load on the database server and every bit counts, I wouldn't do this, but your choice)
There is a second reason for the above described behavior: This code will turn off change tracking:
Context.Configuration.AutoDetectChangesEnabled = false;
After I found this had been set in the constructor and removed it, all worked as expected.
I'm working on an application which will use EF5 as ORM.
Before start, I needed to create tests to confirm that everything in EF5 is working as it should. One of things I tested was support for UnitOfWork. I simulated multiple units of work by creating multiple instances of DbContext (like multiple requests when DbContext lifetime is set to PerWebRequest in IOC).
I encountered a suprise there, as EF5, as it seems uses something like SqlDependency, because I have situation as folows:
DbContext unit1 = new DbContext(), unit2 = new DbContext();
unit1.Items.Add(new Item() { Name = "Item1" });
Assert.AreEqual(0, unit1.Items.ToList().Count); // true as item is in unit1.Items.Local collection
Assert.AreEqual(0, unit2.Items.ToList().Count);
unit1.SaveChanges();
Assert.AreEqual(1, unit1.Items.ToList().Count); // true as unit1 is saved
Assert.AreEqual(1, unit2.Items.ToList().Count); // true as unit2 is somehow notified of changes
I am trying to find documentation but neither EF project pages and blogs nor google help me. This is not how old version of EF worked, where there was no out of the box feature to notify other contexts (i.e. see this question). It is nice if this is implemented but I need document which says it is, because I'm afraid that I cannot rely on this feature, and I cannot rely on knowing that data in my context won't change if other unit of work completes, as here obviously does.
Starting to explore EF4.3.
Using navigation properties makes things very easy, although I have a concern in one area, where I'd appreciate some advice on how other people deal with this scenario.
Take the scenario - Store.Programme.ProgrammeAccountNumber when lazy loading nested properties.
Store is the entity I'm querying.
Programme is an entity, and also a navigation property of Store, and ProgrammeAccountNumber is a property of Programme.
To get the original entity, I write something like this:
public static IQueryable<Store> FindActive()
{
var r = new ReadRepo<Store>(Local.Items.Uow.Context);
return r.Find(s => s.StoreStatusId == (int)StoreStatus.Active);
}
But lets suppose that Programme has it's own status fields:
var store = StoreBL.FindActive()
.Where(s => s.Programme != null && s.Programme.ProgramStatusId = (int)ProgrammeStatus);
It's not easy to remember all the conditions for each entity each time the query extends beyond the original entity.
Imagine a chain of properties where I have a product, that has a start/end dates and a status, that belongs to a store, that has a programme with a status and is valid if the category it operates is live.
I got to thinking there must be a technique for dealing with this effectively, or is it a case of using Lazy Loading sparingly?
Any advice appreciated.
So I'm using Entity Framework Code First (so no .edmx)
I have a base entity class with a bool IsEnabled to do soft delete's
I am using repository pattern so all queries against the repository can be filtered out with IsEnabled.
However any time I use the repository to get an MyType which is IsEnabled, Lazy Loading MyType.Items may mean that Items could be not enabled.
Is there a way, perhaps with EF Fluent to describe how to do filtering on tables?
Update:
If I have a Dbset
public class UnitOfWork : DbContext
{
private IDbSet<MyObj> _MyObj;
public IDbSet<MyObj> MyObjs
{
get { return _MyObj ?? (_MyObj = base.Set<MyObj>()); }
}
}
Is there any way I can tell the DbContext to filter the DbSet?
No, there is no way to define a filter for lazy loading (also not for eager loading using Include). If you want that your navigation collections only get populated with items where IsEnabled is true you can only shape your queries accordingly, for example with explicit loading:
context.Entry(parent).Collection(p => p.Items).Query()
.Where(i => i.IsEnabled)
.Load();
This will populate the Items collection of parent only with the enabled items.
Edit
I feel a bit like the messenger of the bad news about a lost battle who gets his head knocked off. Maybe it's too hard to believe that Entity Framework sometimes does not have the capabilities you want. To improve my chance to convince you I add a quote from an authority, Julie Lerman:
Neither eager loading nor deferred/lazy loading in the Entity
Framework allows you to filter or sort the related data being
returned.
It looks like it is still possible. If you are intrested you can take a look at the Wiktor Zychla blogpost, where he gives a solution to the soft delete problem.
This http://blogs.claritycon.com/blog/2012/01/25/a-smarter-infrastructure-automatically-filtering-an-ef-4-1-dbset/ basically defines how I can achieve what I was looking for.
Basically you create a FilteredDbSet and make all your DbContext IDbSet's return it.
In the "Admin" area of my application, an object must be available in ViewData on every page (for display in the Master template). I have already inherited from Controller, so I cannot make a simple base class that handles it. What is a good solution of doing this when not using inheritance? An ActionFilter seems interesting but I don't want to put it on every controller in the Admin area. I'm considering the following:
Custom ControllerFactory that detects Area as well
Application_BeginRequest(), though I have no knowledge on executing controller then.
Maybe you have a better solution?
In this case I would create a separate action that executes a partial view that shows the data you need. In my opinion this is the most clean solution for this kind of problem and it's easily testable and reusable.
i have a dropdown on my masterpage. you dont need viewdata for it. i did it like this
code on masterpage:
<%= Html.DropDownList("schselectr", MVC2_NASTEST.MvcApplication.masterSchooljaarList())%>
in Global.asax.cs
public static SelectList masterSchooljaarList() {
NASDataContext _db = new NASDataContext();
List<Schooljaar> newlist = _db.Schooljaars.ToList();
return new SelectList(_db.Schooljaars.ToList(), "Sch_Schooljaar", "Sch_Schooljaar");
}
so simply, it calls the method, which returns the data i need, every time you load the page. easy, clean, effective.