欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

C#用表達(dá)式樹構(gòu)建動(dòng)態(tài)查詢的方法

 更新時(shí)間:2020年12月02日 08:53:42   作者:yangyang  
這篇文章主要介紹了C#用表達(dá)式樹構(gòu)建動(dòng)態(tài)查詢的方法,幫助大家更好的理解和學(xué)習(xí)c#,感興趣的朋友可以了解下

  前文介紹了C#中表達(dá)式樹的基本知識(shí),在實(shí)際中,表達(dá)式樹有很多用法,這里舉幾個(gè)例子,說明如何使用表達(dá)式樹構(gòu)建動(dòng)態(tài)查詢,從而擴(kuò)展LINQ的查詢方法。

  在LINQ中,只要數(shù)據(jù)源實(shí)現(xiàn)了IQuerable<T>接口,表達(dá)式樹就可以用來表示結(jié)構(gòu)化查詢。比如,LINQ提供了用來查詢關(guān)系數(shù)據(jù)源的IQueryable<T>接口的實(shí)現(xiàn),C#編譯器在執(zhí)行這類數(shù)據(jù)源查詢時(shí),會(huì)在運(yùn)行時(shí)生成表達(dá)式樹,然后,查詢會(huì)遍歷表達(dá)式樹的數(shù)據(jù)結(jié)構(gòu),然后將其轉(zhuǎn)換成針對(duì)特定數(shù)據(jù)源的合適的查詢語言。

   下面的幾個(gè)例子演示了如何使用表達(dá)式樹動(dòng)態(tài)生成查詢。

Example 1:動(dòng)態(tài)生成Where和OrderBy

這個(gè)例子是MSDN上的例子,平常在C#中我們一般直接寫LINQ代碼,比如下面這個(gè):

companies.Where(company => (company.ToLower() == "coho winery" || company.Length > 16))
     .OrderBy(company => company)

   對(duì)集合進(jìn)行查詢,然后排序等。這個(gè)時(shí)固定化了數(shù)據(jù)源,在有些時(shí)候,我們需要把這個(gè)查詢“泛型化”,這就需要能夠動(dòng)態(tài)構(gòu)造查詢的能力,恰好我們可以使用表達(dá)式樹,動(dòng)態(tài)構(gòu)建查詢。

   在System.Linq.Expression命名空間下有一些工廠方法能夠用來表示各種查詢,從而來組合出上述的查詢條件。首先構(gòu)造出一個(gè)表達(dá)式樹,然后將表達(dá)式樹傳給要查詢數(shù)據(jù)的CreateQuery<IElement>(Expression)方法。

//數(shù)據(jù)源
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" };

IQueryable<string> queryableData = companies.AsQueryable();

//company 參數(shù)
ParameterExpression pe = Expression.Parameter(typeof(string), "company");

// ***** 開始構(gòu)造 Where(company => (company.ToLower() == "coho winery" || company.Length > 16)) ***** 

//構(gòu)造表達(dá)式 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);

//構(gòu)造表達(dá)式 company.Length > 16
left = Expression.Property(pe, typeof(string).GetProperty("Length"));
right = Expression.Constant(16, typeof(int));
Expression e2 = Expression.GreaterThan(left, right);

//構(gòu)造上述兩個(gè)表達(dá)式的或
// '(company.ToLower() == "coho winery" || company.Length > 16)'.
Expression predictBody = Expression.OrElse(e1, e2);

//構(gòu)造where表達(dá)式 '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>>(predictBody, new ParameterExpression[] { pe }));
// ***** Where 部分構(gòu)造完成 ***** 

// *****開始構(gòu)造 OrderBy(company => company) ***** 
// 構(gòu)造表達(dá)式樹,表示 '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 }));
//***** OrderBy 構(gòu)造完成 *****

//創(chuàng)建查詢
IQueryable<string> results = queryableData.Provider.CreateQuery<string>(orderByCallExpression);

foreach (string company in results)
{
  Console.WriteLine(company);
}

  上面代碼中,在傳遞到Queryable.Where方法中,使用了固定數(shù)量的表達(dá)式,在實(shí)際中,可以動(dòng)態(tài)根據(jù)用戶輸入,來合并各種查詢操作。

Example 2: 擴(kuò)展in查詢

   LINQ中,并沒有提供In的操作,比如要查詢xx 是否在["xx1","xx2"...]中時(shí),需要手動(dòng)編寫SQL使用in,并將array拼成分割的字符串。如果使用LINQ,則可以變成xx==xx1,或者xx==xx2,實(shí)現(xiàn)如下:

public static Expression<Func<TElement, bool>> BuildWhereInExpression<TElement, TValue>(
 Expression<Func<TElement, TValue>> propertySelector, IEnumerable<TValue> values)
{
  ParameterExpression p = propertySelector.Parameters.Single();
  if (!values.Any())
    return e => false;

  var equals = values.Select(value => (Expression)Expression.Equal(propertySelector.Body,
    Expression.Constant(value, typeof(TValue))));
  var body = equals.Aggregate<Expression>((accumulate, equal) => Expression.Or(accumulate, equal));

  return Expression.Lambda<Func<TElement, bool>>(body, p);
}

public static IQueryable<TElement> WhereIn<TElement, TValue>(this IQueryable<TElement> source,
  Expression<Func<TElement, TValue>> propertySelector, params TValue[] values)
{
  return source.Where(BuildWhereInExpression(propertySelector, values));
}

比如上例中,我們要判斷在集合中是否存在以下數(shù)據(jù),則可以直接使用where in

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" };

string[] t = new string[] { "Consolidated Messenger", "Alpine Ski House 1" };
 
IQueryable<string> results = companies.AsQueryable().WhereIn(x => x, t);
foreach (string company in results)
{
  Console.WriteLine(company);
}

Example 3:封裝分頁邏輯

   在很多地方都會(huì)用到分頁,所以我們可以把查詢排序跟分頁封裝到一起,這樣用的時(shí)候就很方便,這里針對(duì)查詢和排序,新建一個(gè)實(shí)體對(duì)象。

public class QueryOptions
{
  public int CurrentPage { get; set; } = 1;
  public int PageSize { get; set; } = 10;
  public string OrderPropertyName { get; set; }
  public bool DescendingOrder { get; set; }

  public string SearchPropertyName { get; set; }
  public string SearchTerm { get; set; }
}

   上面這個(gè)對(duì)象,包含了分頁相關(guān)設(shè)置,以及查詢和排序字段及屬性。

    將排序邏輯封裝在了PageList對(duì)象中,該對(duì)象繼承自List,每次分頁就返回分頁后的數(shù)據(jù)放在PageList中。

public class PagedList<T> : List<T>
{
  public int CurrentPage { get; set; }
  public int PageSize { get; set; }
  public int TotalPages { get; set; }
  public QueryOptions Options { get; set; }
  public bool HasPreviousPage => CurrentPage > 1;
  public bool HasNextPage => CurrentPage < TotalPages;

  public PagedList(IQueryable<T> query, QueryOptions o)
  {
    CurrentPage = o.CurrentPage;
    PageSize = o.PageSize;
    Options = o;
    if (o != null)
    {
      if (!string.IsNullOrEmpty(o.OrderPropertyName))
      {
        query = Order(query, o.OrderPropertyName, o.DescendingOrder);
      }

      if (!string.IsNullOrEmpty(o.SearchPropertyName) && !string.IsNullOrEmpty(o.SearchTerm))
      {
        query = Search(query, o.SearchPropertyName, o.SearchTerm);
      }
    }
    TotalPages = query.Count() / PageSize;
    AddRange(query.Skip((CurrentPage - 1) * PageSize).Take(PageSize));
  }

  private IQueryable<T> Search(IQueryable<T> query, string searchPropertyName, string searchItem)
  {
    var parameter = Expression.Parameter(typeof(T), "x");
    var source = searchPropertyName.Split(".").Aggregate((Expression)parameter, Expression.Property);
    var body = Expression.Call(source, "Contains", Type.EmptyTypes, Expression.Constant(searchItem, typeof(string)));
    var lambda = Expression.Lambda<Func<T, bool>>(body, parameter);
    return query.Where(lambda);
  }

  private IQueryable<T> Order(IQueryable<T> query, string orderPropertyName, bool descendingOrder)
  {
    var parameter = Expression.Parameter(typeof(T), "x");
    var source = orderPropertyName.Split(".").Aggregate((Expression)parameter, Expression.Property);
    var lambda = Expression.Lambda(typeof(Func<,>).MakeGenericType(typeof(T), source.Type), source, parameter);
    return typeof(Queryable).GetMethods().Single(method =>
        method.Name == (descendingOrder ? "OrderByDescending" : "OrderBy")
        && method.IsGenericMethodDefinition
        && method.GetGenericArguments().Length == 2
        && method.GetParameters().Length == 2)
      .MakeGenericMethod(typeof(T), source.Type)
      .Invoke(null, new object[] { query, lambda }) as IQueryable<T>;
  }
}

可以看到,在where和order部分,我們通過查詢表達(dá)式,動(dòng)態(tài)構(gòu)造了這兩部分的邏輯。使用的時(shí)候,非常方便:

public PagedList<Category> GetCategories(QueryOptions options)
{
  return new PagedList<Category>(context.Categories, options);
}

這里context.Categories時(shí)DbSet集合,QueryOptions是用戶傳入的分頁選項(xiàng)。每次輸入查詢排序及分頁選項(xiàng),就把分好的放在PageList里面返回。

Example 4 簡(jiǎn)化數(shù)據(jù)綁定

   在Windows Form中,所有的控件都繼承自Control基類,它有一個(gè)DataBindings屬性,可以用來綁定對(duì)象,這樣就能自動(dòng)更新。通常,在需要給一些對(duì)象賦值,或者保存空間里設(shè)置的值到配置文件時(shí),我們可以使用數(shù)據(jù)綁定,而不是手動(dòng)的去一個(gè)一個(gè)的賦值。但DataBindings的原型方法Add為如下,比如我要將某個(gè)空間的值綁定到配置對(duì)象里,代碼如下:

textBoxCustomerName.DataBindings.Add("Text", bindingSource, "CustomerName");

上述代碼將TextBox控件的Text屬性跟bingSource對(duì)象的CustomerName屬性進(jìn)行了綁定,可以看到以上代碼有很多地方需要手寫字符串:TextBox控件的屬性“Text”,bingSource對(duì)象的“CustomerName”屬性,非常不友好,而且容易出錯(cuò),一看這種需要手打字符串的地方,就是"Bad Smell",需要優(yōu)化。。

    可以感覺到,如果我們直接能像在Visual Studio里面那樣,直接使用對(duì)象.屬性的方式來賦值,比如這里直接用textBoxCustomerName.Text,和bindingSource.CustomerName,來替代兩個(gè)手動(dòng)輸入的地方,整個(gè)體驗(yàn)就要好很多,比如,如果將上述代碼寫為下面的形式:

dataSource.CreateBinding(textBoxCustomerName, ctl => ctl.Text, data => data.Name);

 就要好很多。其實(shí)現(xiàn)方式為,我們先定義一個(gè)綁定的類,如下:

public class FlexBindingSource<TDataSource>
{
  private BindingSource _winFormsBindingSource;

  /// <summary>
  /// 
  /// </summary>
  /// <param name="source">object entity binding to the control</param>
  public FlexBindingSource(TDataSource source)
  {
    _winFormsBindingSource = new BindingSource();
    _winFormsBindingSource.DataSource = source;
  }

  /// <summary>
  /// Creates a DataBinding between a control property and a datasource property
  /// </summary>
  /// <typeparam name="TControl">The control type, must derive from System.Winforms.Control</typeparam>
  /// <param name="controlInstance">The control instance on wich the databinding will be added</param>
  /// <param name="controlPropertyAccessor">A lambda expression which specifies the control property to be databounded (something like textboxCtl => textboxCtl.Text)</param>
  /// <param name="datasourceMemberAccesor">A lambda expression which specifies the datasource property (something like datasource => datasource.Property) </param>
  /// <param name="dataSourceUpdateMode">See control.DataBindings.Add method </param>
  public void CreateBinding<TControl>(TControl controlInstance, Expression<Func<TControl, object>> controlPropertyAccessor, Expression<Func<TDataSource, object>> datasourceMemberAccesor, DataSourceUpdateMode dataSourceUpdateMode = DataSourceUpdateMode.OnValidation)
    where TControl : Control
  {
    string controlPropertyName = FlexReflector.GetPropertyName(controlPropertyAccessor);
    string sourcePropertyName = FlexReflector.GetPropertyName(datasourceMemberAccesor);
    controlInstance.DataBindings.Add(controlPropertyName, _winFormsBindingSource, sourcePropertyName, true, dataSourceUpdateMode);
  }
}

這里面,構(gòu)造函數(shù)里面的source,就是需要綁定的實(shí)體對(duì)象;在CreateBinding方法中,我們通過在FlexReflector在表達(dá)式中獲取待綁定的字符串,然后調(diào)用傳進(jìn)去的Control對(duì)象的綁定方法來實(shí)現(xiàn)綁定。FlexReflector方法內(nèi)容如下:

private static MemberExpression GetMemberExpression(Expression selector)
{
  LambdaExpression lambdaExpression = selector as LambdaExpression;
  if (lambdaExpression == null)
  {
    throw new ArgumentNullException("The selector is not a valid lambda expression.");
  }
  MemberExpression memberExpression = null;
  if (lambdaExpression.Body.NodeType == ExpressionType.Convert)
  {
    memberExpression = ((UnaryExpression)lambdaExpression.Body).Operand as MemberExpression;
  }
  else if (lambdaExpression.Body.NodeType == ExpressionType.MemberAccess)
  {
    memberExpression = lambdaExpression.Body as MemberExpression;
  }
  if (memberExpression == null)
  {
    throw new ArgumentException("The property is not a valid property to be extracted by a lambda expression.");
  }
  return memberExpression;
}

使用方法很簡(jiǎn)單,首先定義一個(gè)綁定源的實(shí)例。

private FlexBindingSource<CustomerInfo> dataSource;
dataSource = new FlexBindingSource<CustomerInfo>(customerInstance);

然后就可以使用CreateBinding方法直接綁定了。

dataSource.CreateBinding(textBoxCustomerName, ctl => ctl.Text, data => data.Name);

以上就是C#用表達(dá)式樹構(gòu)建動(dòng)態(tài)查詢的方法的詳細(xì)內(nèi)容,更多關(guān)于c# 構(gòu)建動(dòng)態(tài)查詢的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評(píng)論