Linq ToList() fails to trigger Immediate Execution - c#

I am having a very peculiar problem: the ToList() extension method is failing to convert results to a list. here is my code, standard boilerplate linq query, I converted ToList() twice for good measure
var assets = new List<Asset>();
using (var ctx = new LeaseContext())
{
assets = ctx.Assets.OrderBy(o => o.Reference).Where(w => w.Status == AssetStatus.Active).ToList();
assets.ToList();
}
return assets;
yet the assets are still a list of System.Data.Entities.DynamicProxies....
I've never had this problem before.

The reason is lazy loading. When lazy loading is enabled in EF (by default), then (again, by default) EF creates dynamic proxies for each entity. It's required for loading related entities. Dynamic proxy will be inherited from entity class. So in your case it will be inherited from Asset. But dynamic proxy will have reference to the context which created its instance. And it will override navigation properties (which are virtual) to query entities via context which is stored in dynamic proxy.
And it's completely legal to add instances of derived types to list of base type.
If you don't want dynamic proxies, then just disable lazy loading and proxy creation:
ctx.Configuration.LazyLoadingEnabled = false; // turn-off loading on-demand
ctx.Configuration.ProxyCreationEnabled = false; // turn-off wrapper class generation
Technically you can just turn-off proxy generation and lazy loading will not work. But I prefer to turn-off both settings explicitly.

Related

The ObjectContext instance has been disposed when using entity in view

There were many related questions, but none solved my matter. My purpose here is generating a pdf using Razor PDF.
So I have a controller action, which contains;
var pdf = new PdfResult(null, "myView");
ViewBag.VrList = MyDbQuery.GetExpiredVL(DateTime.Today);
return pdf;
MyDbQuery is in a different solution which I'm using. And there I have this method:
public static List<VLEntity> GetExpiredVL(DateTime ReportDate)
{
using (MyDbContext db = new MyDbContext())
{
return db.VLEntity.Where(vl => vl.ValidTo.Month == ReportDate.Month && vl.ValidTo.Year == ReportDate.Year).ToList();
}
}
My view looks like:
#foreach (var vrRow in ViewBag.VrList)
{
#vrRow.VEntity.VssId
}
When I debug, I get:
System.ObjectDisposedException: The ObjectContext instance has been disposed and can no longer be used for operations that require a connection.
In similar questions i have found here says to use using statement. But you can see here I have already used that. I'm very new to ASP.NET and C# altogether and I'll be grateful if you can come up with a solution for this.
In this code:
#foreach (var vrRow in ViewBag.VrList)
{
#vrRow.VEntity.VssId
}
You're accessing the navigation property VEntity. If you don't disable lazy loading (which is enabled by default), this property isn't loaded from the database when querying, but only when accessing the property (through proxy generation).
In your data access method you do db.VLEntity.[..].ToList(), but that only materializes the VLEntitys returned from the query - not their navigation properties.
Because of the disconnected scenario (the action method in the controller runs first, then hands its data off to the view), the context is not accessible anymore where you access the navigation property.
For this, you need to explicitly load the navigation property (as you claim you did):
return db.VLEntity
.Include(v => v.VEntity)
.Where([..])
.ToList();
Now when the view accesses the VEntity properties of the list are also materialized, so your view doesn't have to access the database anymore.
You also say it worked before, but then you probably just didn't access any navigation properties.
Related: Best practises in MVC for not disposing the object context?, How to solve the error The ObjectContext instance has been disposed and can no longer be used for operations that require a connection.
Alternatives would be to keep the DbContext around longer (which you shouldn't want) and introducing a ViewModel that you map to in the controller.

Entity framework update change

I have an entity model with a self relation (parent/child). My entity named Article has a property called parent. This parent is in fact the relation, and ParentID which is the field in the relation. In ef 4 i did this:
using (var dbContext= new DataBaseModel())
{
ArticleTable newEntity= new ArticleTable();
newEntity.name="childArt";
newEntity.ParentID = 1;
dbContext.ArticleTable.Add(newEntity);
dbContext.SaveChanges();
//after calling save I can do this
var parentName = newEntity.Parent.Name;
}
With entity framework 6, this doesn't work any more, I have get the entity from the database again in order to get the related parent entity. Is this because of changes to lazyloading? what should i do different.
The difference is that in EF 4 entities were generated with piles of code that took care of change notification and lazy loading. Since then, the DbContext API with POCOs has become the standard.
If you want the same behavior as with the old 'enriched' entities you must make sure that lazy loading can occur by a number of conditions:
The context must allow lazy loading. It does this by default, but you can turn it off.
The navigation properties you want to lazy load must have the virtual modifier, because
EF must create dynamic proxies. These proxies somewhat resemble the old generated entities in that they are able to execute lazy loading, because they override virtual members of the original classes.
The last (and maybe second) point is probably the only thing for you to pay attention to. If you create a new object by new, it's just a POCO object that can't do lazy loading. However, if you'd create a proxy instead, lazy loading would occur. Fortunately, there is an easy way to create a proxy:
ArticleTable newEntity= dbContext.ArticleTables.Create();
DbSet<T>.Create() creates dynamic proxies -
if the underlying context is configured to create proxies and the entity type meets the requirements for creating a proxy.

Why does disabling lazy loading cause related tables to have no results?

Given:
public SomeEntity Read(int primaryKey)
{
SomeEntity myEntity;
using (var context = new MyEntities2())
{
context.Configuration.LazyLoadingEnabled = false;//This line is wacky
myEntity = context.SomeEntities.SingleOrDefault(ct => ct.PrimaryKey == primaryKey);
if (myEntity == null)
return myEntity;
//Force eager Load...
var bypassDeferredExecution = myEntity.RelatedTable1.ToList();
var bypassDeferredExecution2 = myEntity.RelatedTable2.ToList();
}
return myEntity;
}
If I set LazyLoadingEnabled = false then myEntity.RelatedTable1.Count == 0.
Leave at the default LazyLoadingEnabled = true then myEntity.RelatedTable1.Count == 2.
My understanding is that Lazy Loading and Eager Loading are polar opposites. I forced eager loading. I expect my related table (a cross reference table) to have 2 results whether or not I use lazy loading. So in my mind these results make no sense.
Why does lazy loading impact my results?
You have to use Include to eagerly load related entities:
myEntity = context.SomeEntities
.Include("RelatedTable1")
.Include("RelatedTable2")
.SingleOrDefault(ct => ct.PrimaryKey == primaryKey);
Setting Lazy Loading to false won't cause it happen automatically.
If you are using lazy loading, then there needs to be a LINQ to Entities Include method call to identify the (foreign keyed) tables to eagerly load.
Navigation property isn't query, it's enumerable collection. You have 2 ways to get it from DB:
- Lazy loading (will be loaded on the first access to property)
- Eager loading (will be loaded after executing main query if you add Include({propertyName} method
So, if you turned off lazy loading and don't add Include methods to the query each navigation property will be empty (empty collection or null value for single entities)
The following code should work for your case:
myEntity = context.SomeEntities
.Include("RelatedTable1")
.Include("RelatedTable2")
.SingleOrDefault(ct => ct.PrimaryKey == primaryKey);
Lazy loading defers the initialization of an object until it is needed. In this case it will automatically execute a query to the DB to load the object requested.
Eager loading loads a specific set of related objects along with the objects that were explicitly requested in the query.
So in order to use Eager Loading you need to specify the related objects that you want to load.
In EF you can achieve this using the method Include from ObjectQuery.
context.Entity.Include("RelatedObject");

Why can't you attach new objects to tracked objects using navigation references?

In short, why does this fail (myChildObject is not added to the database). Note that it works with ObjectContext:
using (var db = new dbEntities())
{
var myParentObject = db.parentObjects.First(); // this definitely exists in the db
// this **should** attach myChildObject to the context,
// and therefore add it to the db on .SaveChanges() - it doesn't!
var myChildObject = new childObject(){
parentObject = myParentObject
};
db.SaveChanges();
}
This MSDN Blog Post says
You can add a new entity to the context by hooking it up to another entity that is already being tracked. This could be by adding the new entity to the collection navigation property of another entity or by setting a reference navigation property of another entity to point to the new entity.
Surely the above code should work because myChildObject references myParentObject which is a tracked object. EF should be smart enough to figure out that it needs adding into the childObjects collection. It worked fine when I was using ObjectContext and now I'm finding that I need to rewrite all of my code to get it to work with dbContext.
To get it to work I have to rewrite it like this:
using (var db = new dbEntities())
{
var myParentObject = db.parentObjects.First(); // this definitely exists in the db
var myChildObject = new childObject();
myParentObject.childObjects.Add(myChildObject);
db.SaveChanges();
}
If you were using POCO entities with ObjectContext it worked indeed. But not because EF's change tracking worked differently than with DbContext but because the POCO entities generated by the EF 4 T4 templates contained "relationship fixup" methods.
Basically the property setter for the line parentObject = myParentObject wasn't only an object assignment but the setter included a call to a method that in the end exactly did what you are doing manually now, namely: myParentObject.childObjects.Add(myChildObject). At this point the rule "You can add a new entity to the context by hooking it up to another entity that is already being tracked" applies and myChildObject gets added to the context and inserted into the database.
For the T4 templates that generate POCO entities for DbContext those fixup methods have been removed because they were causing trouble in other scenarios. Especially when lazy loading is involved your reference assignment and the automatic call of myParentObject.childObjects... in the property setter would trigger lazy loading on the collection and load all childObjects first that are already stored for myParentObject before the new child is added to the collection. If those are thousands this is a huge unnecessary overhead, performance gets disastrous and the reason for a suddenly bad performance (just because you assigned a single reference property) isn't easy to detect if you are not aware of the fixup methods that run behind the scenes.
Here and here and here and here are examples about the confusion that relationship fixup methods were causing.
You could modify the T4 templates and add relationship fixup methods again - or if you are using Code-First just write them by hand in your entity classes - to get the old behaviour back. But this might be more complex than and at least as much work as changing your existing code the way you've outlined in your last code snippet - which I would certainly prefer over having those bothersome fixup methods back.
#theyetiman you're doing a little interpretation mistake of the blog text.
see:
[...]
or by setting a reference navigation property of another entity to point to the new entity.
In this part the blog said you can set a reference navigation property of a tracked object with a new entity.
like this:
[tracked entity].NavigationProperty = [new entity];
But you tring to do:
[new entity].Navigation Property = [tracked entity];
This not works. If your childObject was tracked and parentObject not you would be able to add parentObject setting it in childObject property, but the opposite is not true.

Loading from database without proxy classes?

In Entity Framework 4 Is it possible to choose to load some queries into a POCO without it using proxy classes? (For the purpose of caching that object for future read only use). I am using the Repository - Service pattern.
By this I mean:
var order = _orderService.GetById(1);
// after order is loaded then we can see in the debugger that:
// order.Customer is of type System.Data.Entity.DynamicProxy.Customer_17631AJG_etc
What I want is for the order.Customer to actually use the POCO type MyApp.Models.Entities.Customer instead of a proxy to that type.
EDIT: Based on Ladislav's suggestion to add a "GetUnproxied" method to the Repository, I've made this change:
// this is the current method that must return a DynamicProxy
public IQueryable<T> GetQuery()
{
return ObjectSet.AsQueryable();
}
// this is the new additional method that must return the plain POCO
public IQueryable<T> GetReadOnly()
{
ObjectContext.ContextOptions.ProxyCreationEnabled = false;
var readOnly = ObjectSet.AsQueryable();
ObjectContext.ContextOptions.ProxyCreationEnabled = true;
return readOnly;
}
Is this correct?
It does not look thread safe to me. Both methods use the same ObjectContext instance, so it might be possible for ProxyCreationEnabled == false to happen on one thread and then public IQueryable<T> GetQuery() to be called on another thread - which would suddenly mean that the proxy method could return the non proxied object.
Use this before you query data to turn off proxy creation
context.ContextOptions.ProxyCreationEnabled = false;
I think it can be also turned off globally in EDMX designer.
Update:
This applied to ObjectContext. With DbContext the code is:
context.Configuration.ProxyCreationEnabled = false;
plus I do not see any option in the edmx designer

Categories