EF Core 包括其他实体(通用存储库模式)

EF Core Including Other Entities (Generic Repository pattern)

我在我的应用程序 c# 中使用通用存储库/UoW patter 我使用的是 EF6,然后我转到了 EF 核心。 我的应用程序运行良好,但由于某些原因我的包含不起作用,并且出现异常

接口:

TEntity GetFirstOrDefault(
        Expression<Func<TEntity, bool>> filter = null,
        params Expression<Func<TEntity, object>>[] includes);

实施(EF 核心):

    public virtual TEntity GetFirstOrDefault(Expression<Func<TEntity, bool>> filter = null, 
                            params Expression<Func<TEntity, object>>[] includes)
    {
        IQueryable<TEntity> query = dbSet;

        query = includes.Aggregate(query, (current, item) => EvaluateInclude(current, item));
        return query.FirstOrDefault(filter);
    }

在 Entity Framework EF6 中,它是:

        foreach (Expression<Func<TEntity, object>> include in includes)
            query = query.Include(include);

EvaluateInclude 函数是:

private IQueryable<TEntity> EvaluateInclude(IQueryable<TEntity> current, Expression<Func<TEntity, object>> item)
    {
        if (item.Body is MethodCallExpression)
        {
            var arguments = ((MethodCallExpression)item.Body).Arguments;
            if (arguments.Count > 1)
            {
                var navigationPath = string.Empty;
                for (var i = 0; i < arguments.Count; i++)
                {
                    var arg = arguments[i];
                    var path = arg.ToString().Substring(arg.ToString().IndexOf('.') + 1);

                    navigationPath += (i > 0 ? "." : string.Empty) + path;
                }
                return current.Include(navigationPath);
            }
        }

        return current.Include(item);
    }

当我像这样调用 GetFirstOrDefault 函数时,它起作用了:

internal Domain.Entities.Project GetProject(int projectId)
    {
        Expression<Func<Domain.Entities.Project, bool>> funcWhere = j => (!j.IsDisabled && j.ProjectId == projectId);
      return  UnitOfWork.Repository<Domain.Entities.Project>().GetFirstOrDefault(funcWhere,
            p => p.StatusProject,
            p => p.ProjectRoles.Select(t => t.Employee),
            //p => p.ProjectTeams.Select(t => t.Team.TeamEmployees.Select(e => e.Employee)),
            );

    }

但是当我取消注释额外的 include 时,它​​失败了:

 internal Domain.Entities.Project GetProject(int projectId)
    {
        Expression<Func<Domain.Entities.Project, bool>> funcWhere = j => (!j.IsDisabled && j.ProjectId == projectId);
      return  UnitOfWork.Repository<Domain.Entities.Project>().GetFirstOrDefault(funcWhere,
            p => p.StatusProject,
            p => p.ProjectRoles.Select(t => t.Employee),
            p => p.ProjectTeams.Select(t => t.Team.TeamEmployees.Select(e => e.Employee)),
            );

    }

System.InvalidOperationExceptionInvalid include path: 'Project.ProjectTeams.Team.TeamEmployees.Select(e => e.Employee)' - couldn't find navigation for: 'Select(e => e'

已解决:

关注这个答案link 我添加了解析我的 lambda 表达式的代码 includes :

    // This method is a slight modification of EF6 source code
    private bool TryParsePath(Expression expression, out string path)
    {
        path = null;
        var withoutConvert = RemoveConvert(expression);
        var memberExpression = withoutConvert as MemberExpression;
        var callExpression = withoutConvert as MethodCallExpression;

        if (memberExpression != null)
        {
            var thisPart = memberExpression.Member.Name;
            string parentPart;
            if (!TryParsePath(memberExpression.Expression, out parentPart))
            {
                return false;
            }
            path = parentPart == null ? thisPart : (parentPart + "." + thisPart);
        }
        else if (callExpression != null)
        {
            if (callExpression.Method.Name == "Select"
                && callExpression.Arguments.Count == 2)
            {
                string parentPart;
                if (!TryParsePath(callExpression.Arguments[0], out parentPart))
                {
                    return false;
                }
                if (parentPart != null)
                {
                    var subExpression = callExpression.Arguments[1] as LambdaExpression;
                    if (subExpression != null)
                    {
                        string thisPart;
                        if (!TryParsePath(subExpression.Body, out thisPart))
                        {
                            return false;
                        }
                        if (thisPart != null)
                        {
                            path = parentPart + "." + thisPart;
                            return true;
                        }
                    }
                }
            }
            else if (callExpression.Method.Name == "Where")
            {
                throw new NotSupportedException("Filtering an Include expression is not supported");
            }
            else if (callExpression.Method.Name == "OrderBy" || callExpression.Method.Name == "OrderByDescending")
            {
                throw new NotSupportedException("Ordering an Include expression is not supported");
            }
            return false;
        }

        return true;
    }

    // Removes boxing
    private Expression RemoveConvert(Expression expression)
    {
        while (expression.NodeType == ExpressionType.Convert
               || expression.NodeType == ExpressionType.ConvertChecked)
        {
            expression = ((UnaryExpression)expression).Operand;
        }

        return expression;
    }

    #endregion

然后将我的 EvaluateInclude 函数更改为:

  private IQueryable<TEntity> EvaluateInclude(IQueryable<TEntity> current, Expression<Func<TEntity, object>> item)
    {
        if (item.Body is MethodCallExpression)
        {
            string path;
            TryParsePath(item.Body, out path);
            return current.Include(path);

        }

        return current.Include(item);
    }

而且有效