Entity Framework Eager Loading Loads Everything - entity-framework

We are using Entity Framework + Repository Pattern in a web based application to fetch database . Because of our complex business, our models are getting complex sometimes and this cause strange behaviour at Entity Framework eager loading system.
Please imagine our real model like this. We have tables, boxes which are on table, pencil cases which can be on table or in the box and pencils that can be on the table or in the box or in the pencil case.
We had modelled this in our application like this.
public class Table
{
public int TableID{ get; set; }
public virtual ICollection<Box> Boxes{ get; set; }
public virtual ICollection<PencilCases> PencilCases{ get; set; }
public virtual ICollection<Pencils> Pencils{ get; set; }
}
public class Box
{
public int BoxID{ get; set; }
public int TableID{ get; set; }
[ForeignKey("TableID")]
public virtual Table Table{ get; set; }
public virtual ICollection<PencilCases> PencilCases{ get; set; }
public virtual ICollection<Pencils> Pencils{ get; set; }
}
public class PencilCases
{
public int PencilCaseID{ get; set; }
public int? BoxID{ get; set; }
public int TableID{ get; set; }
[ForeignKey("TableID")]
public virtual Table Table{ get; set; }
[ForeignKey("BoxID")]
public virtual Box Box{ get; set; }
public virtual ICollection<Pencils> Pencils{ get; set; }
}
public class Pencils
{
public int PencilID{ get; set; }
public int? PencilCaseID{ get; set; }
public int? BoxID{ get; set; }
public int TableID{ get; set; }
[ForeignKey("TableID")]
public virtual Table Table{ get; set; }
[ForeignKey("BoxID")]
public virtual Box Box{ get; set; }
[ForeignKey("PencilCaseID")]
public virtual PencilCase PancelCase{ get; set; }
}
Our repository pattern implementation similar with this tutorial, http://www.asp.net/mvc/tutorials/getting-started-with-ef-5-using-mvc-4/implementing-the-repository-and-unit-of-work-patterns-in-an-asp-net-mvc-application
So we call get method like this.
var tables = unitOfWork.TableRepository.Get(includeProperties: "Boxes, PencilCases, Boxes.Pencils");
So the problem is the result is very different from my expectations;i expect only Boxes,PencilCases and Boxes.Pencils collections will be fetched, but all the Pencil entities fetched from database including Pencils, PencilCases.Pencils and Boxes.PencilCases.Pencils. This recursive fetch causes OutOfMemoryException because amount of data.
I couldn't understand why Entity Framework fetches all Pencils except Boxes.Pencils. I also tried to specify including list with Expression instead of Query Path but result didn't change.

first off - I'm fairly new to EF myself so please excuse if the following is not 100% accurate. However, I've dealt with this exact same problem just a couple of days ago, so hopefully this will help.
The problem is that when EF loads a specific entity, it will add that entity to every part of the Data Model that it appears in - not just the parts that were explicitly loaded.
This means that every Pencil in Boxes.Pencils that is also in the ICollection of Table.Pencils will be automatically resolved even though you did not specifically ask for it.
By itself that fact does not present a problem, and can even be helpful in a user-driven MVC application.
Where it all goes wrong is when you try to do anything that recurses trough the Data Entity, such as trying to map the self-recursing Data Entity to a Business Model or trying to turn the self-recursing data entity into JSON/XML.
Now, there are several solutions to this problem:
Implement a mapper / encoder that hashes / remembers each object and only adds it once:
The problem with this one is that it can lead to some hard-to-predict results, especially when you want / need the object in multiple places. Additionally, hashing and comparing every object could be costly.
Implement a mapper / encoder that can be configured to ignore some properties
Relatively simple - if you can specify that you don't want to map or encode Pencil at all, you won't have any issues. Downsides are of course that you could still encounter a stackoverflow if you are not vigilant about specifying the ignored properties.
Implement a mapper / encoder with specifyable recursion depth
This is a very simple and pretty decent solution - simply set a hard limit on recursion depth, either on a global or on a per-type basis, and you won't have any more stackoverflows. Downside is that you would still end up with elements that you don't want, and thus get a unnecessarily bloated return object.
Implement custom business entities
This is probably the best solution - simply create a new business entity with the offending navigational properties removed. The primary downside is that it would require you to create different business entities for different purposes.
Here is a example:
// Removed Pencils
public class BusinessTable
{
public int TableID{ get; set; }
public IEnumerable<Box> Boxes{ get; set; }
public IEnumerable<PencilCases> PencilCases{ get; set; }
}
// Removed Table & PencilCases
public class BusinessBox
{
public int BoxID{ get; set; }
public int TableID{ get; set; }
public IEnumerable<Pencils> Pencils{ get; set; }
}
// Removed Table & Box & Pencils
public class BusinessPencilCases
{
public int PencilCaseID{ get; set; }
public int? BoxID{ get; set; }
public int TableID{ get; set; }
}
// Removed Table, Box, PencilCase
public class BusinessPencils
{
public int PencilID{ get; set; }
public int? PencilCaseID{ get; set; }
public int? BoxID{ get; set; }
public int TableID{ get; set; }
}
Now when you map your Data Entity to this set of Business Entities, you won't get any more errors.
For the mapping aspect of this, theres 2 solutions: Manually doing things / using a mapping factory Example of Model Factory, ValueInjecter and AutoMapper - the latter two being available NuGet packages.
For AutoMapper:
I don't use AutoMapper, but you'd have to create a config file that looks something like this:
Mapper.CreateMap<Table, BusinessTable>();
Mapper.CreateMap<Box, BusinessBox>();
Mapper.CreateMap<PencilCases, BusinessPencilCases>();
Mapper.CreateMap<Pencils, BusinessPencils>();
And then in your query:
var tables = unitOfWork.TableRepository.Get(includeProperties: "Boxes, PencilCases, Boxes.Pencils");
var result = Mapper.Map<IEnumerable<Table>, IEnumerable<BusinessTable>>(tables);
Or
var tables = unitOfWork.TableRepository.Get(includeProperties: "Boxes, PencilCases, Boxes.Pencils").Project().To<IEnumerable<BusinessTable>;
For more info pertaining AutoMapper ( like how to set up a config file ): https://github.com/AutoMapper/AutoMapper/wiki/Getting-started
For ValueInjecter:
var tables = unitOfWork.TableRepository.Get(includeProperties: "Boxes, PencilCases, Boxes.Pencils");
var result = new List<BusinessTable>().InjectFrom(tables);
Or:
var tables = unitOfWork.TableRepository.Get(includeProperties: "Boxes, PencilCases, Boxes.Pencils");
var result = tables.Select(x => new BusinessTable.InjectFrom(x).Cast<BusinessTable>());
It might also be worthwhile to look at additional ValueInjecter Injections, like SmartConventionInjection, Deep Cloning, Useful Injections and a ORM with ValueInjecter guide.
I also made a few injections for my own project that may be of use to you, which you can find On my Github
With MaxDepthCloneInjector for example, you can supply a dictionary of (property names, max recursion depth) and it will only map values included in the dictionary, and only until the specified level.
Two more pieces of advice:
If you want a bit more freedom with your queries, you should consider using the Query Expression Syntax for some of your more complex needs. Theres also some good information in this answer on SO: How to limit number of related data with Include
If you are planning to run queries including navigational properties like the one in your example: STICK WITH EAGER LOADING. A query like that in Lazy Loading would lead to the N + 1 problem. As a rule of thumb:
Use Lazy Loading if you don't need the entire result set right away, for example if you are developing a application where data requirements naturally expand based on the User's interaction with the application.
Use Eager Loading if you need the entire result-set right away, for example in a Web Api, or a application that needs to work with the complete entity.
Best of luck,
Felix

Related

EF6 Code first - skip binary (or any other) columns during load()

I have the ReportingActivity entity class.
public class ReportingActivity
{
public int ReportingActivityID { get; set; }
public DateTime ReportingActivitySend { get; set; }
public string Remark { get; set; }
public string SendersCSV { get; set; }
public string MailSenderStatus { get; set; }
public long RptGenerationCostMiliseconds { get; set; }
public DateTime RptGeneratedDateTime { get; set; }
public string RptGeneratedByWinUser { get; set; }
public string RptGeneratedOnMachine { get; set; }
public Int64 Run { get; set; }
public byte[] AttachmentFile { get; set; }
public virtual Report Report { get; set; }
public virtual Employee Employee { get; set; }
public virtual ReportingTask ReportingTask { get; set; }
}
I use this code to load data:
ctxDetail = new ReportingContext();
ctxDetail.ReportingActivity
.Where(x => x.Employee.EmployeeID == currentEmployee.EmployeeID)
.Load();
My code gets all the columns in (like SELECT * FROM... )
My question is how to skip the byte[] column, ideally recommend me a way how to improve my lines of code to be able specify exact list of columns.
Normally when dealing with a schema where records have large, seldom accessed details, it is beneficial to split those details off into a separate table as David mentions /w a one-to-one relationship back to the main record. These can be eager or lazy loaded as desired when they are needed.
If changing the schema is not practical then another option when retrieving data from the table is to utilize Projection via Select to populate a view model containing just the fields you need, excluding the larger fields. This will help speed up things like reads for views, however for things like performing updates you will still need to load the entire entity including the large fields to ensure you don't accidentally overwrite/erase data. It is possible to perform updates without loading this data, but it will add a bit of complexity and risk of introducing bugs if mismanaged later.
You can use Table Splitting, and optionally Lazy Loading to have only commonly needed columns loaded.
See
https://learn.microsoft.com/en-us/ef/core/modeling/table-splitting
This is for EF Core but it works the same on EF6 code-first.

EF 5 code first optional one to one mapping accessing foreign key fields on the model

This question is basically a repeat of this question regarding EF4 CTP but specific to EF 5.
I have a POCOs set up such that
public class ClassPrinciple
{
public int ClassPrincipleID { get; set; }
public virtual ClassDependent ClassDependent{ get; set; }
}
and
public class ClassDependent
{
public int ClassDependentID { get; set; }
public virtual ClassPrinciple ClassPrinciple{ get; set; }
}
in my model builder I create the optional one to one mapping like this
modelBuilder.Entity<ClassPrinciple>().HasOptional(p => p.ClassDependent)
.WithOptionalDependent(s => s.ClassPrinciple);
this creates, on the ClassPrinciples table a column called ClassDependent_ClassDependentID . I would like to be able to reference the data in this column through a property on the ClassPrinciple model but I seem unable to do so. The web page I linked to at the top of this question states:
EF in general only supports exposing FK properties on your entities in
one:many relationships (unless the FK is also the PK). This is
somewhat artificial but a side effect of EF not supporting non-PK
unique constraints. We are working on support for unique constraints
for EF at the moment but it won't be there in our first RTM of Code
First.
Sorry not to have a better answer as there really isn't a workaround
at this stage.
Is this still the case or is there a way to resolve this. I have tried fluent api map to column and data annotations in all sorts of combinations without success.
use this code :
public class ClassPrinciple
{
public int ClassPrincipleID { get; set; }
public int ClassDependentId { get; set; }
[ForeignKey("ClassDependentId")]
[InverseProperty("ClassPrinciple")]
public virtual ClassDependent ClassDependent{ get; set; }
}
public class ClassDependent
{
public int ClassDependentID { get; set; }
[InverseProperty("ClassDependent")]
public virtual ClassPrinciple ClassPrinciple{ get; set; }
}

Entity Framework Code first missing foreign key entities in WebAPI project

I'm afraid that if I include my code this post will get too long and too complicated, so I'll try and explain my problem. If however you'd like to see some code illustrating this problem, I'll be happy to add it afterwards.
I have a project, MVC4 project (Website.Web) that used entity framework code first. My entity classes is in a seperate project: Website.Domain
I have a NewsPost class:
public virtual int Id { get; set; }
public virtual string Subject { get; set; }
public virtual string Content { get; set; }
public virtual string ImageName { get; set; }
public virtual DateTime CreateTime { get; set; }
public virtual int CreatedById { get; set; }
public virtual ICollection<Comment> Comments { get; set; }
And my comments class looks like this:
public virtual int Id { get; set; }
public virtual int CreatedById { get; set; }
public virtual DateTime CreatedTime { get; set; }
public virtual string Content { get; set; }
Now in my MVC project, I have entity framework set up with some repository classes and ninject to seperate everything from my controllers. And when I do a "GetAll()" on my newsposts. The NewsPost.Comments will be filled with the comments that's associated with this newspost. It all works perfectly.
Now I got the idea that I'd like to use webapi, so I set up a new MVC basic project. I removed the views folder, and removed the models folder. Then I setup all the repositories here as well along with my entity framework dbcontext class. And enabled migrations on the project to allow me to use entity framework code first in the same fasion as the Website.Web project. I also referenced the same Domain classes as the web project.
Now here comes the problems. I tried doing a GetAll() on the newsposts, but when I inspect the list returned by GetAll(), I see that though it fetches all the news in the database, the COMMENTS are null. I'm pretty sure I have the Website.API set up the same way as my Website.Website.Web - So what am I missing?
I hope I have explained well enough. Again if you need any additional code or I need to clarify some points, I'll happily do this, I just didn't wanna make the question more complicated than it already is with too much code.

MVC4 Stored Procedure returns results, but MVC shows the lookup field values are null

My proc returns all results but MVC is showing the lookups are null.
I have a VIEW where the "grid" for loop looks like this:
#foreach (var item in Model) {
<tr>
<td>#Html.ActionLink(item.Headline, "ViewDetails", "Advisory", new { id=item.AdvisoryId }, "")</td>
<td>#item.AdvisoryStartDate.ToShortDateString()</td>
<td>#item.AdvisoryType.AdvisoryType</td>
<td>#item.AdvisoryProvider.AdvisoryProvider</td>
<td>#item.AdvisoryCategory.AdvisoryCategory</td>
</tr>
}
The last three fields are lookups. I initially populated this with this controller call:
var advisories = db.Advisories.Include(i => i.AdvisoryType)
.Include(i => i.AdvisoryProvider)
.Include(i => i.AdvisoryCategory)
.OrderByDescending(i => i.AdvisoryStartDate);
ViewData["Advisories"] = advisories;
But I've now replaced that with this proc call (because I need to get "advisories" the user hasn't seen yet, so it's more complicated than the above, which is basically "get all"; I also need to query the DB by UserID whereas MVC only has User.Identity.Name):
var results = db.Database.SqlQuery<Advisory>("EXEC [ponzi].[uspUserAdvisories] {0}", User.Identity.Name).ToList();
ViewData["Advisories"] = results;
I know the proc returns all results (when I run it in SQL Server), but in MVC, when I mouse over "results", the AdvisoryCategoryID is filled in but AdvisoryCategory is null. Same with the other two lookups. I assume it's because AdvisoryCategoryID is in my Advisory model, but AdvisoryCategory is from the AdvisoryCategory model.
Here is the relevant part of the Advisory model:
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int AdvisoryId { get; set; }
public virtual int AdvisoryTypeID { get; set; }
public virtual AdvisoryTypes AdvisoryType { get; set; }
public virtual AdvisoryProviders AdvisoryProvider { get; set; }
public virtual int AdvisoryProviderID { get; set; }
public virtual AdvisoryCategories AdvisoryCategory { get; set; }
public virtual int AdvisoryCategoryID { get; set; }
How can I get these three lookup fields to show the data returned by the proc? Thanks.
I think it's about the eager loading of navigation properties (AdvisoryCategory etc), if I understand properly what you're trying to do...
If you're trying to load navigation properties - from your SQL query, SP - I don't think that'd work. That's the Include part of the normal linq statement above - and that generates
an more complex SQL to join in other table's properties etc. It gets
more complicated with more of it.
That doesn't work for code-first and even if it would requires special 'techniques' to get it to match.
See this link here for the confirmation Eager Load from Entity Framework SP
You can try setting it to lazy loading by default (virtual etc.) - and hope it'd load on first access. Also try different loading methods.
Or just explicitely load on demand sort of - see this link and Explicitly Loading.
Eager Load from Entity Framework SP
http://msdn.microsoft.com/en-US/data/jj574232
Eager loading in EntityFramework with DbContext.Database.SqlQuery
I found a solution: ViewModels. Relevant portion of the new AdvisoriesViewModel here (to replace my Advisories model):
public int AdvisoryTypeID { get; set; }
public string AdvisoryType { get; set; }
public string AdvisoryProvider { get; set; }
public int AdvisoryProviderID { get; set; }
public string AdvisoryCategory { get; set; }
public int AdvisoryCategoryID { get; set; }
Now I call my proc with this line, having replaced "Advisory" with "AdvisoryViewModel":
var advisories = db.Database.SqlQuery<AdvisoryViewModel>("EXEC [ponzi].[uspUserAdvisories] {0}", User.Identity.Name).ToList();
Since the returned data set has a model with the same fields, and which aren't relying on another model, it works.

Deletion of entire entity graph, including relationships, using EF Code First

I have classes that are structured like the following:
public class Forecast
{
[Key]
[ForeignKey("Stop")]
public string Abbreviation { get; set; }
public virtual Stop Stop { get; set; }
public virtual List<Direction> Directions { get; set; }
}
public class Direction
{
public int DirectionId { get; set;}
public string Abbreviation { get; set;}
public virtual Forecast Forecast { get; set;}
public virtual List<Transport> Transports { get; set;}
}
public class Transport
{
public int TransportId { get; set; }
public int DirectionId { get; set;}
public virtual Direction Direction { get; set;}
}
public partial class Stop
{
[Key]
public string Abbreviation { get; set; }
public virtual Forecast Forecast { get; set; }
}
I developed these classes and used EF Code First 4.1 to generate the database. CF does appear to properly create all of the primary and foreign key relationships between the classes within the database (MSSQL).
My problem is when I want to delete a Forecast. I thought I do could something like the following:
using (MyContext ctxt = new MyContext())
{
// get a forecast, somehow, not really important
// The one assumption is I'm absolutely sure it's
// Abbreviation key already exists in the database
// and the list of Forecasts.
Forecast f;
ctxt.Forecasts.Remove(f);
}
This deletes the top-level object from the database just fine. However, all of its child objects - all of the directions and transports - remain in the database and become orphaned (their key relationship column gets set to null. I expect that but I DON'T know why they're not just deleted). I have resorted to recursing down the object graph and calling Remove on every object from its appropriate DbSet in ctxt, but that seems like... the wrong way to do it.
What am I missing here?
Why can't I just say
ctxt.Forecasts.Remove(f);
and be done with it?
Edit:
#Ladislav gave me the right answer - I
needed to add [Required] to the
Abbreviation property on Direction.
However, I am still forced to actually
load the child entities for this to
work - doing something as simple as
Direction d = f.Directions[0];
will cause the delete to actually
delete the child entities. I'm well
aware that this is due to lazy
loading. I thought the point of the
FK relationship and ON CASCADE DELETE
was that you wouldn't have to actually
load the entities to delete them?
Again I seem to be missing something simple.
#Eranga is right that this is done by ON DELETE CASCADE setting on relation in the database BUT you are using code first approach and EF creates database for you so the problem here is that your model is not correctly defined because EF didn't create cascading rule for you.
Why? Because of this:
public class Direction
{
public int DirectionId { get; set; }
public string Abbreviation { get; set; }
public virtual Forecast Forecast { get; set; }
public virtual List<Transport> Transports { get; set; }
}
Abbreviation is FK property and it is nullable! So EF looks at your model and it sees that you defined Direction entity which can have Abbreviation set to null and because of that it can exists orphaned. Change it to:
public class Direction
{
public int DirectionId { get; set; }
[Required]
public string Abbreviation { get; set; }
public virtual Forecast Forecast { get; set; }
public virtual List<Transport> Transports { get; set; }
}
and removing Forecast will delete all related Direction instances and Transport instances. Stop is different story because it is parent entity to Forecast so it will never be removed with Forecast.
Edit:
One more point - you don't want to add ON DELETE CASCADE to your relations manually because EF have to know about enabled cascade deletes. EF use this information in case where you have related entities loaded.
If you place the rule manually into the database you must use fluent mapping and tell EF about this rule as well. Once you force cascade delete in fluent api you don't need to make it manually in the database - it will be created automatically during database recreation.
You can easily achieve this by setting ON DELETE CASCADE when you create foreign keys in the Database.