【问题标题】:How to convert an anonymous type in Dynamic LINQ to a strong type如何将动态 LINQ 中的匿名类型转换为强类型
【发布时间】:2015-09-27 04:10:36
【问题描述】:

这是一个多方面的问题,所以让我深入研究代码并在底部进行一些解释。

示例样本数据:

List<Encounter> input
Id, Facility, HospitalService, Field3, Field4, Field5, ...
 1,  1,       A,               ...,    ...,    ...
 2,  2,       A,               ...,    ...,    ...
 3,  1,       B,               ...,    ...,    ...
 4,  2,       B,               ...,    ...,    ...
 5,  1,       A,               ...,    ...,    ...
 5,  2,       A,               ...,    ...,    ...

我想要做的是查询我的数据对象,例如返回不同的字段,例如

distinct Facility is 1, 2
distinct HospitalService is A, B
distinct pair is 1A, 2A, 1B, 2B

但是,一个问题是我想返回一个强类型对象,在这种情况下,与输入相同的对象,在这种情况下是 Encounter 对象,所有其他字段都具有空值或默认值,例如

List<Encounter> output, with only fields of interest populated
Id, Facility, HospitalService, Field3, Field4, Field5, ...
 0,  1,       A,               "",     "",     ""
 0,  2,       A,               "",     "",     ""
 0,  1,       B,               "",     "",     ""
 0,  2,       B,               "",     "",     ""

使用标准 LINQ,我可以做到这一点,而且效果很好。

    List<Encounter> sampleData = CreateSampleData();
    List<Encounter> rawResultFromStandardLinq =
        sampleData
            .GroupBy(e => new {e.Facility, e.HospitalService})
            .Select(e => new Encounter() { Facility = e.Key.Facility, HospitalService = e.Key.HospitalService})
            .ToList();

问题 #1: 在上面的例子中,它不是动态的。我必须知道使用 new 关键字创建哪个对象。此外,我必须知道选择/项目哪些领域。我怎样才能动态地做到这一点?如何将匿名类型投影为强类型?

例如我想我可以做这样的事情来使用 json 序列化。这行得通,但我认为它会很慢。

    var rawResultAsAnonymousType =
        sampleData
            .GroupBy(e => new { e.Facility, e.HospitalService })
            .Select(e => new { e.Key.Facility, e.Key.HospitalService }) 
            .ToList();

    string json = JsonConvert.SerializeObject(rawResultAsAnonymousType);
    var encountersFromJson = JsonConvert.DeserializeObject<List<Encounter>>(json);

问题 #2: 我们遇到的下一个问题是我们希望查询是动态的。 即我们想要公开一个接口,让客户端查询数据以获得他们想要的任何东西。为此,我们转向了 Dynamic LINQ。

有人可以帮我解决这个问题吗?

[Update: I can now do this for multiple columns]
            var rawResultFromDynamicLinq4 =
                DynamicQueryable
                    .GroupBy(_sampleData.AsQueryable(), @"new (Facility, HospitalService)", "it")
                    .Select("new (it.Key.Facility, it.Key.HospitalService)")
                    ;

[Before, I was trying to do this]
        var rawResultFromDynamicLinq =
            sampleData
                .GroupByMany("Facility", "HospitalService")
                //.Select(.... how do I get my object back?)
            ;

一些解释:

我们为什么要这样做? 这在很大程度上与技术问题无关,但如果你必须知道,我在医疗保健部门工作,我们正在使用 FHIR 标准来查询数据,所以我们必须使用定义的 FHIR 模型。这意味着我们不能只返回一个包含特定字段的不同值的列表(例如,让客户端创建用于过滤数据的下拉值)。

【问题讨论】:

    标签: c# linq anonymous-types dynamic-linq


    【解决方案1】:

    如何将匿名类型投影为强类型?

    嗯,匿名类型 一个强类型——你只是在编译时不知道类型的名称(因此是“匿名”)。当然,您可以投影到 不同的 类型并使用 AutoMapper 等工具将匿名类型映射到不同的类型,但您仍然必须在编译时知道这些字段.

    可能能够执行以下操作:

        sampleData
            .GroupBy(e => new {e.Facility, e.HospitalService})
            .Select(g => g.First())
            .ToList();
    

    但不清楚这是否正是您要寻找的(并且您仍然需要在编译时了解“分组”字段)。

    我怎样才能动态地做到这一点?

    “动态”是什么意思?你的意思是有一些东西可以根据目标类型自动设置属性?

    有人可以帮我解决这个问题吗?

    同样,如果你只想要第一个匹配每个分组条件的项目,你可以这样做

    var rawResultFromDynamicLinq =
        sampleData
            .GroupByMany("Facility", "HospitalService")
            .Select(g -> g.First())
        ;
    

    【讨论】:

      【解决方案2】:

      对#2 的回答:确实没有动态 linq 这样的东西;但是,有一些第三方库可以做到这一点。 .Net 也内置有 2 个选项。第一个是执行动态 SQL 的能力(这是个坏主意)。第二个选项是表达式树。这可能适用于问题 1,但类型不能是动态的。

           // Add a using directive for System.Linq.Expressions. 
      
              string[] companies = { "Consolidated Messenger", "Alpine Ski House", "Southridge Video", "City Power & Light",
                                 "Coho Winery", "Wide World Importers", "Graphic Design Institute", "Adventure Works",
                                 "Humongous Insurance", "Woodgrove Bank", "Margie's Travel", "Northwind Traders",
                                 "Blue Yonder Airlines", "Trey Research", "The Phone Company",
                                 "Wingtip Toys", "Lucerne Publishing", "Fourth Coffee" };
      
              // The IQueryable data to query.
              IQueryable<String> queryableData = companies.AsQueryable<string>();
      
              // Compose the expression tree that represents the parameter to the predicate.
              ParameterExpression pe = Expression.Parameter(typeof(string), "company");
      
              // ***** Where(company => (company.ToLower() == "coho winery" || company.Length > 16)) *****
              // Create an expression tree that represents the expression 'company.ToLower() == "coho winery"'.
              Expression left = Expression.Call(pe, typeof(string).GetMethod("ToLower", System.Type.EmptyTypes));
              Expression right = Expression.Constant("coho winery");
              Expression e1 = Expression.Equal(left, right);
      
              // Create an expression tree that represents the expression 'company.Length > 16'.
              left = Expression.Property(pe, typeof(string).GetProperty("Length"));
              right = Expression.Constant(16, typeof(int));
              Expression e2 = Expression.GreaterThan(left, right);
      
              // Combine the expression trees to create an expression tree that represents the 
              // expression '(company.ToLower() == "coho winery" || company.Length > 16)'.
              Expression predicateBody = Expression.OrElse(e1, e2);
      
              // Create an expression tree that represents the expression 
              // 'queryableData.Where(company => (company.ToLower() == "coho winery" || company.Length > 16))'
              MethodCallExpression whereCallExpression = Expression.Call(
                  typeof(Queryable),
                  "Where",
                  new Type[] { queryableData.ElementType },
                  queryableData.Expression,
                  Expression.Lambda<Func<string, bool>>(predicateBody, new ParameterExpression[] { pe }));
              // ***** End Where ***** 
      
              // ***** OrderBy(company => company) ***** 
              // Create an expression tree that represents the expression 
              // 'whereCallExpression.OrderBy(company => company)'
              MethodCallExpression orderByCallExpression = Expression.Call(
                  typeof(Queryable),
                  "OrderBy",
                  new Type[] { queryableData.ElementType, queryableData.ElementType },
                  whereCallExpression,
                  Expression.Lambda<Func<string, string>>(pe, new ParameterExpression[] { pe }));
              // ***** End OrderBy ***** 
      
              // Create an executable query from the expression tree.
              IQueryable<string> results = queryableData.Provider.CreateQuery<string>(orderByCallExpression);
      
              // Enumerate the results. 
              foreach (string company in results)
                  Console.WriteLine(company);
      
              /*  This code produces the following output:
      
                  Blue Yonder Airlines
                  City Power & Light
                  Coho Winery
                  Consolidated Messenger
                  Graphic Design Institute
                  Humongous Insurance
                  Lucerne Publishing
                  Northwind Traders
                  The Phone Company
                  Wide World Importers
              */
      

      https://msdn.microsoft.com/en-us/library/bb882637.aspx

      【讨论】:

      【解决方案3】:

      这可以通过对 System.Linq.Dynamic.Library 进行一些修改来实现

      在此处查看更改的代码:https://gist.github.com/de1e6c5e758e15cc9154.git 和此处https://gist.github.com/d166f17cd672b696b916.git

      你现在可以这样使用:

      var sampleData = new List<Encounter>
      {
          new Encounter {Id = "1", Language = "1", VersionId = "A"},
          new Encounter {Id = "2", Language = "2", VersionId = "A"},
          new Encounter {Id = "3", Language = "1", VersionId = "B"},
          new Encounter {Id = "4", Language = "2", VersionId = "B"},
          new Encounter {Id = "5", Language = "1", VersionId = "A"},
          new Encounter {Id = "6", Language = "2", VersionId = "A"}
      };
      
      List<Encounter> fromStandardLinq = sampleData
              .GroupBy(e => new { e.Language, e.VersionId })
              .Select(e => new Encounter { Id = "0", Language = e.Key.Language, VersionId = e.Key.VersionId })
              .ToList();
      
      Console.WriteLine("fromStandardLinq:");
      foreach (var en in fromStandardLinq)
      {
          Console.WriteLine("{0} {1} {2}", en.Id, en.Language, en.VersionId);
      }
      
      var fromDynamicLinq1a = sampleData.AsQueryable()
              .GroupBy(@"new (Language, VersionId)", "it")
              .Select<Encounter>("new (\"0\" as Id, it.Key.Language, it.Key.VersionId)")
              ;
      
      Console.WriteLine("fromDynamicLinq1a:");
      foreach (Encounter en in fromDynamicLinq1a)
      {
          Console.WriteLine("{0} {1} {2}", en.Id, en.Language, en.VersionId);
      }
      
      var fromDynamicLinq1b = sampleData.AsQueryable()
              .GroupBy(@"new (Language, VersionId)", "it")
              .Select(new { Id = "9", Language = "9", VersionId = "9" }, "new (\"0\" as Id, it.Key.Language, it.Key.VersionId)")
              .Select(x => x)
              ;
      
      Console.WriteLine("fromDynamicLinq1b:");
      foreach (dynamic en in fromDynamicLinq1b)
      {
          Console.WriteLine("{0} {1} {2}", en.Id, en.Language, en.VersionId);
      }
      
      Console.WriteLine("fromDynamicLinq2a:");
      var rawResultFromDynamicLinq2a = sampleData.AsQueryable()
              .GroupBy(@"new (Language, VersionId)", "it")
              .Select(typeof(Encounter), "new (\"0\" as Id,it.Key.Language, it.Key.VersionId)")
              ;
      
      foreach (Encounter en in rawResultFromDynamicLinq2a)
      {
          Console.WriteLine("{0} {1} {2}", en.Id, en.Language, en.VersionId);
      }
      
      Console.WriteLine("fromDynamicLinq2b:");
      var rawResultFromDynamicLinq2b = sampleData.AsQueryable()
              .GroupBy(@"new (Language, VersionId)", "it")
              .Select(typeof(Encounter), new { Id = "9", Language = "9", VersionId = "9" }, "new (\"0\" as Id,it.Key.Language, it.Key.VersionId)")
              ;
      
      foreach (Encounter en in rawResultFromDynamicLinq2b)
      {
          Console.WriteLine("{0} {1} {2}", en.Id, en.Language, en.VersionId);
      }
      

      【讨论】:

        猜你喜欢
        • 2010-10-22
        • 2011-04-19
        • 1970-01-01
        • 2012-01-17
        • 1970-01-01
        • 1970-01-01
        • 2015-04-25
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多