Setting a collection of related entities in the correct way in EF4 using POCO's (src is the DB) - entity-framework-4

I have a POCO entity Report with a collection of a related POCO entity Reference. When creating a Report I get an ICollection<int> of ids. I use this collection to query the reference repository to get an ICollection<Reference> like so:
from r in referencesRepository.References
where viewModel.ReferenceIds.Contains(r.Id)
select r
I would like to connect the collection straight to Report like so:
report.References = from r in referencesRepository.References
where viewModel.ReferenceIds.Contains(r.Id)
select r;
This doesn't work because References is an ICollection and the result is an IEnumerable. I can do ToList(), but I think I will then load all of the references into memory. There also is no AddRange() function.
I would like to be able to do this without loading them into memory.
My question is very similar to this one. There, the only solution was to loop through the items and add them one by one. Except in this question the list of references does not come from the database (which seemed to matter). In my case, the collection does come from the database. So I hope that it is somehow possible.
Thanks in advance.

When working with entity framework you must load objects into memory if you want to work with them so basically you can do something like this:
report.References = (from r in referencesRepository.References
where viewModel.ReferenceIds.Contains(r.Id)
select r).ToList();
Other approach is using dummy objects but it can cause other problems. Dummy object is new instance of Reference object which have only Id set to PK of existing object in DB - it will act like that existing object. The problem is that when you add Report object to context you must manually set each instance of Reference in ObjectStateManager to Unchanged state otherwise it will insert it to DB.
report.References = viewModel.ReferenceIds.Select(i => new Reference { Id = i }).ToList();
// later in Report repository
context.Reports.AddObject(report);
foreach (var reference in report.References)
{
context.ObjectStateManager.ChangeObjectState(reference, EntityState.Unchanged);
}

Related

DBContext (entity framework) and pre-loaded entities

I use code first in a web application where I have a form to upload text files and import the data into my database.
Each file may have up to 20.000+ records for import.
To speed things up I preload some entities so not to ask the DbContext every time. Then when I create an object for insert, I do for example:
myNewObject.Category = preloadedCategories.First(p => p.Code == code);
I have read some articles on the web because EF is extremey slow on batch inserts, so what I do is:
first use Configuration.AutoDetectChangesEnabled = false;
then every 1000 records I dispose the object and make a new one.
BUT! since the preloaded entities where loaded from a db context that was disposed, after making a new DbContext, I have a problem with preloadedCategories.First(p => p.Code == code). When I make a SaveChanges(), EF tries to also save the preloadedCategories.First(p => p.Code == code) object and fails.
So how can I achive this? I don't want to aks the DbContext every time to load some (non changing) objects. Is it possible?
thanks
When dealing with a large number of records in EF, a few things will help
As #janhartmann states, use .AsNoTracking()
As you stated, use Configuration.AutoDetectChangesEnabled = false, which will require the next point
Use context.Categories.Entry(category).State = EntityState.Modified to attach a disconnected entity to a context and mark is as modified
Also make check that preloadedCategories is no longer an IQuerable and that the data really is local and not trying to lazy load from the database.
If there are no changes to your Category object and you just want to link your myNewObject to an existing category, you have two options
Set the foreign key on myNewObject instead of the navigation property
Use context.Products.Entry(myNewObject).State = EntitySate.Added instead of context.Products.Add(myNewObject) to avoid it adding the entire graph of navigation properties
Good luck

Entity Framework: Explicit loaded foreign key attribute is null after creating new object

In my Entity Framework project, lazy loading is disabled and it want it to be disabled by default.
Up to now I've used to load foreign key values in case they're needed for further processing explictly with the Include() method.
However, whenever I put one of the objects which is holding a loaded foreign key object into another object, from the perspective of the outer object the foreign key seems to be null.
var teams = (from team in db.Teams.Include("Captain")
orderby team.Name
select new ExtendedTeam {
Team = team
}).AsEnumerable();
// teams.First().Customer.Captain is null though the Captain property was explictly included
How can I access the Captain property without disabling lazy loading at all? I'd prefer not to add a Captain property to the Extended Team class.
Your projection seems to be screwing up the Include.
I'm not sure where the "customer" variable came from, but if you need to do a projection like that you can do something similar to:
var teams = (from team in db.Teams
orderby team.Name
select new {
Team = team,
Captain = team.Captain,
}).ToList(); // execute the query
That will give you an anonymous type of Customer and Captain. You can extract the data however you need to from that to populate your ExtendedTeam object.
Since I called ToList() on the query I have executed the query, and my anonymous object has the Team object and the Captain object. As a side effect of this, Entity Framework has also done a "fix-up" of the relationships. If you inspect the anonymous type Team property, you will notice that Captain is also populated in it. EF was smart enough to know that they were related and it did relationship fix-up on the Team objects. You can now process the results like so:
var extended = teams.Select(t => new ExtendedTeam { Team = t });
That should work.

Entity Framework: I don't understand the purpose of EntityCollection.Attach and EntityReference.Attach

ObjectContext.Attach and ObjectSet.Attach are used to attach a detached entity ( which already exists in a DB ) to a context – this way when ObjectContext.SaveChanges is called, EF doesn't try to send an insert command for this attached entity
But I don't understand the purpose of EntityCollection.Attach and EntityReference.Attach. Namely, the two methods can only attach entities that are already managed by ObjectContext ( thus they can't be used to attach entities with EntityState set to Added or Detached ).
And since entities managed by ObjectContext already have their relationships automatically resolved ( ie their EntityReference property returns a parent entity and their EntityCollection property contains related child entities ), I fail to understand what exactly would we gain by using EntityCollection.Attach or EntityReference.Attach to attach a related entity E1 to a particular entity E2, since E1 was already attached to E2 automatically by ObjectContext?
Thank you
As an example, if you have a customer in the ObjectContext and you want to get that
customer’s reservations, you could call the following:
myCust.Reservations.Load()
This would load all of the reservations for that customer.
However, if you want to filter those reservations, you can use CreateSourceQuery in-
stead, as shown in the following code:
var customer=context.Contacts.OfType<Customer>().First();
var sourceQuery = customer.Reservations.CreateSourceQuery()
.Where(r => r.ReservationDate > new DateTime(2008, 1, 1));
customer.Reservations.Attach(sourceQuery);
The query will execute when the Attach method is called. Now only the subset of reservations for that customer will be retrieved from the database and materialized as
objects.
You can also use CreateSourceQuery to filter on types. In the following code, Attach is
being used with an EntityReference, which will not take IQueryable. Instead, you need
to pass in an object, which you can get using the FirstOrDefault query method. Since
Attach will throw an exception if you attempt to pass in a null, you need to test for null
before calling Attach:
var addresses = context.Addresses.Take(5);
foreach (var a in addresses)
{
var sq = a.ContactReference.CreateSourceQuery()
.OfType<Customer>().FirstOrDefault();
if (sq != null)
a.ContactReference.Attach(sq);
}
With this code, only customers will be loaded.

How do I deeply eager load an entity with a reference to an instance of a persistent base type (Entity Framework 4)

Above is a simplified version of our domain model. NotificationOrder has a reference to an instance of a sub class (consider ReferenceNumberBase logically abstract).
Problem:
I want a query to return all NotificationOrders that satisfies XYZ and I want that query to eagerly load all referenced instances of CustomerCase (including all related objects of that graph, except Group forget about that issue for the moment).
I've tried searching for a solution to this, but all I've found are solutions to problems equivalent of querying for CustomerCase as a root object directly.
I'd like something like this:
var query = ObjectContext.CreateObjectSet<NotificationOrder>.Where(e => e.NotificationType == "Foo");
return ((ObjectSet<NotificationOrder>) query).Include("ReferenceNumberBase");
However, that won't load the Vehicle instance of CustomerCase or any of the other related objects. How can I express this so EF understands the eager load I want (I'd very much like to avoid multiple roundtrips / notification order)?
NOTE: Since CustomerCase is a derived type I can't do normal transitive include using something like this:
var query = ObjectContext.CreateObjectSet<NotificationOrder>.Where(e => e.NotificationType == "Foo");
return ((ObjectSet<NotificationOrder>) query).Include("ReferenceNumberBase.Vehicle"); //
since the Vehicle property is a member of the derived CustomerCase type, and not the ReferenceNumberBase type and instead we get errors like:
"The EntityType 'Model.ReferenceNumberBase' does not declare a navigation property with the name 'Vehicle'."
Neither can I use query.OfType<CustomerCase>... since the query type is NotificationOrder, and not ReferenceNumberBase (or can I somehow?).
ps. We are using self tracking POCO entities with EF4 (have not upgraded to 4.1 yet)
EDIT: I've searched some more, and as of about a year ago this looks to have been a limitation of the Include() method (at least at that time). Is this accurate and has this been adressed since then? [sources below]
http://social.msdn.microsoft.com/Forums/en-US/adodotnetentityframework/thread/a30351ab-5024-49a5-9eb4-798043a2b75d
http://data.uservoice.com/forums/72025-ado-net-entity-framework-ef-feature-suggestions/suggestions/1057763-inheritance-eager-loading?ref=title
https://connect.microsoft.com/VisualStudio/feedback/details/594289/in-entity-framework-there-should-be-a-way-to-eager-load-include-navigation-properties-of-a-derived-class

Cannot insert new Employee entity using InsertOnSubmit()

I'm facing this exception An attempt has been made to Attach or Add an entity that is not new, perhaps having been loaded from another DataContext. This is not supported. when I try to insert a new entity into my Employees table (the master one).
There is a relationship between the master Employees table and the details Orders table, and I'm sure that the relationship between these two tables (and specifically Employee.Orders EntitySet) is the cause of the problem since when I removed the relationship, it returns back to insert into Employees table with no problems.
When I searched for the problem, there was this blog post which I tried to implement but my case is a different than the one in the blog post in these items:
He faces the exception when tries to update (while I try to insert).
The tables architecture is different.
how can I solve this problem?
Here's the insertion code:
Employee emp = new Employee();
emp.Name = empName; // empName is a local variable
// What should I default emp.Orders to?
dc.Employees.InsertOnSubmit(emp);
dc.SubmitChanges();
P.S: My DataContext is defined on class-level in my repository and the exception is being thrown when I call dc.SubmitChanges();. and I didn't Attach any object why does it say that?
Here is an article explaining what you need to do using the Attach and Detach methods:
http://www.codeproject.com/KB/linq/linq-to-sql-detach.aspx
I am guessing it is trying to save something else besides just the employee object or you aren't showing us the full code in your repository. When you instantiate your DataContext object (dc) try setting DeferredLoadingEnabled = false, and ObjectTrackingEnabled = false and see if it works. If it does, try watching the SQL code in SQL Server Profiler and see if it is modifying other objects that may have came from a different context like the message says.
var dc = new MyDataContext()
{
DeferredLoadingEnabled = false,
ObjectTrackingEnabled = false
};
My bet is on the primary key.
Are you sure the primary key is also set on auto increment?
Did you
try changing the name, does it work then?
What happens if you remove
all rows from your DB? can you insert one then?

Resources