首页 > 解决方案 > EF Core 2+ - 使用通用存储库预加载

问题描述

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 的IncludeandThenInclude与谓词一起使用(这只是我的愿望),如下所示:

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);
        }

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

标签: entity-frameworkentity-framework-coreef-core-2.0ef-core-2.2

解决方案


即使这无需在多个存储库参数中拆分逻辑即可工作,您真的更愿意编写

        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>. 它与存储库是否“通用”无关,与要如何编写查询有关。查询由存储库的消费者编写。不在存储库中或在存储库中(除非您希望跨消费者重用查询)。

这种设计的疯狂之处在于它允许 repo 的消费者指定查询。它只是迫使他们通过笨重的定制 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();
}

你不需要 Expression 那个,因为Include没有延迟。它是一个返回 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))                                                           ;
}

推荐阅读