简体   繁体   English

使用字符串字段名称动态构建带层次结构的linq查询?

[英]Dynamically build up a linq query with hierarchy using string field names?

I'm stumped.. I need to take a flat list of objects, a couple of arrays of field names as string (which to group by, which to sort by, and which to select) that are provided at runtime and somehow generate a LINQ query to return an object for JSON serialization. 我很难过..我需要取一个对象的平面列表,一些字段名称数组作为字符串(分组,分类,选择哪个)在运行时提供并以某种方式生成LINQ查询返回JSON序列化的对象。

I've built a sample repro below of the setup. 我在设置下面构建了一个示例repro。 OrderBy is easy enough, I just use GetType().GetProperty() to find the right property, and iterate the orderby fields to daisy-chain .OrderBy calls. OrderBy很简单,我只是使用GetType()。GetProperty()来查找正确的属性,并将orderby字段迭代到菊花链.OrderBy调用。 That all falls apart on the GroupBy because each one wraps the results in IEnumerable>.. 这一切都在GroupBy上崩溃,因为每个人都将结果包装在IEnumerable>中。

Is there a good way to do this? 有没有办法做到这一点? Everything I'm finding on the web has people resorting to good old recursive procedural code. 我在网上找到的所有东西都让人们诉诸于良好的旧递归程序代码。 LINQ must have a way to do this, but I'm lost. LINQ必须有办法做到这一点,但我迷路了。 Everything I try won't even compile, the types just won't match up.. 我尝试的一切都不会编译,类型只是不匹配..

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Linq.Dynamic;

namespace linqtest2
{
    class Program
    {
        static void Main(string[] args)
        {
            var products = new List<Product>
                {
                    new Product("Food", "Fruit", "Apple"),
                    new Product("Food", "Fruit", "Banana"),
                    new Product("Food", "Fruit", "Orange"),
                    new Product("Food", "Vegetables", "Carrot"),
                    new Product("Food", "Vegetables", "Pea"),
                    new Product("Drink", "Soft Drink", "Orange Juice"),
                    new Product("Drink", "Soft Drink", "Lemonade"),
                    new Product("Drink", "Alcoholic", "Bitter"),
                    new Product("Drink", "Alcoholic", "Lager"),
                    new Product("Drink", "Alcoholic", "Vodka")
                };

            string[] select = new[] { "Category", "Name" };
            string[] orderBy = new[] { "Name", "Category" };
            string[] groupBy = new[] { "Category", "Subcategory" };
        }
    }

    public class Product
    {
        public string Name { get; set; }
        public string Category { get; set; }
        public string Subcategory { get; set; }

        public Product(string category, string subcategory, string name)
        {
            Category = category;
            Subcategory = subcategory;
            Name = name;
        }
    }
}

Please check the Dynamic Linq library which provide a way to use string instead of lambdas. 请检查Dynamic Linq库,它提供了一种使用字符串而不是lambdas的方法。

With dynamic linq you will can write: 使用动态linq,您可以编写:

products.OrderBy("Category").ThenBy("Name")

And a lot of other possibilities. 还有很多其他的可能性。

EDIT: 编辑:

Updating the solution to be more dynamic . 更新解决方案以使其更具动态性

IQueryable<Product> query = products;
bool firstOrderBy = true;
foreach(var orderExpression in orderBy)
{
    if (firstOrderBy)
    {
        query = query.OrderBy(orderExpression);
        firstOrderBy = false;
    }
    else
    {
        query = query.ThenBy(orderExpression);
    }
}

As I suggest, verify the library, explore it and adapt it to your needs. 正如我建议的那样,验证库,探索它并根据您的需要进行调整。 You can also do GroupBy and Select , please with it. 您也可以使用它来进行GroupBySelect

Check also the solution proposed by @dkackman and see which solution is better for your needs, either of the solutions you will need to do a little work to adapt to your needs. 还要检查@dkackman提出的解决方案,看看哪种解决方案更适合您的需求,您需要做一些解决方案来满足您的需求。

Linq syntax is really built around knowing the static structure of the data at compile time. Linq语法实际上是在编译时知道数据的静态结构。 The underlying System.Linq.Expressions namespace has enormous capability to create runtime, as opposed to compile time, bindings but it is a fairly labor intensive under taking. 底层System.Linq.Expressions命名空间具有创建运行时的巨大功能,而不是编译时绑定,但是在使用时它是相当费力的。

In order to support dynamic typing of inputs and outputs you're best served by resorting to some libraries. 为了支持输入和输出的动态类型,最好通过使用一些库来实现。

Dynamic Linq as @Sergio suggests is one option. 动态Linq作为@Sergio建议是一种选择。

SqlLinq is another. SqlLinq是另一个。 With SqlLinq you'd build up the whole query dynamically as a string and execute it something like this: 使用SqlLinq,您将以字符串形式动态构建整个查询,并执行如下操作:

var products = new List<Product>
    {
        new Product("Food", "Fruit", "Apple"),
        new Product("Food", "Fruit", "Banana"),
        new Product("Food", "Fruit", "Orange"),
        new Product("Food", "Vegetables", "Carrot"),
        new Product("Food", "Vegetables", "Pea"),
        new Product("Drink", "Soft Drink", "Orange Juice"),
        new Product("Drink", "Soft Drink", "Lemonade"),
        new Product("Drink", "Alcoholic", "Bitter"),
        new Product("Drink", "Alcoholic", "Lager"),
        new Product("Drink", "Alcoholic", "Vodka")
    };

var results = products.Query<Product, dynamic>("SELECT category, subcategory, count(*) AS cnt FROM this GROUP BY category, subcategory ORDER BY category, subcategory");

foreach(dynamic d in results)
{
    Debug.WriteLine("{0} {1} {2}", (string)d.category, (string)d.subcategory, (int)d.cnt);
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM