C# 用LINQ连接到SQL的表达式树

C# 用LINQ连接到SQL的表达式树,c#,linq-to-sql,lambda,expression,func,C#,Linq To Sql,Lambda,Expression,Func,我正在尝试制作一个灵活的导出器,将信息从通过LINQ访问的SQL数据库导出到SQL,这样程序就可以动态地选择要选择的字段,并在服务器端执行所有处理 最终目的是要有一个简单的陈述,如: var result = db.Product.Select(p => selector(p)); 其中选择器是一个动态创建的表达式树,描述要选择的字段。目前,我将每个db字段分配给它自己的单独选择器,如: Expression<Func<Product, string>> name

我正在尝试制作一个灵活的导出器,将信息从通过LINQ访问的SQL数据库导出到SQL,这样程序就可以动态地选择要选择的字段,并在服务器端执行所有处理

最终目的是要有一个简单的陈述,如:

var result = db.Product.Select(p => selector(p));
其中选择器是一个动态创建的表达式树,描述要选择的字段。目前,我将每个db字段分配给它自己的单独选择器,如:

Expression<Func<Product, string>> name = p => p.Name; 
Expression<Func<Product, string>> createdAt = p => p.createdAt.ToString();
expressionname=p=>p.name;
表达式createdAt=p=>p.createdAt.ToString();
目前,我正在获取所选字段,并尝试从中生成一个串联表达式,该表达式从select语句返回逗号分隔的字符串结果,类似于:

Expression<
  Func<
   Func<Product, string>,
   Func<Product, string>,
   Func<Product, string>>> combinator = (a, b) => (p) => a(p) + "," + b(p);

// So I can do something like...
Expression<Func<Product, string>> aggregate = p => "";

foreach (var field in fieldsToSelect)
    aggregate = combinator(aggregate, field);

// This would create...
Expression<Func<Products, string>> aggregate = p => a(p) + "," + b(p);
表达式<
Func<
Func,
Func,
Func>>combinator=(a,b)=>(p)=>a(p)+>,“+b(p);
//所以我可以做一些像。。。
表达式聚合=p=>“”;
foreach(fieldsToSelect中的var字段)
聚合=组合器(聚合,字段);
//这将创造。。。
表达式聚合=p=>a(p)+“,”+b(p);
一旦我用多少字段建立了选择器,我就可以在语句中执行它,所有的处理都在服务器上完成。但是,我无法正确创建一个表达式来连接两个子函数,从而使结果不是一个Func,而该Func只是在往返获取结果后执行:

var joinedSelector = combinator.Compile();
Func<Products, string> result = joinedSelector(firstSelector.Compile(), secondSelector.Compile());
var query  = db.Product.Select(p => result(p)).ToList();
var joinedSelector=combinator.Compile();
Func result=joinedSelector(firstSelector.Compile(),secondSelector.Compile());
var query=db.Product.Select(p=>result(p)).ToList();
从我对表达式树的有限理解来看,这实际上并不会产生表达式树,因为这些语句是编译成普通func的。我已经看过Expression.Coalesce(),但不确定它是否是我想要的(认为它只是做了“?”)

我对这类东西还不太熟悉,如果有任何帮助,我将不胜感激


即使人们能想出更好的方法来解决最初的问题,我还是很想解释一下,为了学习如何使用表达式树,我正在尝试如何实现这一目标。

因此,您希望创建一种可以组合两个选择器结果的
组合方法。为此,我们需要一个方法,该方法接受两个具有相同输入和相同输出的函数,然后一个函数接受该输出类型的两个实例并返回一个新值

该函数需要用一个公共参数替换选择器主体的所有参数实例。然后,它将用不同选择器的相应主体替换结果函数的两个参数。然后我们就把这些都包在一个lambda里

public static Expression<Func<T, TResult>> Combine
    <T, TIntermediate1, TIntermediate2, TResult>(
this Expression<Func<T, TIntermediate1>> first,
Expression<Func<T, TIntermediate2>> second,
Expression<Func<TIntermediate1, TIntermediate2, TResult>> resultSelector)
{
    var param = Expression.Parameter(typeof(T));
    var body = resultSelector.Body.Replace(
            resultSelector.Parameters[0],
            first.Body.Replace(first.Parameters[0], param))
        .Replace(
            resultSelector.Parameters[1],
            second.Body.Replace(second.Parameters[0], param));
    return Expression.Lambda<Func<T, TResult>>(body, param);
}
现在我们可以写以下内容:

Expression<Func<Product, string>> name = p => p.Name;
Expression<Func<Product, string>> createdAt = p => p.createdAt.ToString();

Expression<Func<Product, string>> aggregate = 
    Combine(name, createdAt, (a, b) => a + "," + b);
expressionname=p=>p.name;
表达式createdAt=p=>p.createdAt.ToString();
表达式聚合=
组合(名称,createdAt,(a,b)=>a+“,”+b);
它实际上比你的模型要简单一些,因为结果选择器不需要知道它的输入是如何生成的,或者它们实际上是选择器。此解决方案还允许每个选择器选择不同的类型,并使它们与结果不同,这仅仅是因为在推断泛型参数时添加所有这些类型不会带来实际成本

考虑到您设置的类型限制,您甚至可以轻松地聚合任意数量的选择器:

IEnumerable<Expression<Func<Product, string>>> selectors = new[]{
    name,
    createdAt,
    name,
};

var finalSelector = selectors.Aggregate((first, second) =>
    Combine(first, second, (a, b) => a + "," + b));
IEnumerable选择器=new[]{
名称
创建数据,
名称
};
var finalSelector=选择器。聚合((第一,第二)=>
组合(第一,第二,(a,b)=>a+“,”+b));

例如,这将允许您使用
params
方法接受任意数量的选择器(公共输入和输出类型),并能够将其所有结果聚合在一起。

我已编辑了您的标题。请看“”,其中的共识是“不,他们不应该”。嗨,Servy,谢谢你的帮助,这正是我想要的。给了我一些东西让我去仰望以完全理解它,但它的作用就像一个符咒;)
IEnumerable<Expression<Func<Product, string>>> selectors = new[]{
    name,
    createdAt,
    name,
};

var finalSelector = selectors.Aggregate((first, second) =>
    Combine(first, second, (a, b) => a + "," + b));