Reputation: 759
We are using Entity Framework 4.2 with model first approach and DbContext code generation.
Let's say we have following data model in entity framework:
class Person
{
public string Name { get; set; }
public Address Address { get; set; }
}
class Address
{
public string City; { get; set; }
}
The scenario is following:
In phase 3, person is loaded in following way:
using (DatabaseContext context = new DatabaseContext())
{
Person person = from p in context.Persons.FirstOrDefault();
return person;
}
Ok, I am aware that (in theory) I could force loading of the Address object in two ways: 1) Use DbSet.Include, e.g:
context.Persons.Include("Address").FirstOrDefault();
2) Access Person.Address while database context is still alive as this will force address to be loaded
Person person = context.Persons.FirstOrDefault();
Address address = person.Address;
return person;
Of course the first one is the preferred solution because it is not so ugly as the second one (just accessing the property to force loading of data and then discarding the result IS ugly). Also, in case of a collection (e.g. list of persons), I would have to loop through the collection and access the Address separately for each person. The problem with first solution is that only DbSet has Include method and all other collections returned from queries have not. So, let's say I have following database structure
class Resource {}
class Person : Resource { public Address Address { get; set; } }
class Appointment { public IList<Resource> Resources { get; set; } }
and I want to load all specific appointments and include address in every resource that is person, I am in trouble (or at least I could not figure a way to write query for it). This is because context.Appointments.Resources is not of type DbSet but a ICollection which does not have Include method. (Ok, maybe in this case I could write a query somehow starting from context.Persons instead of context.Appointments so that I could use Include but there are many scenarios where this is not possible)
So basically the questions are:
Upvotes: 4
Views: 3807
Reputation: 15130
You can set an implementation of a include strategy at startup of your application or test.
First define an Extension method: Include that defines the actual including mechanism (as default by doing nothing)
/// <summary>
/// Extension methods specifically for include since this is essential for DomainContext but not part of IQueryable by default
/// </summary>
public static class QueryableIncludeExtensions
{
public static IIncluder Includer = new NullIncluder();
public static IQueryable<T> Include<T, TProperty>(this IQueryable<T> source, Expression<Func<T, TProperty>> path)
where T : class
{
return Includer.Include(source, path);
}
public interface IIncluder
{
IQueryable<T> Include<T, TProperty>(IQueryable<T> source, Expression<Func<T, TProperty>> path) where T : class;
}
internal class NullIncluder : IIncluder
{
public IQueryable<T> Include<T, TProperty>(IQueryable<T> source, Expression<Func<T, TProperty>> path)
where T : class
{
return source;
}
}
}
Then create an EF specific includer implementation like:
internal class DbIncluder : QueryableIncludeExtensions.IIncluder
{
public IQueryable<T> Include<T, TProperty>(IQueryable<T> source, Expression<Func<T, TProperty>> path)
where T : class
{
return DbExtensions.Include(source, path);
}
}
Finally, hook the DbIncluder implementation up in the project you need it for, in my case i did it like:
public class DomainContext : DbContext, IDomainContext
{
static DomainContext()
{
// register the DbIncluder for making sure the right call to include is made (standard is null)
QueryableIncludeExtensions.Includer = new DbIncluder();
}
Now IQueryable always has the extension method: Include available. You can extend this to IEnumerable if you want. The actual implementation is simply set by QueryableIncludeExtensions.Includer
Upvotes: 2