Code First and data loading - c#

I should make a choice between EF code first or model first.
I'm not sure what is the best solution for development efficiency and simplest solution for IoC framework integration.
I have tried to start with code first but I could not make the navigation properties to be used as IQueryable. When I declare navigation properties I use ICollection like:
public class Invoice {
...
public ICollection<LineItem> LineItems { get; set; }
...
}
and
public class LineItem {
...
public Invoice Invoice { get; set; }
...
}
When I load Invoice, my LineItems collection is empty because of lazy load.
But when I load this property from database will I load all LineItems of this Invoice from database before I could use this Collection as IQueryable?
My idea is to use IQueryable to make database loading as minimal as possible to increase performance.
What I'm doing wrong? What is the best practice?
When I use Database first I can use my navigation properties as IQueryable out of the box without writing any proxy classes or wrappers.
Thank you

You need to make all of your entity properties virtual. This allows EF to generate a proxy class for your entity, such that you can query over it.
public class Invoice {
public virtual ICollection<LineItem> LineItems { get; set; }
}

You need Virtual Keyword to make Lazyloading working.

Related

Reading related data from a member method of the model

I have a model that has some related data in navigation properties, like so:
public class Document
{
[Key]
public int DocumentId { get; set; }
public string DocumentName { get; set; }
public virtual ICollection<DocumentBeneficiary> DocumentBeneficiaries { get; set; }
public virtual ICollection<DocumentExecutor> DocumentExecutors { get; set; }
public virtual ICollection<DocumentSuccessor> DocumentSuccessors { get; set; }
}
I understand how to do eager loading of this related data from a controller method, like so:
var doc = context.Documents.Include(x => x.DocumentBeneficiaries)
However, what I want to do is write a member method inside the model that takes the data related to an entity, does some processing, and outputs a string. Something like this:
public class Document
{
...
public string ProcessStuff() {
//use data in navigation properties here like so:
foreach (var d in DocumentBeneficiaries) { ... }
}
}
Is this allowable? I can't seem to find anything about it on google. Will it load the related data lazy vs. eager depending on how I load the entity in the controller prior to calling the method in the model?
I realize that some schools of thought hold that models should have no methods, but others say it's ok to put business logic in the model. If I have to I suppose I can make this a controller method, but this way makes more sense to my design if possible. Sorry if this is a somewhat speculative question, but I can't seem to find any info on this pattern.
Yes it will load the DocumentBeneficiaries when you invoke ProcessStuff method as long as Lazyloading is enabled, thou it may not be a good design (my opinion) to add business logic directly into the model, but as you stated, there are some who like it and some who don't.
If you don't load the child collection ahead of time using Include and Lazyloading is enabled, then you will end up making extra database trips while executing ProcessStuff(). Using Include pre loads the data you need with less number of database round trips. It is always better to make less database trips whenever possible.
If Lazyloading is disabled, you have to use Include before invoking ProcessStuff()

Entity framework 'include' including more than it should

I have a class Program:
[Table("Program")]
public class Program
{
[Key]
public long ProgramId { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public bool IsActive { get; set; }
public virtual ICollection<Activity> Activities { get; set; }
public virtual ICollection<User> Users { get; set; }
public virtual ICollection<TimeEntry> TimeEntries { get; set; }
}
The typical usage pattern is to grab a Program and all it's associated Activities. Much less often need Programs and their related Users or TimeEntries.
So, to return json of all the programs and their related activities. I used .Include:
//...
var programActivities = db.Programs.Include(p => p.Activities);
As expected the json emitted has programs and nested activities. Good.
But, it also returned each Program's associated TimeEntries and Users. Didn't expect that! Other articles i've read indicate that you use one .Include for each of the related objects to be returned, so it would follow that if you don't use an .include for an object, you don't get it.
Is there an additional switch or option i have to use to exclude the other related objects?
Don't declare your navigation properties as virtual or disable Lazy Loading behavior. Lazy loading is enable by default and is achieved by creating instances of derived proxy types and then overriding virtual properties to add the loading hook. So, if you want to work with JSON I recommend you turn off lazy loading:
public class YourContext : DbContext
{
public YourContext()
{
this.Configuration.LazyLoadingEnabled = false;
}
}
Now you can load the related entities you want using the Include extension method as part of a query. This behavior is called Eager Loading.
These links can help you to understand better what I explain in my answer:
Loading Related Entities (read the lazy loading section)
Requirements for Creating POCO Proxies
In Entity Framework, when using the Include statement like so db.Programs.Include(p => p.Activities), you are basically ensuring that the navigation property will be eagerly loaded, rather than lazy loaded. Essentially, this means using one database query to get the data for a Program and its related Activites rather than two.
The problem is, what if lazy loading is enabled for the context, any attempt to get the rest of the navigation properties will result in extra database calls. This is probably what's happening when the JSON serializer tries to serialize your Program objects.
In order to prevent this, you should probably look for a way to exclude the navigation properties from being serialized and so they will never be loaded from the database. For example:
[JsonIgnore]
public virtual ICollection<User> Users { get; set; }
[JsonIgnore]
public virtual ICollection<TimeEntry> TimeEntries { get; set; }
This will allow you to keep lazy loading on, while preventing the loading of the rest of the properties. It all depends on your application design, for example if you need to use lazy loading or if your navigation properties need to be exposed occasionally.

Why is EF eagerly loading all navigation properties with EF Database first?

I use EF Database first, because I like to design my databases in SQL Management Studio, and it's quite frankly very easy to make Visual Studio create all the entities directly from the database, without having to do any code.
How ever, I noticed on SQL Profiler, that EF is eagerly loading all the related entities of an object, whenever I call ToList() on the result collection.
Let's say I have an entity like this:
public class SomeEntity
{
public string Name { get; set; }
public IEnumerable<SomeOtherEntity> ListOfOtherEntites { get; set; }
}
I could then have a query, getting a list of these entities:
public IEnumerable<SomeEntity> GetAllOfTheSomeEntities(Guid customerId){
return dbContex.SomeEntity.Where(x => x.CustomerId == customerId);
}
At a later point in the code, I want to do stuff with this list (for instance in a controller in MVC), and I would call ToList() on the query:
var list = repository.GetAllOfTheSomeEntities(customerId).ToList();
Even though I never use the property "ListOfOtherEntites" on the Entity, it's still being loaded into my server's memory.
I know that I don't have to worry about this in Code first, because I can control the loading with the "virtual/non-virtual" properties and Include - but how am I to go about this in Database first?
I could alter my entities after I load them from the database; but they're just gonna get recreated if I update my model at a later point.
You need to mark your navigation properties as virtual to enable lazy loading in entity framework.
public class SomeEntity
{
public string Name { get; set; }
public virtual IEnumerable<SomeOtherEntity> ListOfOtherEntites { get; set; }
}
Doesn't simply modifying the generated code fix the problem?
If you're experiencing issues with lazy loading then I might recommend turning lazy loading off within your EF context.
public class YourContext : DbContext
{
public YourContext()
{
this.Configuration.LazyLoadingEnabled = false;
}
}
From here you can then include the related entities using LINQ's .Include() method like so:
var posts = context.Blogs.Include(b => b.Posts).ToList();

Entity Framework Code First Relationships; what am I missing?

I'm experimenting with EF5 Code First and I am using the models (show below).
When I look at the database that is created, I am confused because I do not see anything in the Track table that points to the Category table. Category has a FK pointing back to Track but that means that there are going to be duplicates of the categories?
A little background: I am trying to build a model that has tracks and every track can have 1 to N Categories. All of the categories are already defined, that is they are basically a lookup and I plan to create them in the seed method when database is created.
I think I am not understanding something obvious... When I query a track, how will I know what category it contains?
Thx
public class Track : IAuditInfo
{
public Int32 Id { get; set; }
public String Name { get; set; }
public String Description { get; set; }
public String Data { get; set; }
public DateTime CreatedOn { get; set; }
public DateTime ModifiedOn { get; set; }
public ICollection<Category> Categories { get; set; }
public Track()
{
Categories = new List<Category>();
}
}
public class Category
{
public Int32 Id { get; set; }
public Boolean IsVisible { get; set; }
public String DisplayName { get; set; }
}
Your current model is a one-to-many relationship between tracks and categories.
This usually implemented, as you have noted that entity framework does, using a foreign key on the many side (category) to the one side (track).
If I understand you correctly, what you want is a many-to-many relationship. Many tracks can be related to the same category, and a single track can belong to many categories.
To let entity framework understand that you want a many-to-many relationship you can simply add a ICollection property to your category class.
So both your classes should have a collection of the other class.
I.e. tracks have many categories and categories have many tracks.
For more information you can also see: http://msdn.microsoft.com/en-us/data/hh134698.a.nospx
Olav is right, your data model at the moment is not telling Entity Framework that there is a many-to-many relationship in there.
The simplest way to resolve this is to add
public virtual ICollection<Track> Tracks { get; set; }
to your Category class.
However... You may not want to pollute your domain model with artefacts that are not relevant to your domain. More importantly, when you do it this way, it is up to Entity Framework to figure out what to call the binding table. Prior to EF6 this naming is non deterministic (see http://entityframework.codeplex.com/workitem/1677), which may mean that two different machines compiling the same code will decide on different names for that table and cause some interesting migration problems in your production system.
The answer to both problems is to always explicitly manage many-to-many relationships with Fluent Configuration.
In your Data Context class, override the OnModelCreating, something like this:
public class MyDb : DbContext
{
public IDbSet<Track> Tracks { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Track>()
.HasMany(t => t.Categories)
.WithMany()
.Map(c => c.ToTable("CategoriesForTracks"));
}
}
If you do this, you don't need to add a navigation property to your Category class, though you still can (if you do, you should use the overload for WithMany that allows you to specify a property).
Relationships between entities and how to map that to a relational database is inherently hard. For anything other than the simplest parent-child relationships you will want to use the fluent API to make sure you actually get what you want.
Morteza Manavi has a really good blog series describing relationships in EF Code First in exhaustive detail.
NOTE
You should usually make navigation properties virtual. So, you should change your Category class like this:
public virtual ICollection<Category> Categories { get; set; }
In theory, not making it virtual should just cause eager loading rather than lazy loading to happen. In practice I have always found lots of subtle bugs appearing when my navigation properties are not virtual.

Dealing with lazy loading outside of an ISession?

I'm using NHibernate to do database access in my application. My ISessions have no persistance, and I'm happy with this as it makes it easier for me to separate my application into different layers. The only difficulty is dealing with lazy loading in a nice way.
I have a model class that looks like this:
public class User {
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual Country CountryOfBirth { get; set }
public virtual Country CountryOfResidence {get; set; }
}
At the moment, I have CountryOfBirth and CountryOfResidence set to fetch="join". However, as the list of countries in my database is mostly static, I want to cache these values. I changed the CountryOfBirth property to look like this:
Country countryOfBirth;
public virtual Country CountryOfBirth{
get
{
if (country is INHibernateProxy)
countryOfBirth = CountryRepository.GetById(countryOfBirth.Id);
return countryOfBirth;
}
set { countryOfBirth = value; }
}
However, it requires my Model class to know that it is being used by NHibernate, which breaks encapsulation.
Is there a better way to achieve this? For instance, is there a way to get NHibernate to automatically go through my Repository classes if it tries to load a proxy and the session has expired?
Or should I use a different method?
If you want to add cache capabilities then look at NHibernate L2 Cache. Check this tutorial http://nhforge.org/blogs/nhibernate/archive/2009/02/09/quickly-setting-up-and-using-nhibernate-s-second-level-cache.aspx and search for this topic. By using cache you don't polute your model with any NH proxies, repositories, etc.

Categories

Resources