Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/322.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

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/linq/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# IQueryable上的动态LINQ聚合作为单个查询_C#_Linq_Dynamic Linq - Fatal编程技术网

C# IQueryable上的动态LINQ聚合作为单个查询

C# IQueryable上的动态LINQ聚合作为单个查询,c#,linq,dynamic-linq,C#,Linq,Dynamic Linq,我正在构建一个datagrid库,它在通用IQueryable数据源上工作。在底部,所选列将具有聚合:总和、平均值、计数等 我可以使用本文中的代码单独计算总和/平均数/计数 我不想为一个数据源单独运行它们,因为这会导致对数据库进行多个查询,我宁愿创建一个表达式树,并将其作为一个查询执行 在静态LINQ中,您将执行所有的.Sum、.Average和.Count方法,并返回一个新的匿名类型和值。我不需要匿名类型(除非这是唯一的方法):聚合的列表或数组就可以了 我假设在另一篇文章中,我需要以某种方式将

我正在构建一个datagrid库,它在通用IQueryable数据源上工作。在底部,所选列将具有聚合:总和、平均值、计数等

我可以使用本文中的代码单独计算总和/平均数/计数

我不想为一个数据源单独运行它们,因为这会导致对数据库进行多个查询,我宁愿创建一个表达式树,并将其作为一个查询执行

在静态LINQ中,您将执行所有的.Sum、.Average和.Count方法,并返回一个新的匿名类型和值。我不需要匿名类型(除非这是唯一的方法):聚合的列表或数组就可以了


我假设在另一篇文章中,我需要以某种方式将一系列MethodCallExpression对象串在一起。有人能帮忙吗?

我找到了另一种方法,它使用动态LINQ库,避免了构建复杂的表达式树

对于任何感兴趣的人,解决方案都在下面的单元测试中。我有一个名为TestQueryableDataset的随机数据集。此IQueryable数据源的泛型类型具有Total属性(decimal)、折扣属性(nullable decimal)和ID属性(int)

单元测试首先使用静态LINQ查询获得预期结果

然后它构造一个select语句,该语句使用groupby变量“It”来计算总和、平均值和计数。属性名通过字符串传递,以证明它是字符串类型的

GroupBy方法.GroupBy(x=>1)是一个虚拟分组,用于使聚合应用于整个数据集

请注意,这将返回一个具有属性t0、t1和t2的动态结果。但是,groupby/select操作仍然返回IQueryable,但只返回一个结果。我们必须先使用t.Cast();转换为对象数组,然后获得第一个结果

然后,我们可以使用反射来获得每个结果(t0、t1、t2)的属性作为实际值,并断言它们与我们之前得到的静态结果相匹配

    [TestMethod()]
    [TestProperty("Anvil.DataSets", "QueryableExtensions")]
    public void DynamicAggregate_test()
    {
        var source = new Anvil.Test.DataSets.TestQueryableDataset();

        var data = source.GetData();

        var expectedTotal = (from d in data select d.Total).Sum();
        var expectedDiscount = (from d in data select d.Discount).Average();
        var expectedCount = (from d in data select d.ID).Count();

        const string prop0 = "Total";
        const string prop1 = "Discount";
        const string prop2 = "ID";

        string sumExpr = string.Format("new ( Sum(it.{0}) as t0, Average(it.{1}) as t1 , Count() as t2)", prop0,prop1, prop2);
        var t = data.GroupBy(x => 1).Select(sumExpr);

        var firstItem = t.Cast<object>().First();

        var ttype = firstItem.GetType();
        var p0 = ttype.GetProperty("t0");
        var p1 = ttype.GetProperty("t1");
        var p2 = ttype.GetProperty("t2");

        decimal actualTotal = (decimal)(p0.GetValue(firstItem));
        decimal actualDiscount = (decimal)(p1.GetValue(firstItem));
        int actualCount = (int)(p2.GetValue(firstItem));

        Assert.AreEqual(expectedTotal, actualTotal);
        Assert.AreEqual(expectedDiscount, actualDiscount);
        Assert.AreEqual(expectedCount, actualCount);
    }
[TestMethod()]
[TestProperty(“Anvil.dataset”、“QueryableExtensions”)]
公共无效动态GREGATE_测试()
{
var source=new Anvil.Test.DataSets.TestQueryableDataset();
var data=source.GetData();
var expectedTotal=(从数据中的d选择d.Total).Sum();
var expectedDiscount=(从数据中的d选择d.Discount).Average();
var expectedCount=(从数据中的d选择d.ID).Count();
常量字符串prop0=“总计”;
const string prop1=“折扣”;
常量字符串prop2=“ID”;
string sumExpr=string.Format(“new(Sum(it.{0})作为t0,Average(it.{1})作为t1,Count()作为t2)”,prop0,prop1,prop2;
var t=data.GroupBy(x=>1);
var firstItem=t.Cast().First();
var ttype=firstItem.GetType();
var p0=ttype.GetProperty(“t0”);
var p1=ttype.GetProperty(“t1”);
var p2=ttype.GetProperty(“t2”);
decimal actualTotal=(十进制)(p0.GetValue(第一项));
decimal ActualDiscovery=(十进制)(p1.GetValue(第一项));
int actualCount=(int)(p2.GetValue(firstItem));
等于(预期总数、实际总数);
Assert.AreEqual(预期结算、实际结算);
Assert.AreEqual(预期计数、实际计数);
}
另见:


    • 您不需要匿名类型。您只需要一个具有3个属性的类型
      Sum
      Count
      Average
      <代码>总和和
      平均值
      类型在设计时未知。因此,对这两个属性使用
      Object
      type
      Count
      始终是一个
      int

      public class Aggregation
      {
          public Aggregation(object sum, object average, int count)
          {
              Sum = sum;
              Average = average;
              Count = count;
          }
          public object Sum { get; private set; }
          public object Average { get; private set; }
          public int Count { get; private set; }
      }
      
      与本文中描述的
      Sum
      扩展方法类似,您可以编写一个
      Aggregate
      扩展方法,该方法从
      IQueryable
      集合和属性名称计算
      Aggregation
      类实例。 真正的困难在于确定与属性类型匹配的平均重载方法。重载不能从返回类型确定,而是从用作第二个参数的lambda表达式的返回类型确定

      例如,如果属性类型是int,则代码必须选择
      公共静态双平均值(
      这是可靠的消息来源,
      表达式选择器
      )
      过载

      public static Aggregation Aggregate(this IQueryable source, string member)
      {
          if (source == null)
              throw new ArgumentNullException("source");
          if (member == null)
              throw new ArgumentNullException("member");
      
          // Properties
          PropertyInfo property = source.ElementType.GetProperty(member);
          ParameterExpression parameter = Expression.Parameter(source.ElementType, "s");
          Expression selector = Expression.Lambda(Expression.MakeMemberAccess(parameter, property), parameter);
          // We've tried to find an expression of the type Expression<Func<TSource, TAcc>>,
          // which is expressed as ( (TSource s) => s.Price );
      
          // Methods
          MethodInfo sumMethod = typeof(Queryable).GetMethods().First(
              m => m.Name == "Sum"
                  && m.ReturnType == property.PropertyType // should match the type of the property
                  && m.IsGenericMethod);
          MethodInfo averageMethod = typeof(Queryable).GetMethods().First(
              m => m.Name == "Average"
                  && m.IsGenericMethod
                  && m.GetParameters()[1]
                      .ParameterType
                          .GetGenericArguments()[0]
                              .GetGenericArguments()[1] == property.PropertyType);
          MethodInfo countMethod = typeof(Queryable).GetMethods().First(
              m => m.Name == "Count"
                  && m.IsGenericMethod);
      
          return new Aggregation(
              source.Provider.Execute(
                  Expression.Call(
                      null,
                      sumMethod.MakeGenericMethod(new[] { source.ElementType }),
                      new[] { source.Expression, Expression.Quote(selector) })),
              source.Provider.Execute(
                  Expression.Call(
                      null,
                      averageMethod.MakeGenericMethod(new[] { source.ElementType }),
                      new[] { source.Expression, Expression.Quote(selector) })),
              (int)source.Provider.Execute(
                  Expression.Call(
                      null,
                      countMethod.MakeGenericMethod(new[] { source.ElementType }),
                      new[] { source.Expression })));
      }
      
      公共静态聚合聚合(此IQueryable源,字符串成员)
      {
      if(source==null)
      抛出新的ArgumentNullException(“源”);
      if(成员==null)
      抛出新的异常(“成员”);
      //性质
      PropertyInfo property=source.ElementType.GetProperty(成员);
      ParameterExpression参数=Expression.parameter(source.ElementType,“s”);
      表达式选择器=Expression.Lambda(Expression.MakeMemberAccess(参数,属性),参数);
      //我们试图找到表达式类型的表达式,
      //表示为((t来源s)=>s.价格);
      //方法
      MethodInfo sumMethod=typeof(Queryable).GetMethods().First(
      m=>m.Name==“总和”
      &&m.ReturnType==property.PropertyType//应与属性的类型匹配
      &&m.一般方法);
      MethodInfo averageMethod=typeof(Queryable).GetMethods().First(
      m=>m.Name==“平均”
      &&m.IsGenericMethod
      &&m.GetParameters()[1]
      .参数类型
      .GetGenericArguments()[0]
      .GetGenericArguments()[1]==property.PropertyType);
      MethodInfo countMethod=typeof(Queryable).GetMethods().First(
      m=>m.Name==“计数”
      &&m.一般方法);
      返回新的聚合(
      source.Provider.Execute(
      表情,打电话(
      无效的
      SummMethod.MakeGenericMethod(新[]{source.Element
      
      public static object AggregateFunc(this IQueryable source, string function, string member)
      {
              if (source == null) throw new ArgumentNullException("source");
              if (member == null) throw new ArgumentNullException("member");
      
              // Properties
              PropertyInfo property = source.ElementType.GetProperty(member);
              ParameterExpression parameter = Expression.Parameter(source.ElementType, "s");
      
              // We've tried to find an expression of the type Expression<Func<TSource, TAcc>>,
              // which is expressed as ( (TSource s) => s.Price );
      
              Type propertyType = property.PropertyType;
              Type convertPropType = property.PropertyType;
              if (function == "Sum")//convert int to bigint
              {
                  if (propertyType == typeof(Int32))
                      convertPropType = typeof(Int64);
                  else if (propertyType == typeof(Int32?))
                      convertPropType = typeof(Int64?);
              }
              Expression selector = Expression.Lambda(Expression.Convert(Expression.MakeMemberAccess(parameter, property), convertPropType), parameter);
              //var methods = typeof(Queryable).GetMethods().Where(x => x.Name == function);
              // Method
              MethodInfo aggregateMethod = typeof(Queryable).GetMethods().SingleOrDefault(
                  m => m.Name == function
                      && m.IsGenericMethod
                      && m.GetParameters().Length == 2 && m.GetParameters()[1].ParameterType.GenericTypeArguments[0].GenericTypeArguments[1] == convertPropType);// very hacky but works :)
      
              MethodCallExpression callExpr;
              // Sum, Average
              if (aggregateMethod != null)
              {
                  callExpr = Expression.Call(
                          null,
                          aggregateMethod.MakeGenericMethod(new[] { source.ElementType }),
                          new[] { source.Expression, Expression.Quote(selector) });
                  return source.Provider.Execute(callExpr);
              }
              // Min, Max
              else
              {
                  aggregateMethod = typeof(Queryable).GetMethods().SingleOrDefault(
                      m => m.Name == function
                          && m.GetGenericArguments().Length == 2
                          && m.IsGenericMethod);
                  if (aggregateMethod != null)
                  {
                      callExpr = Expression.Call(
                          null,
                          aggregateMethod.MakeGenericMethod(new[] { source.ElementType, propertyType }),
                          new[] { source.Expression, Expression.Quote(selector) });
      
                      return source.Provider.Execute(callExpr);
                  }                
              }
              return null;
          }