r3plica r3plica - 2 months ago 26
C# Question

EF Eager loading include multiple

I have this Repository:

public class Repository<T> : IRepository<T> where T : class
{
private readonly DbContext context;
private readonly DbSet<T> dbEntitySet;

public Repository(DbContext context)
{
if (context == null)
throw new ArgumentNullException("context");

this.context = context;
this.dbEntitySet = context.Set<T>();
}

public IEnumerable<T> GetAll()
{
return this.dbEntitySet;
}

public IEnumerable<T> GetAll(string include)
{
return this.dbEntitySet.Include(include);
}

public IEnumerable<T> GetAll(string[] includes)
{
IQueryable<T> query = context.Set<T>();
foreach (var include in includes)
query.Include(include);

return query;
}

public void Create(T model)
{
this.dbEntitySet.Add(model);
}

public void Update(T model)
{
this.context.Entry<T>(model).State = EntityState.Modified;
}

public void Remove(T model)
{
this.context.Entry<T>(model).State = EntityState.Deleted;
}

public void Dispose()
{
this.context.Dispose();
}
}


And I have a service which looks like this:

public class Service<T> where T : class
{
private readonly IRepository<T> repository;

protected IRepository<T> Repository
{
get { return this.repository; }
}

internal Service(IUnitOfWork unitOfWork)
{
this.repository = unitOfWork.GetRepository<T>();
}
}


My pages service looks like this (simplified):

public class PageService : Service<Page>
{

// ...

public IList<Page> GetPublished()
{
return this.Repository.GetAll(new string[] { "ForbiddenUsers", "ForbiddenGroups" }).Where(model => model.Published).ToList();
}

// ...

}


and just for clarity my Page looks like this:

public enum PageType
{
Root,
System,
Page,
Link,
Group
}

public partial class Page
{
public int Id { get; set; }
public System.DateTime DateCreated { get; set; }
public string CreatedById { get; set; }
public Nullable<System.DateTime> DateModified { get; set; }
public string ModifiedById { get; set; }
public string CompanyId { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public string FileName { get; set; }

public string Path { get; set; }
public string ViewData { get; set; }
public string ViewTitle { get; set; }

public string Link { get; set; }
public bool Published { get; set; }
public PageType Type { get; set; }
public int Order { get; set; }
public string Lineage { get; set; }
public Nullable<int> ParentId { get; set; }
public bool Restricted { get; set; }
public bool Deleted { get; set; }

public Company Company { get; set; }
public User CreatedBy { get; set; }
public User ModifiedBy { get; set; }
public Page Parent { get; set; }
public MenuPage MenuPage { get; set; }
public ICollection<Page> Pages { get; set; }
public ICollection<User> ForbiddenUsers { get; set; }
public ICollection<Group> ForbiddenGroups { get; set; }
}


When I run this code, ForbiddenUsers and ForbiddenGroups are always null.
If I examine the call stack and I can see that the query that is generated looks like this:

SELECT
[Extent1].[Id] AS [Id],
[Extent1].[DateCreated] AS [DateCreated],
[Extent1].[CreatedById] AS [CreatedById],
[Extent1].[DateModified] AS [DateModified],
[Extent1].[ModifiedById] AS [ModifiedById],
[Extent1].[CompanyId] AS [CompanyId],
[Extent1].[Name] AS [Name],
[Extent1].[Description] AS [Description],
[Extent1].[FileName] AS [FileName],
[Extent1].[Path] AS [Path],
[Extent1].[ViewData] AS [ViewData],
[Extent1].[ViewTitle] AS [ViewTitle],
[Extent1].[Link] AS [Link],
[Extent1].[Published] AS [Published],
[Extent1].[Type] AS [Type],
[Extent1].[Order] AS [Order],
[Extent1].[Lineage] AS [Lineage],
[Extent1].[ParentId] AS [ParentId],
[Extent1].[Restricted] AS [Restricted],
[Extent1].[Deleted] AS [Deleted]
FROM [dbo].[Pages] AS [Extent1]


As you can see, that is completely ignoring my includes.

If I change my service method to this:

public IList<Page> GetPublished()
{
return this.Repository.GetAll("ForbiddenUsers").Where(model => model.Published).ToList();
}


and run my code, ForbiddenUsers is now populated and the call stack shows the correct generated query (too big to paste in here).

I need to allow multiple includes but I can not figure out why they are not working....

Any help would be greatly appreciated....

Answer

You should assign included query back to your query variable, because QueryableExtensions.Include creates new DbQuery<T> instead of modifying existing one. Also I suggest you to use params for included paths:

public IEnumerable<T> GetAll(params string[] includes)
{
    IQueryable<T> query = context.Set<T>();
    foreach (var include in includes)
        query = query.Include(include);

    return query;
}

That will allow you pass all included paths without explicit array creation:

public IList<Page> GetPublished()
{
    return Repository.GetAll("ForbiddenUsers", "ForbiddenGroups")
                     .Where(model => model.Published)
                     .ToList();
}
Comments