EF Core 2+ - 使用通用存储库进行预加载

EF Core 2+ - Eager loading using generic repository

我的BaseRespository是这样的:

public abstract class BaseRespository<TEntity, TContext> : IBaseRepository<TEntity> 
    where TEntity : class
    where TContext : DbContext
{
    private readonly TContext _context;

    protected BaseRespository(TContext context)
    {
        _context = context;
    }

    public async Task<TEntity> GetByCondition(Expression<Func<TEntity, bool>> predicate)
    {
        return await _context.Set<TEntity>().Where(predicate).FirstOrDefaultAsync();
    }      
}

然后我像这样访问 GetByCondition 方法:

public async Task<Tips> GetTipBySlug(string slug)
{
    Expression<Func<Tips, bool>> predicate = (t) => t.Slug == slug &&
                                                    t.Status == (int)LU_Status.active &&
                                                    t.User.Status == (int)LU_Status.active;

    return await _tipRepository.GetByCondition(predicate);
}

我想将 EF Core 的 IncludeThenInclude 与谓词一起使用(这正是我的愿望),如下所示:

public async Task<Tips> GetTipBySlug(string slug)
        {
            Expression<Func<Tips, bool>> whereExpr = (t) => t.Include(t=>t.User).ThenInclude(u=>u.UserImages)
                                                            t.Slug == slug &&
                                                            t.Status == (int)LU_Status.active &&
                                                            t.User.Status == (int)LU_Status.active;

            return await _tipRepository.GetByCondition(whereExpr);
        }

如何使用 EF CORE 2 及更高版本将所需的 t.Include(t=>t.User).ThenInclude(u=>u.UserImages) 添加到谓词?

即使这不需要将您的逻辑拆分到多个存储库参数中也能奏效,您真的更愿意写

        Expression<Func<Tips, bool>> whereExpr = (t) => t.Include(t=>t.User).ThenInclude(u=>u.UserImages)
                                                        t.Slug == slug &&
                                                        t.Status == (int)LU_Status.active &&
                                                        t.User.Status == (int)LU_Status.active;

        return await _tipRepository.GetByCondition(whereExpr);

关于 EF 的设计使用方式:

    var q = _tipRepository.Set<Tips>() 
                          .Include(t=>t.User)
                          .ThenInclude(u=>u.UserImages)
                          .Where(t => t.Status == (int)LU_Status.active)
                          .Where(t => t.User.Status == (int)LU_Status.active);

    return await q.FirstOrDefaultAsync();

为什么要创建 Expression<Func<Tips,bool>> 而不是 IQueryabe<T>。它 nothing 与存储库是否 "generic" 相关,everything 与您想如何编写查询有关。查询由存储库的 消费者 编写。不在存储库中或存储库中(除非您希望跨消费者重复使用查询)。

这种设计的疯狂之处在于它允许存储库的消费者指定查询。它只是迫使他们通过笨重的定制 API.

来做到这一点

你可以这样写:

public async Task<TEntity> GetByCondition<TEntity>(Expression<Func<TEntity, bool>> predicate, Func<DbSet<TEntity>, IQueryable<TEntity>> baseQuery = null) where TEntity : class
{
    IQueryable<TEntity> q = Set<TEntity>();
    if (baseQuery != null)
    {
       q = baseQuery(Set<TEntity>());
    }
    return await q.Where(predicate).FirstOrDefaultAsync();
}

你不需要那个表达式,因为 Include 没有延迟。它是returns一个IIncludableQueryable的函数,所以它是一个查询转换函数。

然后:

public async Task<Tips> GetTipBySlug(string slug)
{
    Expression<Func<Tips, bool>> whereExpr = (t) => t.Slug == slug &&
                                                    t.Status ==1 &&
                                                    t.User.Status == 1;

    return await GetByCondition(whereExpr, s => s.Include(t => t.User).ThenInclude(u => u.UserImages))                                                           ;
}