C#实体框架核心条件投影

C#实体框架核心条件投影,c#,lambda,entity-framework-core,projection,C#,Lambda,Entity Framework Core,Projection,我目前使用的是实体框架核心,它工作得非常好。然而,我试图在应用程序中优化的一件事是在查询时从数据库返回计算数据。我首先使用代码,其中每个模型直接映射到中的单个表 以下是我的持久性模型的简化示例: 公共类用户 { 公共int Id{get;set;} 公共字符串名称{get;set;} 公共ICollection角色{get;set;} } 公共类用户角色 { 公共int Id{get;set;} public int UserId{get;set;} 公共用户{get;set;} 公共字符串角色

我目前使用的是实体框架核心,它工作得非常好。然而,我试图在应用程序中优化的一件事是在查询时从数据库返回计算数据。我首先使用代码,其中每个模型直接映射到中的单个表

以下是我的持久性模型的简化示例:

公共类用户
{
公共int Id{get;set;}
公共字符串名称{get;set;}
公共ICollection角色{get;set;}
}
公共类用户角色
{
公共int Id{get;set;}
public int UserId{get;set;}
公共用户{get;set;}
公共字符串角色{get;set;}
}
我目前使用的是规范模式的一个变体,它使我能够在执行之前对查询运行一个可变数量的
.Include/。然后Include
。但是,我希望能够有条件地启用投影的特定部分

例如,以下是我的域模型的显示方式:

公共类UserImpl
{
公共用户{get;set;}
公共int?RoleCount{get;set;}
公共静态表达式投影(UserImplParams opts){
返回u=>newuserimpl
{
用户=u,
rolecoount=opts!=null&&opts.includerolecoount?u.Roles.Count():默认值(int?)
};
}
}
公共类UserImplParams
{
公共bool includeLolecount{get;set;}
}
我想实现的是一种类似的方法:

var opts=new UserImplParams
{
IncludeUserRole=true
};
wait_databaseContext.Users.Select(UserImpl.Projection(opts)).toListSync();
我希望EF Core能够看到:

u=>newuserimpl
{
用户=u,
rolecoount=u.Roles.Count()
};

u=>newuserimpl
{
用户=u,
RoleCount=默认值(整数?)
};

可能吗?这主要是因为此表达式可能包含多个投影属性,甚至是嵌套属性。每次只为一小部分数据向数据库发送整个代码似乎效率低下。

[EDIT]我在我的网站上发布了此代码的更新版本 它基本相同,但增加了对更多场景的支持


我想这样做的部分原因是因为过早的优化。我确信,在90%的情况下,当1=1或1=0(表示真或假)时,发送大规模SQL将得到正确的优化。然而,事实上,案例陈述并不总是短路

不用再多说了,下面是我如何实现这一目标的解决方案

主要功能在此新类中:

公共类ProjectionExpressionVisitor:ExpressionVisitor
{
内部表达式优化(表达式)
{
回访(表达)作为表达;
}
受保护的重写表达式访问条件(条件表达式节点)
{
var测试=还原表达式(node.test);
//条件现在是常量,我们可以替换分支
if(测试为恒压测试节点)
{
var value=(动态)testNode.value;
返回值?ReduceExpression(node.IfTrue):ReduceExpression(node.IfFalse);
}
//如果它不是一个条件,我们遵循默认行为
返回base.VisitConditional(节点);
}
公共表达式ReduceExpression(表达式节点)
{
if(节点为恒压)
{
//常量表示最小的项,因此我们可以直接返回它
返回节点;
}
else if(节点为MemberExpression memberNode)
{
返回reduceMemberPression(memberNode);
}
else if(节点为BinaryExpression binaryNode)
{
返回ReduceBinaryExpression(binaryNode);
}
//这不是要减少的受支持表达式类型,请退回到默认值
返回节点;
}
公共表达式ReduceMemberExpression(MemberExpression节点)
{
如果(
node.Expression.NodeType==ExpressionType.Constant||
node.Expression.NodeType==ExpressionType.MemberAccess
)
{
var objectMember=Expression.Convert(节点,typeof(对象));
var getterLambda=Expression.Lambda(objectMember);
var getter=getterLambda.Compile();
var值=getter();
返回表达式。常量(值);
}
返回节点;
}
公共表达式ReduceBinaryExpression(BinaryExpression节点)
{
var left=ReduceExpression(node.left);
var right=ReduceExpression(node.right);
var leftConst=左为恒压;
var rightConst=右侧为恒定压力;
//特殊优化
var optimized=optimizeboleanbinaryExpression(node.NodeType、leftConst、righconst);
if(优化!=null)返回表达式常量(优化);
if(leftConst!=null&&righconst!=null)
{
var leftValue=(动态)leftConst.Value;
var rightValue=(动态)rightConst.Value;
开关(node.NodeType)
{
大小写表达式类型。添加:
返回表达式.常量(leftValue+rightValue);
大小写表达式类型。除法:
返回表达式.Constant(leftValue/rightValue);
大小写表达式类型。模:
返回表达式.常量(leftValue%rightValue);
大小写表达式类型。乘法:
返回表达式.Constant(leftValue*rightValue);
大小写表达式类型。电源:
返回表达式.Constant(leftValue^rightValue);
大小写表达式类型。减法:
返回表达式.Constant(leftValue-rightValue);
            .GroupBy(x => new {x.Brand})
            .Select(x => new DisputeReportListModel
            {
                Amount = x.Sum(y => y.Amount),
                Scheme = _isMastercard(x.Key.Brand) ? "MASTERCARD" : "VISA",
            }).AsEnumerable()
            .GroupBy(x => new {x.Scheme})
            .Select(x => new DisputeReportListModel
            {
                Amount = x.Sum(y => y.Amount),
                Scheme = x.Key.Scheme
            })
            .ToList();