问题
Previously (when using .net 4.5.2 and EF 6). I have had a generic Get
method that accepted a number of includes as follows;
public abstract class DataContext : IdentityDbContext<ApplicationUser, ApplicationRole, int>, IDataContext
{
public DataContext(DbContextOptions options)
: base(options)
{
}
// reduced for brevity
public T Get<T>(int id, params Expression<Func<T, object>>[] includes) where T : class, IEntity
{
return this.Set<T>().Include(includes).FirstOrDefault(x => x.Id == id);
}
I would then call for example;
context.Get<Job>(id,
x => x.Equipment,
x => x.Equipment.Select(y => y.Type));
To include the Job.Equipment
and also the Job.Equipment.Type
.
However, when I have ported this over to asp.net core 2. I have tried the same generic approach, but if I try to include a sub-entity I get the following error;
The property expression 'x => {from Equipment y in x.Equipment select [y].Type}' is not valid. The expression should represent a property access: 't => t.MyProperty'. For more information on including related data, see http://go.microsoft.com/fwlink/?LinkID=746393.
Can anyone suggest how I can work around this to include sub entities in my Generic Get<T>
method with Entity Framework Core 2?
Update
From looking at the documents there is an additional include method
include(string navigationPropertyPath)
I added the following method;
public T Get<T>(int id, string[] includes) where T : class, IEntity
{
var result = this.Set<T>().AsQueryable();
foreach(var include in includes)
{
result = result.Include(include);
}
return result.FirstOrDefault(x => x.Id == id);
}
Which does work, although I am not convinced on the efficiency here?
回答1:
EF Core Include
/ ThenInclude
pattern cannot be represented by Expression<Func<T, object>>[]
like in EF6.
Looking at the source code of one of the EF Core extensions - Microsoft.EntityFrameworkCore.UnitOfWork, which claims to be
A plugin for Microsoft.EntityFrameworkCore to support repository, unit of work patterns, and multiple database with distributed transaction supported.
looks like the intended pattern for includes should be based on Func<IQueryable<T>, IIncludableQueryable<T, object>>
:
public T Get<T>(int id, Func<IQueryable<T>, IIncludableQueryable<T, object>> include = null) where T : class, IEntity
{
var result = this.Set<T>().AsQueryable();
if (include != null)
result = include(result);
return result.FirstOrDefault(x => x.Id == id);
}
The drawback is that it adds EF Core dependency on the caller and requires using Microsoft.EntityFrameworkCore;
. Which in your case is not essential since you are extending the DbContext
.
The usage with your example would be:
context.Get<Job>(id, q => q
.Include(x => x.Equipment)
.ThenInclude(y => y.Type));
回答2:
You can do something like this:
public abstract class DataContext : IdentityDbContext<ApplicationUser, ApplicationRole, int>, IDataContext
{
public DataContext(DbContextOptions options)
: base(options)
{
}
// reduced for brevity
public T Get<T>(int id, Func<IQueryable<T>, IIncludableQueryable<T, object>> includes = null) where T : class, IEntity
{
IQueryable<T> queryable = this.Set<T>();
if (includes != null)
{
queryable = includes(queryable);
}
return queryable.FirstOrDefault(x => x.Id == id);
}
}
context.Get<Job>(id, includes: source => source.Include(x => x.Equipment).ThenInclude(x => x.Type));
来源:https://stackoverflow.com/questions/50137839/theninclude-for-sub-entity-in-entity-framework-core-2