Eager loading and navigation properties - c#

var ret = (from f in context.foo
join b in context.bar on f.barid = b.barid
select f).ToList();
my returning list cointains all foos that have a barId, it also contains all navigation properties. What I mean by that is,
context.foo.mark is populated even though I didnt not explicitly include it nor did i access it during the query. I have lazy loading turned on, why is this occuring?
To elaborate on my question, somehow my related entities are getting loaded from the above query.I am curious as to how that is occurring, I have lazy loading enabled and I am not accessing any of the related objects

The lazy loading inspection is kind of a "catch-22" type problem. With Lazy Loading turned on, even a call to the property from the debugger will load the results as long as your context is still hanging around. Furthermore, if your context is still open from other queries, EF will maintain the state of those objects automatically and include them.
The only real way I can think of to determine if it is being lazily loaded or not is to inspect the SQL code sent to your database.
First, add this line to your DbContext constructor:
this.Database.Log = s => System.Diagnostics.Debug.WriteLine(s); //remove in production
Then, run your code as normal (but don't stop in the debugger to inspect your object). Look at your debug console and inspect the SQL calls made. My bet is that the SQL will not include the related properties.
If you run the code again, and stop the debugger to inspect the object properties, you should see another SQL call in the debug console fetching the related entities.

Related

EF attaching object to context in foreach working only first time

I have a list of objects that I need to attach to the Context in order to track changes and, afterward, saving them, but the foreach iterating the items executes only the first time, after that the method ends.
I'm certain that those items already exist in the database.
I have tried both calling the .Attach() method and setting the Entry state to Unchanged.
protected override Task SetViewModelPropertiesAsync()
{
SelectedItems.ForEach(l =>
{
//Context.Pap_Pedido_ODP.Attach(l);
Context.Entry(l).State = System.Data.Entity.EntityState.Unchanged;
// After the first iteration the method ends
});
return base.SetViewModelPropertiesAsync();
}
I expect all the items to be added to context but after the first iteration of the foreach loop breaks the method and continues to the next one, without even giving an exception.
EDIT:
There is more code after the foreach that is being skipped when I do either the Attach or EntityState.
If I comment both the code executes correctly
The behaviour does sound like an exception is being thrown. This is IMO a huge red-flag about List<T>.ForEach() and the main reason I never use it. If you alter your code to:
foreach(var item in SelectedItems)
{
Context.Pap_Pedido_ODP.Attach(item);
Context.Entry(item).State = System.Data.Entity.EntityState.Unchanged;
}
... you should at least now see the exception(s) that are blocking your code. Attaching/Detaching entities between contexts is messy and there are very, very few scenarios where I personally can ever justify it. You are dealing with references to an entity. This means that:
item must not already be associated to any other context.
Context must not already have another entity tracked with the same PK as item.
Point #1 will hinder you because any code returning an entity that "might" want to attach that entity to another context will need to detach, or otherwise load that entity AsNoTracking. Passing a tracked entity to your method from somewhere will break your code.
Point #2 will hinder you because even if the entity passed is detached, if your context happens to already know about that entity via another reference, you have to essentially discard that untracked entity, and use the reference to the tracked instance. This means before attaching any entity you need to check Context .Local for a matching entity.
Only if the entity isn't tracked, and the context does not have a tracked entity with the same PK can you attach it.
If your code is not breaking on an exception and you are debugging, make sure your debug exception handling is set to break on all exceptions. Alternatively you can pop a try/catch block with a breakpoint in the catch to inspect the exception.
Edit: To check instances
foreach(var item in SelectedItems)
{
if(Context.Pap_Pedido_ODP.Local.Contains(item))
{ // This exact instance is already associated to the Context.
// We shouldn't need to copy anything across or do anything...
}
else
{
var existingItem = Context.Pap_Pedido_ODP.Local.SingleOrDefault(x => x.Id == item.Id);
if(existingItem != null)
{ // A different instance matching this one already exists in the context,
// Here if item represents changes we would need to copy changes across to existingItem...
}
else
{ // Item is not associated, safe to attach.
Context.Pap_Pedido_ODP.Attach(item);
// ...
}
}
}
Now it doesn't end there. If "item" contains references to other entities, each and every one will be updated automatically. This can cause problems if some of them have already been associated to the context. This can be caused when the DbContext is too long-lived or where multiple copies of the same instance of a referenced entity are passed back. For instance if I have a set of Orders being saved, and Orders contain references to Customers. 2 orders have a reference to the same customer. When I attach Order #1 to Customer #1, Customer 1 is now associated to the context. When I try to attach Order #2, the instance of Customer #1 is a different instance to Order #1 so attaching Order #2 will generate an error. When dealing with detached entities, you need to take steps to ensure that all instances of objects in the graph that refer to the same record are using the same object instance reference. When you loaded the data from EF, these would be the same object reference, but if you feed them to a Serializer/Deserializer you will get 2 identical copies as separate references. You cannot simply re-attach those object references.
Unfortunately there's no really simple answer I can offer to make it easier, but this is why serializing and deserializing entities can be a terrible idea, and even detaching/attaching instances can be a pain.

Upgrading to EntityFramework 6.1.3 and now receiving context exceptions

I recently upgraded from Entity Framework 5 to Entity Framework 6.1.3.
The below code using multiple contexts of the same connection worked fine before in EF5:
var Ids = MyDbContext.MyObject.Select(x => x.Id).Take(5).AsEnumerable();
var myObjects = MyDbContext2.MyObject.Where(x => Ids.Contains(x.Id)).ToList();
In EF6, I receive:
The specified LINQ expression contains references to queries that are
associated with different contexts. Description: An unhandled
exception occurred during the execution of the current web request.
Please review the stack trace for more information about the error and
where it originated in the code.
Exception Details: System.NotSupportedException: The specified LINQ
expression contains references to queries that are associated with
different contexts.
What in Entity Framework changed to stop this from working? Is there anyway I can get this to work without changing code?
Change first line from .AsEnumerable() to .ToList().
https://msdn.microsoft.com/en-us/data/hh949853.aspx#_Query_Plan_Caching
According to this documentation, there were changes made to Contains processing in EF 6 to optimize the way the underlying SQL query is generated.
Just a shot in the dark without looking at EF6's code:
IEnumerable is generally a deferred execution that doesn't hit the database until you reference the data in some way. From the framework's point of view, that isn't a list of integers or longs, but a query that needs to be performed in a different context. Since it's in the middle of a query in a different context, the SQL parser is probably having trouble resolving it with their new way of doing things. IEnumerable is a sort of half way state between Queryable and loaded. I'd guess whatever changes they made for optimization do not perform the outstanding queries any longer, and it just immediately short circuits to an exception if the referenced object isn't part of the context, no matter what it is.
This is also why changing it to a List() allows it to work. You're working off of a list of primitives and not an unresolved query.
Why did they make the change? I suppose they have their reasons (even beyond optimization). One I can think of is that it prevents the query generation parts from modifying the loaded state of that IEnumerable to remove that possibly unwanted side effect.
You can add the ToDictionary():
var Ids = (
from x in MyDbContext.MyObject.Select()
where x.Contains(x.Id)
select x
).ToDictionary(x => x.Key).Keys.ToList();
var myObjects = (
from y in MyDbContext2.MyObject
where y => Ids.Contains(y.Id).ToList()
).ToList();
return myObjects;

Unused results from Entity Framework database call

Any decent compiler should eliminate dead code, at least to a certain extent. However, I am curious how a compiler (specifically MSBuild) handles a situation like the following:
// let's assume LazyLoadingEnabled = false;
var users = db.Users.ToList();
// more code that never touches 'users'
Since LazyLoadingEnabled = false, will the compiled code:
Eagerly load the results from the database call
Make the call to the database without storing the results
or
Never make the call to begin with?
I was cleaning up some old code at work and I found several cases of this occurring, so I'm curious as to whether we've been wasting resources or not.
It feels like the right answer is number 3, but I haven't found any solid evidence to back up my claims. Thank you for your help!
The answer is #1.
Not only will this execute the database query to select all the records from the Users table, but it will fetch all those records and construct entities for each of those records in the Users table. Very expensive if you have many records. Of course, the GC will eventually collect the wasted resources.
If you want to prove the above for yourself, just add the following line after you create your DbContext to log the SQL being executed:
db.Database.Log = s => Console.WriteLine(s);
BTW, the LazyLoadingEnabled setting has no effect on the observed behavior. The LazyLoadingEnabled setting determines if navigational properties are eagerly loaded or not. In this case, db.Users is not a navigational property, so it has no effect.

Entity Framework - Lazy Loading working even with ToList()

First of all, I am using EF 6.0 with Code First approach.
My context Configuration is set to Enable "Proxy Creation" and "Lazy Loading".
My question is:
Does the lazy loading work with results of a method that returns IEnumerable (and not IQueryable)?
I think the code below is more explanatory:
public void Test()
{
var company = GetCompanies().FirstOrDefault();
if (company.Employees.Count() > 0)
{
//I got here without errors!
}
}
public IEnumerable<Company> GetCompanies()
{
var company = context.Companies.ToList();
//Note that I did not Include the Employee (child table)
return company;
}
Note comment where I say that: "I got here without errors!". It means that lazy loading is working even after ToList() call. I thought that after converting IQueryable to List or IEnumerable the EF would lose the capability of doing lazy loading.
I have noted that the Proxy still enabled for the entities that are returned by GetCompanies method (in debbug mode I can see that ugly hash like: System.Data.Entity.DynamicProxies.Company_7035BEA374959AC1...).
The lazy loading works even when calling it on different DLL. Is this correct? I mean, can a different DLL make subsequent calls in my database even if my method returns an IEnumerable (and not IQueryable)?
Any clarification will be greatly appreciated.
Note that comment that I say: "I got here without errors!". It means
that lazy loading is working even after ToList() call.
That's the whole point of lazy-loading: you can get entities from the DB when they are required (i.e. when you access to the property), not only when you execute the query for the first time (i.e. your call to .ToList()).
The lazy loading works even calling it on different DLL. Is this
correct? I mean, can a different DLL made subsequent calls in my
database even if my method return an IEnumerable (and not IQueriable)?
Yes it's correct, but be careful, if you dispose your context, lazy loading won't work (it'll throw an ObjectDisposedException).
Also, while your code will work, you might have performance issues because of the number of SQL requests generated.
Side note: personally I recommend to not use lazy-loading. See https://stackoverflow.com/a/21379510/870604

How to clear the DataContext cache on Linq to Sql

I'm using Linq to Sql to query some database, i only use Linq to read data from the DB, and i make changes to it by other means. (This cannot be changed, this is a restriction from the App that we are extending, all updates must go trough its sdk).
This is fine, but I'm hitting some cache problems, basically, i query a row using Linq, then i delete it trough external means, and then i create a new row externally if i query that row again using linq i got the old (cached) data.
I cannot turn off Object Tracking because that seems to prevent the data context from auto loading associated propertys (Foreign Keys).
Is there any way to clear the DataContex cache?
I found a method sufring the net but it doesn't seem safe: http://blog.robustsoftware.co.uk/2008/11/clearing-cache-of-linq-to-sql.html
What do you think? what are my options?.
If you want to refresh a specific object, then the Refresh() method may be your best bet.
Like this:
Context.Refresh(RefreshMode.OverwriteCurrentValues, objectToRefresh);
You can also pass an array of objects or an IEnumerable as the 2nd argument if you need to refresh more than one object at a time.
Update
I see what you're talking about in comments, in reflector you see this happening inside .Refresh():
object objectByKey = context.Services.GetObjectByKey(trackedObject.Type, keyValues);
if (objectByKey == null)
{
throw Error.RefreshOfDeletedObject();
}
The method you linked seems to be your best option, the DataContext class doesn't provide any other way to clear a deleted row. The disposal checks and such are inside the ClearCache() method...it's really just checking for disposal and calling ResetServices() on the CommonDataServices underneath..the only ill-effect would be clearing any pending inserts, updates or deletes that you have queued.
There is one more option, can you fire up another DataContext for whatever operation you're doing? It wouldn't have any cache to it...but that does involve some computational cost, so if the pending insert, update and deletes aren't an issue, I'd stick with the ClearCache() approach.
I made this code to really CLEAR the "cached" entities, detaching it.
var entidades = Ctx.ObjectStateManager.GetObjectStateEntries(EntityState.Added | EntityState.Deleted | EntityState.Modified | EntityState.Unchanged);
foreach (var objectStateEntry in entidades)
Ctx.Detach(objectStateEntry.Entity);
Where Ctx are my Context.
You should be able to just requery the result sets that are using this objects. This would not pull a cached set, but would actually return the final results. I know that this may not be as easy or feasible depending on how you setup your app...
HTH.

Categories