Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/codeigniter/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 让EF Linq Select语句选择常量或函数_C#_Linq_Entity Framework - Fatal编程技术网

C# 让EF Linq Select语句选择常量或函数

C# 让EF Linq Select语句选择常量或函数,c#,linq,entity-framework,C#,Linq,Entity Framework,我有一个Select语句,当前的格式如下 dbEntity .GroupBy(x => x.date) .Select(groupedDate => new { Calculation1 = doCalculation1 ? x.Sum(groupedDate.Column1) : 0), Calculation2 = doCalculation2 ? x.Count(

我有一个Select语句,当前的格式如下

dbEntity
.GroupBy(x => x.date)
.Select(groupedDate => new {
                             Calculation1 = doCalculation1 ? x.Sum(groupedDate.Column1) : 0),
                             Calculation2 = doCalculation2 ? x.Count(groupedDate) : 0)
在查询中,doccalculation1和doccalculation2是较早设置的布尔值。这将在生成的Sql中创建一个case语句,如

DECLARE @p1 int = 1
DECLARE @p2 int = 0
DECLARE @p3 int = 1
DECLARE @p4 int = 0
SELECT (Case When @p1 = 1 THEN Sum(dbEntity.Column1)
     Else @p2
     End) as Calculation1,
     (Case When @p3 = 1 THEN Count(*)
     Else @p4
     End) as Calculation2
我希望生成的sql在doCalculation1为true时是这样的

SELECT SUM(Column1) as Calculation1, Count(*)  as Calculation2
当doCalculation2是假的时候

SELECT 0 as Calculation1, Count(*) as Calculation2
有没有办法强制通过EF执行这样的查询

编辑:

生成此sql

-- Region Parameters
DECLARE @p0 VarChar(1000) = 'E'
DECLARE @p1 Int = 0
DECLARE @p2 Decimal(5,4) = 0
DECLARE @p3 Int = 0
DECLARE @p4 Int = 1
DECLARE @p5 Decimal(1,0) = 0
-- EndRegion
SELECT [t1].[ReportDate], 
    (CASE 
        WHEN @p1 = 1 THEN (
            SELECT COUNT(*)
            FROM [dbEntity] AS [t2]
            WHERE ([t2].[Flow] > @p2) AND ([t1].[ReportDate] = [t2].[ReportDate]) AND ([t2].[FundType] = @p0)
        )
        ELSE @p3
     END) AS [CountInFlows], 
    (CASE 
        WHEN @p4 = 1 THEN CONVERT(Decimal(33,4),[t1].[value])
        ELSE CONVERT(Decimal(33,4),@p5)
     END) AS [NetAssetEnd]
FROM (
    SELECT SUM([t0].[AssetsEnd]) AS [value], [t0].[ReportDate]
    FROM [dbEntity] AS [t0]
    WHERE [t0].[FundType] = @p0
    GROUP BY [t0].[ReportDate]
    ) AS [t1]
它有许多索引扫描,执行计划中有一个假脱机和一个连接。在测试集上运行平均大约需要20秒,而生产集将要大得多

我希望它能像sql一样以同样的速度运行

select reportdate, 1, sum(AssetsEnd)
from vwDailyFundFlowDetail
where fundtype = 'E'
group by reportdate
它平均运行时间约为12秒,并将大部分查询绑定到执行计划中的单个索引查找中。实际的sql输出是什么并不重要,但是case语句的性能似乎要差得多

至于我为什么要这样做,我需要生成一个动态select语句,就像我在中所问的那样。用户可以选择一个或多个要执行的计算集,在收到请求之前,我不知道选择了什么。这些请求非常昂贵,因此我们不希望运行它们,除非它们是必需的。我正在根据用户请求设置doCalculation布尔值


该查询应该替换一些代码,这些代码从存储为字符串的硬编码sql查询中插入或删除字符,然后执行该查询。这运行得相当快,但维护起来却是一场噩梦。从技术上讲,可以通过Select查询传递表达式,它检查三元运算符左侧的常量值,并用适当的子表达式替换三元表达式

例如:

public class Simplifier : ExpressionVisitor
{
    public static Expression<T> Simplify<T>(Expression<T> expr)
    {
        return (Expression<T>) new Simplifier().Visit(expr);
    }

    protected override Expression VisitConditional(ConditionalExpression node)
    {
        var test = Visit(node.Test);
        var ifTrue = Visit(node.IfTrue);
        var ifFalse = Visit(node.IfFalse);

        var testConst = test as ConstantExpression;
        if(testConst != null)
        {
            var value = (bool) testConst.Value;
            return value ? ifTrue : ifFalse;
        }

        return Expression.Condition(test, ifTrue, ifFalse);
    }

    protected override Expression VisitMember(MemberExpression node)
    {
        // Closed-over variables are represented as field accesses to fields on a constant object.
        var field = (node.Member as FieldInfo);
        var closure = (node.Expression as ConstantExpression);
        if(closure != null)
        {
            var value = field.GetValue(closure.Value);
            return VisitConstant(Expression.Constant(value));
        }
        return base.VisitMember(node);
    }
}
公共类简化器:ExpressionVisitor
{
公共静态表达式简化(表达式表达式表达式)
{
return(表达式)new Simplifier().Visit(expr);
}
受保护的重写表达式访问条件(条件表达式节点)
{
var测试=访问(节点测试);
var-ifTrue=访问(node.ifTrue);
var ifFalse=访问(node.ifFalse);
var testConst=测试为恒定压力;
if(testConst!=null)
{
var值=(bool)testConst.value;
返回值?ifTrue:ifFalse;
}
返回表达式.Condition(test、ifTrue、ifFalse);
}
受保护的重写表达式VisitMember(MemberExpression节点)
{
//闭合变量表示为对常量对象上字段的字段访问。
变量字段=(node.Member作为FieldInfo);
var closure=(node.Expression为ConstantExpression);
if(闭包!=null)
{
var值=field.GetValue(closure.value);
返回VisitConstant(表达式.常量(值));
}
返回base.VisitMember(节点);
}
}
用法示例:

void Main()
{
    var b = true;
    Expression<Func<int, object>> expr = i => b ? i.ToString() : "N/A";
    Console.WriteLine(expr.ToString()); // i => IIF(value(UserQuery+<>c__DisplayClass0).b, i.ToString(), "N/A")
    Console.WriteLine(Simplifier.Simplify(expr).ToString()); // i => i.ToString()
    b = false;
    Console.WriteLine(Simplifier.Simplify(expr).ToString()); // i => "N/A"
}
void Main()
{
var b=真;
表达式expr=i=>b?i.ToString():“N/A”;
Console.WriteLine(expr.ToString());//i=>IIF(值(UserQuery+c_udisplayClass0.b,i.ToString(),“N/A”)
Console.WriteLine(Simplifier.Simplify(expr.ToString());//i=>i.ToString()
b=假;
Console.WriteLine(Simplifier.Simplify(expr.ToString());//i=>“不适用”
}
因此,您可以在代码中使用以下内容:

Expression<Func<IGrouping<DateTime, MyEntity>>, ClassYouWantToReturn> select = 
    groupedDate => new {
        Calculation1 = doCalculation1 ? x.Sum(groupedDate.Column1) : 0),
        Calculation2 = doCalculation2 ? x.Count(groupedDate) : 0
    };
var q = dbEntity
    .GroupBy(x => x.date)
    .Select(Simplifier.Simplify(select))
表达式选择=
groupedDate=>new{
Calculation1=doccalculation1?x.Sum(groupedDate.Column1):0,
Calculation2=doccalculation2?x.Count(groupedDate):0
};
var q=dbEntity
.GroupBy(x=>x.date)
.选择(简化器.简化(选择))
然而,这可能比它的价值更麻烦。SQL Server几乎毫无疑问会优化“1==1”的情况,允许实体框架生成不太漂亮的查询不应该成为性能问题

更新 看看更新后的问题,从性能角度看,这似乎是产生正确查询真正重要的少数实例之一


除了我建议的解决方案之外,还有一些其他选择:可以映射到返回类型,也可以根据需要选择不同的表达式,然后在Select查询中“调用”该表达式

我不太明白。如果这太麻烦了,那么您应该建议如何让生成的Sql像我在第二个案例中所展示的那样结束,而不是变成一个案例语句?@AlexanderBurke:我用一个例子更新了答案,说明了编写表达式访问者将需要什么来简化您的选择表达式,因此,实体框架将生成您所要求的查询类型。正如您所看到的,它相当复杂,并且没有提供任何真正的好处,因为它与SQL Server的透视图没有真正的区别,而且这些查询只供SQL Server使用——它们不是人类可读的。因此,我建议您保持代码的原样,不要费心尝试更改SQL输出。感谢您提供的示例。我不确定我是否做了错误的事情,但是linq为case语句生成的sql运行速度似乎比只有Select 1的sql慢40-60%。我也在看执行计划,case语句版本有很多小表扫描和各种连接,它们占据了大部分查询。Select 1版本的sql由一个表查找占用72%,由hastmatch和嵌套循环占用27%。我将编辑我的问题,以包含确切的linq查询及其生成的sql,以便您可以查看是否介意进一步澄清。我正在实现上面列出的表达式visitor。我想我已经开始理解它是如何工作的了,但是select表达式的实例化不能告诉我们groupedDate是什么类型的
Expression<Func<IGrouping<DateTime, MyEntity>>, ClassYouWantToReturn> select = 
    groupedDate => new {
        Calculation1 = doCalculation1 ? x.Sum(groupedDate.Column1) : 0),
        Calculation2 = doCalculation2 ? x.Count(groupedDate) : 0
    };
var q = dbEntity
    .GroupBy(x => x.date)
    .Select(Simplifier.Simplify(select))