簡體   English   中英

在表達式樹中組合表達式

[英]Combining Expressions in an Expression Tree

當表達式的某些部分作為參數傳遞時,如何構建表達式樹?

例如,如果我想創建這樣的表達式樹,該怎么辦:

IQueryable<LxUser> test1(IQueryable<LxUser> query, string foo, string bar)
{
  query=query.Where(x => x.Foo.StartsWith(foo));
  return query.Where(x => x.Bar.StartsWith(bar));
}

但通過間接創造它們:

IQueryable<LxUser> test2(IQueryable<LxUser> query, string foo, string bar)
{
  query=testAdd(query, x => x.Foo, foo);
  return testAdd(query, x => x.Bar, bar);
}

IQueryable<T> testAdd<T>(IQueryable<T> query, 
  Expression<Func<T, string>> select, string find)
{
  // how can I combine the select expression with StartsWith?
  return query.Where(x => select(x) .. y => y.StartsWith(find));
}

結果:

雖然樣本沒有多大意義(抱歉,但我試圖保持簡單),這是結果(感謝Quartermeister)。

它可以與Linq-to-Sql一起使用,以搜索以findText開頭或等於的字符串。

public static IQueryable<T> WhereLikeOrExact<T>(IQueryable<T> query, 
  Expression<Func<T, string>> selectField, string findText)
{
  Expression<Func<string, bool>> find;
  if (string.IsNullOrEmpty(findText) || findText=="*") return query;

  if (findText.EndsWith("*")) 
    find=x => x.StartsWith(findText.Substring(0, findText.Length-1));
  else
    find=x => x==findText;

  var p=Expression.Parameter(typeof(T), null);
  var xpr=Expression.Invoke(find, Expression.Invoke(selectField, p));

  return query.Where(Expression.Lambda<Func<T, bool>>(xpr, p));
}

例如

var query=context.User;

query=WhereLikeOrExact(query, x => x.FirstName, find.FirstName);
query=WhereLikeOrExact(query, x => x.LastName, find.LastName);

您可以使用Expression.Invoke創建表示將一個表達式應用於另一個表達式的表達式,並使用Expression.Lambda為組合表達式創建新的lambda表達式。 像這樣的東西:

IQueryable<T> testAdd<T>(IQueryable<T> query, 
    Expression<Func<T, string>> select, string find)
{
    Expression<Func<string, bool>> startsWith = y => y.StartsWith(find);
    var parameter = Expression.Parameter(typeof(T), null);
    return query.Where(
        Expression.Lambda<Func<T, bool>>(
            Expression.Invoke(
                startsWith,
                Expression.Invoke(select, parameter)),
            parameter));
}

內部Expression.Invoke表示表達式select(x) ,外部表示對select(x)返回的值調用y => y.StartsWith(find) select(x)

您也可以使用Expression.Call來表示對StartsWith的調用,而不使用第二個lambda:

IQueryable<T> testAdd<T>(IQueryable<T> query,
    Expression<Func<T, string>> select, string find)
{
    var parameter = Expression.Parameter(typeof(T), null);
    return query.Where(
        Expression.Lambda<Func<T, bool>>(
            Expression.Call(
                Expression.Invoke(select, parameter),
                "StartsWith",
                null,
                Expression.Constant(find)),
            parameter));
}

這個作品:

public IQueryable<T> Add<T>(IQueryable<T> query, Expression<Func<T, string>> Selector1,
                    Expression<Func<T, string>> Selector2, string data1, string data2)
{
    return Add(Add(query, Selector1, data1), Selector2, data2);
}

public IQueryable<T> Add<T>(IQueryable<T> query, Expression<Func<T, string>> Selector, string data)
{
    var row = Expression.Parameter(typeof(T), "row");
    var expression =
        Expression.Call(
            Expression.Invoke(Selector, row),
            "StartsWith", null, Expression.Constant(data, typeof(string))
        );
    var lambda = Expression.Lambda<Func<T, bool>>(expression, row);
    return query.Where(lambda);
}

你使用它像:

IQueryable<XlUser> query = SomehowInitializeIt();
query = Add(query, x => x.Foo, y => y.Bar, "Foo", "Bar");

通常你不會以你所描述的方式(使用IQueryable接口)這樣做,而是使用像Expression<Func<TResult, T>>這樣的Expression<Func<TResult, T>> 話雖如此,您可以將更高階的函數(例如whereselect )組合成一個查詢,並傳入將“填充”所需功能的表達式。

例如,考慮Enumerable.Where方法的簽名:

Where<TSource>(IEnumerable<TSource>, Func<TSource, Boolean>)

該函數將委托作為在每個元素上調用的第二個參數。 從該委托返回的值表示高階函數是否應該產生當前元素(包括在結果中)。

現在,我們來看看Queryable.Where

Queryable.Where<TSource>-Methode (IQueryable<TSource>, Expression<Func<TSource, Boolean>>)

我們可以觀察到更高階函數的相同模式,但是它取代了Func<>委托,它需要一個表達式。 表達式基本上是代碼的數據表示。 編譯該表達式將為您提供真實(可執行)委托。 編譯器為你分配給Expression<...> lambda建立表達式樹做了很多繁重的工作。 表達式樹可以針對不同的數據源(如SQL Server數據庫)編譯所描述的代碼。

回到你的例子,我認為你正在尋找的是一個選擇器 選擇器獲取每個輸入元素並返回它的投影。 它的簽名如下: Expression<Func<TResult, T>> 例如,你可以指定這個:

Expression<Func<int, string>> numberFormatter = (i) => i.ToString(); // projects an int into a string

要傳入選擇器,您的代碼需要如下所示:

IQueryable<T> testAdd<T>(IQueryable<T> query, Expression<Func<string, T>> selector, string find)
{
  // how can I combine the select expression with StartsWith?
  return query.Select(selector) // IQueryable<string> now
              .Where(x => x.StartsWith(find));
}

此選擇器允許您將輸入字符串投影到所需的類型。 我希望我能夠正確地理解你的意圖,很難看出你想要實現的目標。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM