. NET in-depth practical series -- how does EF write filter conditions?,. net -- ef

Source: Internet
Author: User

. NET in-depth practical series -- how does EF write filter conditions?,. net -- ef

Unique access address for this article: http://www.cnblogs.com/yubaolee/p/DynamicLinq.html

For system development, filtering by different fields is a common requirement. In EF, the common practice is:

/// <Summary> /// just a simple example. Only two filtering conditions are used: // </summary> IEnumerable <UserInfo> Search (string username = "", string usertype = "") {var query = _ context. userInfoes. asQueryable (); if (! String. IsNullOrEmpty (username) query = query. Where (u => u. UserName = username); if (! String. IsNullOrEmpty (usertype) query = query. Where (u => u. UserType = usertype); return query. ToList ();}

In this case, if I have a new requirement, for example, to query users whose usernames must contain an indefinite number of keywords. Then we can use the parameter array to perform upgrades similar to the following.

  private IEnumerable<UserInfo> Search(params string[] keys)        {            var query = _context.UserInfoes.AsQueryable();            foreach (var key in keys)            {                query = query.Where(u => u.UserName.Contains(key));            }            return query.ToList();        }

The above code works well. If the requirement becomes: what should we do if the query username contains at least one keyword? It is obvious that the Or operation is used, but how is it most reasonable? Normal queries cannot solve this problem. So Joe Albahari used PredicateBuilder in his blog to easily solve the problem:

   IQueryable<UserInfo> Search(params string[] keys)        {            var predicate = PredicateBuilder.False<UserInfo>();            foreach (string keyword in keys)            {                predicate = predicate.Or(p => p.UserName.Contains(keyword));            }            return _context.UserInfoes.Where(predicate);        }

For the implementation of PredicateBuilder, you can view it in his blog or directly search for and add LINQKit references in nuget. PredicateBuilder can solve the problem of dynamically generating Lambda, And supports And/Or And other mainstream operations. However, it still fails to solve a problem: If the attributes in the query conditions (that is, the fields in the database) are also uncertain, what should we do?

At this moment, Scott stood up. In his blog Dynamic LINQ (Part 1: Using the LINQ Dynamic Query Library), he integrated EF into a join SQL method to achieve this requirement. As follows:

  private IQueryable<UserInfo> Search(string key, string value)        {            return _context.UserInfoes.Where("@0 ='@1'", key, value);        }

In this way, we are not afraid of the need for endless changes. We can call any of them:

Var users = Search ("UserNmae", "yubaolee"); // filter username var users2 = Search ("UserType", "administrator"); // filter user types

You can also use Key-Value and other combinations to form more powerful query functions. However, things in the world are not so beautiful. You will use it in practice to discover that you don't even support like, and use it to discover that you don't even support guids.

Alas! I am here. I have never used it before, and I will encounter such a bird thing. Do it yourself!

Well, the following is the main content of the blog post. If you don't see the following content, thank you! Unfortunately...

 

Analyze our requirements:

Based on the above requirements, we can refer to the implementation method of PredicateBuilder, use the expression tree to generate dynamic lambda, and then pass it to the filter conditions of ef. As follows:

Public class Filter {public string Key {get; set;} // The filtered keyword public string Value {get; set;} // The filtered Value public string Contract {get; set;} // filter constraints such: '<''<=''> = ''like' and so on} public static class DynamicLinq {// <summary> // create parameters in lambda, c => c. c // in xxx = xx </summary> public static ParameterExpression CreateLambdaParam <T> (string name) {return Expression. parameter (typeof (T), name) ;}/// <summary> // create the body section of the linq expression, that is, c => c. c. In xxx = xx. xxx = xx // </summary> public static Expression GenerateBody <T> (this ParameterExpression param, Filter filterObj) {PropertyInfo property = typeof (T ). getProperty (filterObj. key); // assemble the left Expression left = Expression. property (param, property); // assemble Expression right = null on the right; // todo: extend the type of if (property. propertyType = typeof (int) {right = Expression. constant (int. parse (filterObj. value);} else if (property. propertyType = typeof (DateTime) {right = Expression. constant (DateTime. parse (filterObj. value);} else if (property. propertyType = typeof (string) {right = Expression. constant (filterObj. value);} else if (property. propertyType = typeof (decimal) {right = Expression. constant (decimal. parse (filterObj. value);} else if (property. propertyType = typeof (Guid) {right = Expression. constant (Guid. parse (filterObj. value);} else if (property. propertyType = typeof (bool) {right = Expression. constant (filterObj. value. equals ("1");} else {throw new Exception ("the type of the Key cannot be parsed");} // todo: the following compares Expression filter = Expression according to your needs. equal (left, right); switch (filterObj. contract) {case "<=": filter = Expression. lessThanOrEqual (left, right); break; case "<": filter = Expression. lessThan (left, right); break; case ">": filter = Expression. greaterThan (left, right); break; case "> =": filter = Expression. greaterThanOrEqual (left, right); break; case "like": filter = Expression. call (left, typeof (string ). getMethod ("Contains", new [] {typeof (string)}), Expression. constant (filterObj. value); break;} return filter;} // <summary> // create a complete lambda, that is, c => c. xxx = xx // </summary> public static LambdaExpression GenerateLambda (this ParameterExpression param, Expression body) {return Expression. lambda (body, param) ;}/// <summary> /// create a complete lambda, to be compatible with the where statement in EF /// </summary> public static Expression <Func <T, bool> GenerateTypeLambda <T> (this ParameterExpression param, Expression body) {return (Expression <Func <T, bool>) (param. generatelamso (body);} public static Expression AndAlso (this Expression expression, Expression expressionRight) {return Expression. andAlso (expression, expressionRight);} public static Expression Or (this Expression expression, Expression expressionRight) {return Expression. or (expression, expressionRight);} public static Expression And (this Expression expression, Expression expressionRight) {return Expression. and (expression, expressionRight );}}

Let's take a look at our client call:

// Simulate the Filter object var filters = new Filter [] {new Filter {Key = "UserName", Value = "yubaolee", Contract = "like "}, new Filter {Key = "UserType", Value = "administrator", Contract = "=" }}; var param = DynamicLinq. createLambdaParam <UserInfo> ("c"); Expression body = Expression. constant (true); // The default value is true foreach (var filter in filters) {body = body. andAlso (param. generateBody <UserInfo> (filter); // You can freely combine them as needed} var lambda = param. generatetypelamers <UserInfo> (body); // The final lambda var users = _ context. userInfoes. where (lambda); // obtain the final result Console. read ();

At this time, we can combine them freely, but the amount of client code seems to be a lot. We will optimize the encapsulation:

Public static class DynamicExtention {public static IQueryable <T> Where <T> (this IQueryable <T> query, Filter [] filters) {var param = DynamicLinq. createLambdaParam <T> ("c"); Expression body = Expression. constant (true); // The default value is true foreach (var filter in filters) {body = body. andAlso (param. generateBody <T> (filter); // You can freely combine them as needed} var lambda = param. generateTypeLambda <T> (body); // The final lambda return query. where (lambda );}}

Finally, let's look at our client call:

// Simulate the Filter object var filters = new Filter [] {new Filter {Key = "UserName", Value = "yubaolee", Contract = "like "}, new Filter {Key = "UserType", Value = "administrator", Contract = "=" }}; var users = _ context. userInfoes. where (filters); // obtain the final result Console. read ();

The code is so clean and tidy. Because the expanded Where statement is based on generics, you can use it directly regardless of the DbSet of your EF set. If you further deepen the Filter function and extend it to a tree structure, you can implement a combined query. Even join table queries are not supported.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.