{"id":13431301,"url":"https://github.com/scottksmith95/LINQKit","last_synced_at":"2025-03-16T11:31:27.474Z","repository":{"id":37735593,"uuid":"8484244","full_name":"scottksmith95/LINQKit","owner":"scottksmith95","description":"LINQKit is a free set of extensions for LINQ to SQL and Entity Framework power users.","archived":false,"fork":false,"pushed_at":"2024-06-15T15:46:55.000Z","size":3715,"stargazers_count":1632,"open_issues_count":44,"forks_count":162,"subscribers_count":53,"default_branch":"master","last_synced_at":"2024-10-29T17:51:30.075Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/scottksmith95.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2013-02-28T17:35:56.000Z","updated_at":"2024-10-29T07:40:42.000Z","dependencies_parsed_at":"2024-04-25T22:33:27.724Z","dependency_job_id":"11cb550b-1393-4f59-8565-1ae7073a60b8","html_url":"https://github.com/scottksmith95/LINQKit","commit_stats":{"total_commits":240,"total_committers":31,"mean_commits":7.741935483870968,"dds":0.5458333333333334,"last_synced_commit":"9092e0144925ec75c32f2616d633bbcb0a99be2c"},"previous_names":[],"tags_count":26,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/scottksmith95%2FLINQKit","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/scottksmith95%2FLINQKit/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/scottksmith95%2FLINQKit/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/scottksmith95%2FLINQKit/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/scottksmith95","download_url":"https://codeload.github.com/scottksmith95/LINQKit/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243862871,"owners_count":20360230,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-07-31T02:01:02.072Z","updated_at":"2025-03-16T11:31:27.466Z","avatar_url":"https://github.com/scottksmith95.png","language":"C#","funding_links":[],"categories":["Frameworks, Libraries and Tools","C\\#","Supported Packages","others","框架, 库和工具","Libraries","Popular Packages","C# #","ORM"],"sub_categories":["ORM","对象关系映射ORM","Extensions","Contents"],"readme":"### Build\r\n| | |\r\n|-|-|\r\n| AppVeyor | [![AppVeyor](https://ci.appveyor.com/api/projects/status/olv45n682ppmrlp7?svg=true)](https://ci.appveyor.com/project/Thorium/linqkit)\r\n| Github Actions | [![Actions](https://github.com/scottksmith95/LINQKit/workflows/Main%20workflow/badge.svg)](https://github.com/scottksmith95/LINQKit/actions?query=workflow%3A%22Main+workflow%22) |\r\n\r\n\r\n### Projects\r\n\r\n| Package | NuGet | Dependency | Frameworks |\r\n| ------- | ----- | -----------| ---------- | \r\n| LinqKit | [![Nuget](https://img.shields.io/nuget/v/LinqKit) ![Nuget](https://img.shields.io/nuget/dt/LinqKit)](https://www.nuget.org/packages/LinqKit) | EntityFramework\u003cul\u003e\u003cli\u003e≥ 6.2.0 (net45)\u003c/li\u003e\u003cli\u003e≥ 6.3.0 (netstandard2.1)\u003c/li\u003e\u003c/ul\u003e | \u003cul\u003e\u003cli\u003enet45 and up\u003c/li\u003e\u003cli\u003enetstandard2.1\u003c/li\u003e\u003c/ul\u003e |\r\n| LinqKit.Core | [![Nuget](https://img.shields.io/nuget/v/LinqKit.Core) ![Nuget](https://img.shields.io/nuget/dt/LinqKit.Core)](https://www.nuget.org/packages/LinqKit.Core) | - | \u003cul\u003e\u003cli\u003enet35\u003c/li\u003e\u003cli\u003enet40\u003c/li\u003e\u003cli\u003enet45 and up\u003c/li\u003e\u003cli\u003e.NETPortable Profile\u003c/li\u003e\u003cli\u003enetstandard1.3\u003c/li\u003e\u003cli\u003enetstandard2.0\u003c/li\u003e\u003cli\u003enetstandard2.1\u003c/li\u003e\u003cli\u003euap10\u003c/li\u003e\u003c/ul\u003e|\r\n| LinqKit.EntityFramework | [![Nuget](https://img.shields.io/nuget/v/LinqKit.EntityFramework) ![Nuget](https://img.shields.io/nuget/dt/LinqKit.EntityFramework)](https://www.nuget.org/packages/LinqKit.EntityFramework) | EntityFramework\u003cul\u003e\u003cli\u003e≥ 6.2.0 (net45)\u003c/li\u003e\u003cli\u003e≥ 6.3.0 (netstandard2.1) | \u003cul\u003e\u003cli\u003enet45 and up\u003c/li\u003e\u003cli\u003enetstandard2.1\u003c/li\u003e\u003c/ul\u003e |\r\n| LinqKit.Microsoft.EntityFrameworkCore 1 | [![Nuget](https://img.shields.io/badge/nuget-v1.3.6-blue) ![Nuget](https://img.shields.io/nuget/dt/LinqKit.Microsoft.EntityFrameworkCore)](https://www.nuget.org/packages/LinqKit.Microsoft.EntityFrameworkCore/1.3.6) | Microsoft.EntityFrameworkCore\u003cul\u003e\u003cli\u003e≥ 1.1.1\u003c/li\u003e\u003c/ul\u003e | \u003cul\u003e\u003cli\u003enet451\u003c/li\u003e\u003cli\u003enetstandard1.3\u003c/li\u003e\u003c/ul\u003e|\r\n| LinqKit.Microsoft.EntityFrameworkCore 2 | [![Nuget](https://img.shields.io/badge/nuget-v2.1.6-blue) ![Nuget](https://img.shields.io/nuget/dt/LinqKit.Microsoft.EntityFrameworkCore)](https://www.nuget.org/packages/LinqKit.Microsoft.EntityFrameworkCore/2.1.6) | Microsoft.EntityFrameworkCore\u003cul\u003e\u003cli\u003e≥ 2.0.1\u003c/li\u003e\u003c/ul\u003e | \u003cul\u003e\u003cli\u003enetstandard2.0\u003c/li\u003e\u003c/ul\u003e |\r\n| LinqKit.Microsoft.EntityFrameworkCore 3 | [![Nuget](https://img.shields.io/badge/nuget-v3.1.6-blue) ![Nuget](https://img.shields.io/nuget/dt/LinqKit.Microsoft.EntityFrameworkCore)](https://www.nuget.org/packages/LinqKit.Microsoft.EntityFrameworkCore/3.1.6) | Microsoft.EntityFrameworkCore\u003cul\u003e\u003cli\u003e≥ 3.0.1 (netstandard2.0)\u003c/li\u003e\u003cli\u003e≥ 3.0.0 (netstandard2.1) | \u003cul\u003e\u003cli\u003enetstandard2.0\u003c/li\u003e\u003cli\u003enetstandard2.1\u003c/li\u003e\u003c/ul\u003e|\r\n| LinqKit.Microsoft.EntityFrameworkCore 5 | [![Nuget](https://img.shields.io/badge/nuget-v5.1.6-blue) ![Nuget](https://img.shields.io/nuget/dt/LinqKit.Microsoft.EntityFrameworkCore)](https://www.nuget.org/packages/LinqKit.Microsoft.EntityFrameworkCore/5.1.6) | Microsoft.EntityFrameworkCore\u003cul\u003e\u003cli\u003e≥ 5.0.0\u003c/li\u003e\u003c/ul\u003e | \u003cul\u003e\u003cli\u003enetstandard2.1\u003c/li\u003e\u003c/ul\u003e|\r\n| LinqKit.Microsoft.EntityFrameworkCore 6 | [![Nuget](https://img.shields.io/badge/nuget-v6.1.6-blue) ![Nuget](https://img.shields.io/nuget/dt/LinqKit.Microsoft.EntityFrameworkCore)](https://www.nuget.org/packages/LinqKit.Microsoft.EntityFrameworkCore/6.1.6) | Microsoft.EntityFrameworkCore\u003cul\u003e\u003cli\u003e≥ 6.0.0\u003c/li\u003e\u003c/ul\u003e | \u003cul\u003e\u003cli\u003enet6.0\u003c/li\u003e\u003c/ul\u003e|\r\n| LinqKit.Microsoft.EntityFrameworkCore 7 | [![Nuget](https://img.shields.io/badge/nuget-v7.1.6-blue) ![Nuget](https://img.shields.io/nuget/dt/LinqKit.Microsoft.EntityFrameworkCore)](https://www.nuget.org/packages/LinqKit.Microsoft.EntityFrameworkCore/7.1.6) | Microsoft.EntityFrameworkCore\u003cul\u003e\u003cli\u003e≥ 7.0.0\u003c/li\u003e\u003c/ul\u003e | \u003cul\u003e\u003cli\u003enet6.0\u003c/li\u003e\u003cli\u003enet7.0\u003c/li\u003e\u003c/ul\u003e|\r\n| LinqKit.Microsoft.EntityFrameworkCore 8 | [![Nuget](https://img.shields.io/badge/nuget-v8.1.6-blue) ![Nuget](https://img.shields.io/nuget/dt/LinqKit.Microsoft.EntityFrameworkCore)](https://www.nuget.org/packages/LinqKit.Microsoft.EntityFrameworkCore/8.1.6) | Microsoft.EntityFrameworkCore\u003cul\u003e\u003cli\u003e≥ 8.0.0\u003c/li\u003e\u003c/ul\u003e | \u003cul\u003e\u003cli\u003enet8.0\u003c/li\u003e\u003c/ul\u003e|\r\n| LinqKit.Microsoft.EntityFrameworkCore 9 | [![Nuget](https://img.shields.io/badge/nuget-v9.0.6-blue) ![Nuget](https://img.shields.io/nuget/dt/LinqKit.Microsoft.EntityFrameworkCore)](https://www.nuget.org/packages/LinqKit.Microsoft.EntityFrameworkCore/9.0.6) | Microsoft.EntityFrameworkCore\u003cul\u003e\u003cli\u003e≥ 9.0.0\u003c/li\u003e\u003c/ul\u003e | \u003cul\u003e\u003cli\u003enet8.0\u003c/li\u003e\u003cli\u003enet9.0\u003c/li\u003e\u003c/ul\u003e|\r\n| LinqKit.Z.EntityFramework.Classic | [![Nuget](https://img.shields.io/nuget/v/LinqKit.Z.EntityFramework.Classic) ![Nuget](https://img.shields.io/nuget/dt/LinqKit.Z.EntityFramework.Classic)](https://www.nuget.org/packages/LinqKit.Z.EntityFramework.Classic) | Z.EntityFramework.Classic\u003cul\u003e\u003cli\u003e≥ 7.0.40\u003c/li\u003e\u003c/ul\u003e | \u003cul\u003e\u003cli\u003enet40\u003c/li\u003e\u003cli\u003enet45\u003c/li\u003e\u003cli\u003enetstandard2.0\u003c/li\u003e\u003c/ul\u003e|\r\n\r\n\r\nTable of Contents\r\n=======\r\n\r\n* [What is LINQKit?](#what-is-linqkit)\r\n* [Plugging Expressions into EntitySets / EntityCollections: The Problem](#plugging-expressions-into-entitysets--entitycollections-the-problem)\r\n* [Plugging Expressions into EntitySets / EntityCollections: The Solution](#plugging-expressions-into-entitysets--entitycollections-the-solution)\r\n* [Using Expression Variables in Subqueries](#using-expression-variables-in-subqueries)\r\n* [Combining Expressions](#combining-expressions)\r\n* [PredicateBuilder](#predicatebuilder)\r\n* [Complete Example, Getting Started...](#complete-example-getting-started)\r\n* [More optimized queries!](#more-optimized-queries)\r\n* [Original source and author](#original-source-and-author)\r\n\r\n\r\nWhat is LINQKit?\r\n=======\r\n\r\nLINQKit is a free set of extensions for LINQ to SQL and Entity Framework power users. It comprises the following:\r\n\r\n* An extensible implementation of AsExpandable()\r\n* A public expression visitor base class (ExpressionVisitor)\r\n* PredicateBuilder\r\n* Linq.Expr and Linq.Func shortcut methods\r\n\r\nWith LINQKit, you can:\r\n\r\n* Plug expressions into EntitySets and EntityCollections\r\n* Use expression variables in subqueries\r\n* Combine expressions (have one expression call another)\r\n* Dynamically build predicates\r\n* Leverage AsExpandable to add your own extensions.\r\n\r\nAsExpandable is based on a [very clever](http://tomasp.net/blog/linq-expand.aspx) project by Tomas Petricek. ExpressionVisitor comes from a [sample by Matt Warren](http://blogs.msdn.com/mattwar/archive/2007/07/31/linq-building-an-iqueryable-provider-part-ii.aspx).\r\n\r\nIf you like LINQKit, consider linking [this issue on github](https://github.com/aspnet/EntityFrameworkCore/issues/15670). \r\n\r\nPlugging Expressions into EntitySets / EntityCollections: The Problem\r\n=======\r\n\r\nWe'll assume two very simple LINQ to SQL entities: Customer and Purchase, in a one-to-many relationship. (Entity Framework works in exactly the same way in these examples).\r\n\r\nSuppose we wanted to write a method called QueryCustomers that returned the names of all customers who'd made at least one purchase that satisfied a particular criteria.  So, if we wanted all customers who'd made a purchase over $1000, we'd call this hypothetical QueryCustomer method as follows:\r\n\r\n```csharp\r\nstring[] bigSpenders = QueryCustomers (p =\u003e p.Price \u003e 1000);\r\n```\r\n\r\nHere's what the method's signature might look like:\r\n\r\n```csharp\r\nstatic string[] QueryCustomers (some-type purchaseCriteria)\r\n{\r\n   ...\r\n}\r\n```\r\n\r\nBecause we're querying a database, some-type must be Expression\u003cFunc\u003c\u003e\u003e rather than just Func\u003c\u003e. This ensures our query will end up as an expression tree that LINQ to SQL or Entity Framework can traverse and convert to a SQL statement. In other words:\r\n\r\n```csharp\r\nstatic string[] QueryCustomers (Expression\u003cFunc\u003cPurchase,bool\u003e\u003e purchaseCriteria)\r\n{\r\n   ...\r\n}\r\n```\r\n\r\nWe chose Expression\u003cFunc\u003cPurchase,bool\u003e\u003e because our pluggable criteria will accept a Purchase object and return true or false, depending on whether or not to include that Purchase object. Here's how we might write the whole method:\r\n\r\n```csharp\r\nstatic string[] QueryCustomers (Expression\u003cFunc\u003cPurchase, bool\u003e\u003e purchaseCriteria)\r\n{\r\n  var data = new MyDataContext();    // or MyObjectContext()\r\n\r\n  var query =\r\n    from c in data.Customers\r\n    where c.Purchases.Any(purchaseCriteria)  // will not compile\r\n    select c.Name;\r\n\r\n  return query.ToArray();\r\n}\r\n```\r\n\r\nBut there's a problem: Customer.Purchases is of type EntitySet\u003c\u003e (or EntityCollection\u003c\u003e with EF) neither of which implements IQueryable\u003c\u003e. This means that we can't call Queryable's Any method (the one that accepts an Expression\u003cFunc\u003c\u003e\u003e) and our query won't compile!\r\n\r\nIt would be a different story if we were querying the Purchases table directly (rather the via the Customer.Purchases association property). The Purchases property is of type Table\u003cPurchase\u003e which implements IQueryable, allowing us to do the following:\r\n\r\n```csharp\r\nbool any = data.Purchases.Any(purchaseCriteria);\r\n```\r\n\r\nOf course, we could rewrite QueryCustomers to accept a Func\u003cPurchase,bool\u003e instead:\r\n\r\n```csharp\r\nstatic string[] QueryCustomers (Func\u003cPurchase,bool\u003e purchaseCriteria)\r\n...\r\n```\r\n\r\nEverything would then compile, but LINQ to SQL or Entity Framework would throw an exception because it wouldn't be able to understand what was inside the Func delegate. And fair enough too: the query pipeline would have to disassemble IL code to work around that!\r\n\r\nPlugging Expressions into EntitySets / EntityCollections: The Solution\r\n=======\r\n\r\nHere's how to solve the above problem with LINQKit:\r\n\r\n1. Call AsExpandable() on the Table\u003c\u003e object\r\n2. Call Compile() on the expression variable, when used on an EntitySet or EntityCollection.\r\n \r\n```csharp\r\nstatic string[] QueryCustomers (Expression\u003cFunc\u003cPurchase, bool\u003e\u003e purchaseCriteria)\r\n{\r\n  var data = new MyDataContext();\r\n\r\n  var query =\r\n    from c in data.Customers.AsExpandable()\r\n    where c.Purchases.Any (purchaseCriteria.Compile())\r\n    select c.Name;\r\n\r\n  return query.ToArray();\r\n}\r\n````\r\n\r\nCompile is an inbuilt method in the Expression class. It converts the Expression\u003cFunc\u003cPurchase,bool\u003e into a plain Func\u003cPurchase,bool\u003e which satisfies the compiler. Of course, if this method actually ran, we'd end up with compiled IL code instead of an expression tree, and LINQ to SQL or Entity Framework would throw an exception. But here's the clever part: Compile never actually runs; nor does LINQ to SQL or Entity Framework ever get to see it. The call to Compile gets stripped out entirely by a special wrapper that was created by calling AsExpandable, and substituted for a correct expression tree.\r\n\r\nYou can find out more about how AsExpandable works in [Tomas Petricek's blog](http://tomasp.net/blog/linq-expand.aspx).\r\n\r\nUsing Expression Variables in Subqueries\r\n=======\r\n\r\nSuppose we want to write our previous example without using the Customer.Purchases association property. (This might happen in real life if querying an ad-hoc relationship.) To recap, our query is to retrieve the names of all customers who have had made at least one purchase satisfying a particular criteria. Here's how we might proceed:\r\n\r\n```csharp\r\nstatic string[] QueryCustomers (Expression\u003cFunc\u003cPurchase, bool\u003e\u003e purchaseCriteria)\r\n{\r\n  var data = new MyDataContext();\r\n\r\n  var query =\r\n    from c in data.Customers\r\n    let custPurchases = data.Purchases.Where (p =\u003e p.CustomerID == c.ID)\r\n    where custPurchases.Any (purchaseCriteria)\r\n    select c.Name;\r\n\r\n  return query.ToArray();\r\n}\r\n```\r\n\r\nSeem reasonable enough? Entity Framework handles this query without error but LINQ to SQL throws an exception:\r\n\r\n    Unsupported overload used for query operator 'Any'.\r\n\r\nThe problem is that LINQ to SQL cannot handle references to expressions (such as purchaseCriteria) within subqueries. \"But where is the subquery,\" you might ask! The answer lies in the compiler: C# generates a subquery when it translates the let clause into lambda/method syntax.\r\n\r\nThe solution, with LINQKit, is simply to call AsExpandable() on the first table in the query:\r\n\r\n```csharp\r\nstatic string[] QueryCustomers (Expression\u003cFunc\u003cPurchase, bool\u003e\u003e purchaseCriteria)\r\n{\r\n  var data = new MyDataContext();\r\n\r\n  var query =\r\n    from c in data.Customers.AsExpandable()\r\n    let custPurchases = data.Purchases.Where (p =\u003e p.CustomerID == c.ID)\r\n    where custPurchases.Any (purchaseCriteria)\r\n    select c.Name;\r\n\r\n  return query.ToArray();\r\n}\r\n```\r\n\r\nNothing else needs to be changed. The wrapper that AsExpandable generates looks specifically for references to expressions, and substitutes the expression in place of the reference. Voila!\r\n\r\nCombining Expressions\r\n=======\r\n\r\nThe AsExpandable wrapper also lets you write expressions that call other expressions. All you need to do is:\r\n\r\n1. Call Invoke to call the inner expression\r\n2. Call Expand on the final result.\r\n\r\nFor example:\r\n\r\n```csharp\r\nExpression\u003cFunc\u003cPurchase,bool\u003e\u003e criteria1 = p =\u003e p.Price \u003e 1000;\r\nExpression\u003cFunc\u003cPurchase,bool\u003e\u003e criteria2 = p =\u003e criteria1.Invoke (p) || p.Description.Contains (\"a\");\r\n\r\nConsole.WriteLine (criteria2.Expand().ToString());\r\n```\r\n\r\n(Invoke and Expand are extension methods in LINQKit.) Here's the output:\r\n\r\n```csharp\r\np =\u003e ((p.Price \u003e 1000) || p.Description.Contains(\"a\"))\r\n```\r\n\r\nNotice that we have a nice, clean expression: the call to Invoke has been stripped away.\r\n\r\nIf you're using an Invoked expression within a LINQ to SQL or Entity Framework query, and have called AsExpandable on the Table, you can optionally skip step 2. This is because AsExpandable automatically calls Expand on expressions. This means either of the following is valid:\r\n\r\n```csharp\r\nvar query = data.Purchases.AsExpandable().Where (criteria2);\r\n```\r\n\r\n```csharp\r\nvar query = data.Purchases.Where (criteria2.Expand());\r\n```\r\n\r\nBe sure to remember that AsExpandable() works on IQueryable\u003cT\u003e and Expand() works on Expression\u003cTDelegate\u003e\r\n\r\nThe one thing to watch is recursive expressions: these cannot be Expanded! Recursive expressions usually happen by accident when you reuse a variable. It's an easy mistake to make:\r\n\r\n```csharp\r\nExpression\u003cFunc\u003cPurchase,bool\u003e\u003e criteria = p =\u003e p.Price \u003e 1000;\r\ncriteria = p =\u003e criteria.Invoke (p) || p.Description.Contains (\"a\");\r\n```\r\n\r\nThat last line recursively calls itself and the original predicate (p.Price\u003e1000) is lost!\r\n\r\nPredicateBuilder\r\n=======\r\n\r\nWhen applying expressions built with PredicateBuilder to an Entity Framework query, remember to call AsExpandable on the first table in the query.\r\n\r\n## Dynamically Composing Expression Predicates\r\n\r\nSuppose you want to write a LINQ to SQL or Entity Framework query that implements a keyword-style search. In other words, a query that returns rows whose description contains some or all of a given set of keywords.\r\n\r\nWe can proceed as follows:\r\n\r\n```csharp\r\nIQueryable\u003cProduct\u003e SearchProducts (params string[] keywords)\r\n{\r\n  IQueryable\u003cProduct\u003e query = dataContext.Products;\r\n\r\n  foreach (string keyword in keywords)\r\n  {\r\n    string temp = keyword;\r\n    query = query.Where (p =\u003e p.Description.Contains (temp));\r\n  }\r\n  return query;\r\n}\r\n```\r\n\r\nThe temporary variable in the loop is required to avoid the outer variable trap, where the same variable is captured for each iteration of the foreach loop.\r\n\r\nSo far, so good. But this only handles the case where you want to match all of the specified keywords. Suppose instead, we wanted products whose description contains any of the supplied keywords. Our previous approach of chaining Where operators, is completely useless! We could instead chain Union operators, but this would be inefficient. The ideal approach is to dynamically construct a lambda expression tree that performs an or-based predicate.\r\n\r\nOf all the things that will drive you to manually construct expression trees, the need for dynamic predicates is the most common in a typical business application. Fortunately, it’s possible to write a set of simple and reusable extension methods that radically simplify this task. This is the role of our PredicateBuilder class.\r\n\r\n## Using PredicateBuilder\r\n\r\nHere's how to solve the preceding example with PredicateBuilder:\r\n\r\n```csharp\r\nIQueryable\u003cProduct\u003e SearchProducts (params string[] keywords)\r\n{\r\n  var predicate = PredicateBuilder.New\u003cProduct\u003e(true);\r\n\r\n  foreach (string keyword in keywords)\r\n  {\r\n    string temp = keyword;\r\n    predicate = predicate.And (p =\u003e p.Description.Contains (temp));\r\n  }\r\n  return dataContext.Products.Where (predicate);\r\n}\r\n```\r\n\r\n.. and to search for any keyword instead of all keywords (Or instead of And):\r\n\r\n```csharp\r\nIQueryable\u003cProduct\u003e SearchProducts (params string[] keywords)\r\n{\r\n  var predicate = PredicateBuilder.New\u003cProduct\u003e();\r\n\r\n  foreach (string keyword in keywords)\r\n  {\r\n    string temp = keyword;\r\n    predicate = predicate.Or (p =\u003e p.Description.Contains (temp));\r\n  }\r\n  return dataContext.Products.Where (predicate);\r\n}\r\n```\r\n\r\nIf querying with Entity Framework, change the last line to this:\r\n\r\n```csharp\r\nreturn objectContext.Products.AsExpandable().Where (predicate);\r\n```\r\n\r\n## How it Works\r\n\r\n\r\n~~The True and False methods do nothing special: they are simply convenient shortcuts for creating an Expression\u003cFunc\u003cT,bool\u003e\u003e that initially evaluates to true or false.~~\r\n\r\nPredicateBuilder.New() creates an object called ExpressionStarter\u003cT\u003e, which acts for all intents and purposes as an Expression\u003cFunc\u003cT, bool\u003e\u003e object. \r\n\r\nSo the following:\r\n\r\n```csharp\r\nvar predicate = PredicateBuilder.New\u003cProduct\u003e();\r\n```\r\n\r\nWould be a shortcut for this:\r\n\r\n```csharp\r\nExpression\u003cFunc\u003cProduct, bool\u003e\u003e predicate = c =\u003e false;\r\n```\r\n\r\nHowever, a default we don't want a stub expression. In Entity Framework, this would result in a query having a where statement starting with 1=0, so if you were checking that value = 'abc', the query's where clause would look as follows;\r\n\r\n```\r\nWHERE 1=0 OR value = 'abc'\r\n```\r\n\r\nExpressionStarter fixes this. As soon as the first expression is added to ExpressionStarter, the default expression is removed. You can add the first expression by calling ExpressionStarter's Start method. However, calling Start is not required. If no expression has been added to the ExpressionStarter, then calling And or Or will simply add the first expression. This is useful when using loops.\r\n\r\n~~When you’re building a predicate by repeatedly stacking and/or conditions, it’s useful to have a starting point of either true or false (respectively). Our SearchProducts method still works if no keywords are supplied.~~\r\n\r\nThe interesting work takes place inside the And and Or methods. We start by invoking the second expression with the first expression’s parameters. An Invoke expression calls another lambda expression using the given expressions as arguments. We can create the conditional expression from the body of the first expression and the invoked version of the second. The final step is to wrap this in a new lambda expression.\r\n\r\nEntity Framework's query processing pipeline cannot handle invocation expressions, which is why you need to call AsExpandable on the first object in the query. By calling AsExpandable, you activate LINQKit's expression visitor class which substitutes invocation expressions with simpler constructs that Entity Framework can understand.\r\n\r\n## More Examples\r\n\r\nA useful pattern in writing a data access layer is to create a reusable predicate library. Your queries, then, consist largely of select and orderby clauses, the filtering logic farmed out to your library. Here's a simple example:\r\n\r\n```csharp\r\npublic partial class Product\r\n{\r\n  public static Expression\u003cFunc\u003cProduct, bool\u003e\u003e IsSelling()\r\n  {\r\n    return p =\u003e !p.Discontinued \u0026\u0026 p.LastSale \u003e DateTime.Now.AddDays (-30);\r\n  }\r\n}\r\n```\r\n\r\nWe can extend this by adding a method that uses PredicateBuilder:\r\n\r\n``` csharp\r\npublic partial class Product\r\n{\r\n  public static Expression\u003cFunc\u003cProduct, bool\u003e\u003e ContainsInDescription (params string[] keywords)\r\n  {\r\n    var predicate = PredicateBuilder.New\u003cProduct\u003e();\r\n    foreach (string keyword in keywords)\r\n    {\r\n      string temp = keyword;\r\n      predicate = predicate.Or (p =\u003e p.Description.Contains (temp));\r\n    }\r\n    return predicate;\r\n  }\r\n}\r\n```\r\n\r\nThis offers an excellent balance of simplicity and reusability, as well as separating business logic from expression plumbing logic. \r\n\r\nNotice that in the above query, we didn't have to call Start, as the first call to Or will Start the ExpressionStarter for us. Also, notice that even though 'predicate' is a type ExpressionStarter\u003cProduct\u003e, we can return it just fine even though the return method is an Expression\u003cFunc\u003cProduct, bool\u003e\u003e. ExpressionStarter has an implicit conversion operator that allows it to act like an Expression\u003cFunc\u003cT, bool\u003e\u003e.\r\n\r\nTo retrieve all products whose description contains “BlackBerry” or “iPhone”, along with the Nokias and Ericssons that are selling, you would do this:\r\n\r\n```csharp\r\nvar newKids  = Product.ContainsInDescription (\"BlackBerry\", \"iPhone\");\r\n\r\nvar classics = Product.ContainsInDescription (\"Nokia\", \"Ericsson\")\r\n                      .And (Product.IsSelling());\r\nvar query =\r\n  from p in Data.Products.Where (newKids.Or (classics))\r\n  select p;\r\n```\r\n\r\nThe And and Or methods in boldface resolve to extension methods in PredicateBuilder.\r\n\r\nAn expression predicate can perform the equivalent of an SQL subquery by referencing association properties. So, if Product had a child EntitySet called Purchases, we could refine our IsSelling method to return only those products that have sold a minimum number of units as follows:\r\n\r\n```csharp\r\npublic static Expression\u003cFunc\u003cProduct, bool\u003e\u003e IsSelling (int minPurchases)\r\n{\r\n  return prod =\u003e\r\n    !prod.Discontinued \u0026\u0026\r\n     prod.Purchases.Where (purch =\u003e purch.Date \u003e DateTime.Now.AddDays(-30))\r\n                    .Count() \u003e= minPurchases;\r\n}\r\n```\r\n\r\n## Nesting Predicates\r\n\r\nConsider the following predicate:\r\n\r\n```csharp\r\np =\u003e p.Price \u003e 100 \u0026\u0026\r\n     p.Price \u003c 1000 \u0026\u0026\r\n     (p.Description.Contains (\"foo\") || p.Description.Contains (\"far\"))\r\n```\r\n\r\nLet's say we wanted to build this dynamically. The question is, how do we deal with the parenthesis around the two expressions in the last line?\r\n\r\nThe answer is to build the parenthesised expression first, and then consume it in the outer expression as follows:\r\n\r\n```csharp\r\nvar inner = PredicateBuilder.New\u003cProduct\u003e();\r\ninner = inner.Start(p =\u003e p.Description.Contains (\"foo\"));\r\ninner = inner.Or(p =\u003e p.Description.Contains (\"far\"));\r\n\r\nvar outer = PredicateBuilder.New\u003cProduct\u003e();\r\nouter = outer.Start(p =\u003e p.Price \u003e 100);\r\nouter = outer.And(p =\u003e p.Price \u003c 1000);\r\nouter = outer.And(inner);\r\n```\r\n\r\n~~Notice that with the inner expression, we start with PredicateBuilder.False (because we're using the Or operator). With the outer expression, however, we start with PredicateBuilder.True (because we're using the And operator).~~\r\n\r\n## Generic Predicates\r\n\r\nSuppose every table in your database has ValidFrom and ValidTo columns as follows:\r\n\r\n```\r\ncreate table PriceList\r\n(\r\n   ID int not null primary key,\r\n   Name nvarchar(50) not null,\r\n   ValidFrom datetime,\r\n   ValidTo datetime\r\n)\r\n```\r\n\r\nTo retrieve rows valid as of DateTime.Now (the most common case), you'd do this:\r\n\r\n```\r\nfrom p in PriceLists\r\nwhere (p.ValidFrom == null || p.ValidFrom \u003c= DateTime.Now) \u0026\u0026\r\n      (p.ValidTo   == null || p.ValidTo   \u003e= DateTime.Now)\r\nselect p.Name\r\n```\r\n\r\nOf course, that logic in bold is likely to be duplicated across multiple queries! No problem: let's define a method in the PriceList class that returns a reusable expression:\r\n\r\n```csharp\r\npublic static Expression\u003cFunc\u003cPriceList, bool\u003e\u003e IsCurrent()\r\n{\r\n   return p =\u003e (p.ValidFrom == null || p.ValidFrom \u003c= DateTime.Now) \u0026\u0026\r\n               (p.ValidTo   == null || p.ValidTo   \u003e= DateTime.Now);\r\n}\r\n```\r\n\r\nOK: our query is now much simpler:\r\n\r\n```csharp\r\nvar currentPriceLists = db.PriceLists.Where (PriceList.IsCurrent());\r\n```\r\n\r\nAnd with PredicateBuilder's And and Or methods, we can easily introduce other conditions:\r\n\r\n```csharp\r\nvar currentPriceLists = db.PriceLists.Where (\r\n                          PriceList.IsCurrent().And (p =\u003e p.Name.StartsWith (\"A\")));\r\n```\r\n\r\nBut what about all the other tables that also have ValidFrom and ValidTo columns? We don't want to repeat our IsCurrent method for every table! Fortunately, we can generalize our IsCurrent method with generics.\r\n\r\nThe first step is to define an interface:\r\n\r\n```csharp\r\npublic interface IValidFromTo\r\n{\r\n   DateTime? ValidFrom { get; }\r\n   DateTime? ValidTo   { get; }\r\n}\r\n```\r\n\r\nNow we can define a single generic IsCurrent method using that interface as a constraint:\r\n\r\n```csharp\r\npublic static Expression\u003cFunc\u003cTEntity, bool\u003e\u003e IsCurrent\u003cTEntity\u003e()\r\n   where TEntity : IValidFromTo\r\n{\r\n   return e =\u003e (e.ValidFrom == null || e.ValidFrom \u003c= DateTime.Now) \u0026\u0026\r\n               (e.ValidTo   == null || e.ValidTo   \u003e= DateTime.Now);\r\n}\r\n```\r\n\r\nThe final step is to implement this interface in each class that supports ValidFrom and ValidTo. If you're using Visual Studio or a tool like SqlMetal to generate your entity classes, do this in the non-generated half of the partial classes:\r\n\r\n```csharp\r\npublic partial class PriceList : IValidFromTo { }\r\npublic partial class Product   : IValidFromTo { }\r\n```\r\n\r\nComplete Example, Getting Started...\r\n=======\r\n\r\nCreate a database, let's say MyDatabase to your SQL server with a script:\r\n\r\n```sql\r\nCREATE TABLE [dbo].[Orders](\r\n\tId int NOT NULL IDENTITY (1, 1),\r\n\tAmount int NOT NULL,\r\n\tOrderDate smalldatetime NOT NULL\r\n\t) ON [PRIMARY]\r\nGO\r\nALTER TABLE [dbo].[Orders] ADD CONSTRAINT\r\n\tPK_Table_1 PRIMARY KEY CLUSTERED (Id) \r\n\tWITH( STATISTICS_NORECOMPUTE = OFF, \r\n\t\t\tIGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, \r\n\t\t\tALLOW_PAGE_LOCKS = ON) ON [PRIMARY]\r\nGO\r\n-- Insert some demo data:\r\nINSERT INTO [dbo].[Orders]([Amount],[OrderDate]) \r\n\tVALUES (3, '2016-01-01')\r\nINSERT INTO [dbo].[Orders]([Amount],[OrderDate]) \r\n\tVALUES (5, '2016-01-01')\r\nINSERT INTO [dbo].[Orders]([Amount],[OrderDate]) \r\n\tVALUES (7, '2016-01-02')\r\nGO\r\n```\r\n\r\nThen, create a new C# console application. Add references to Nuget packages `EntityFramework` and `LinqKit`.\r\n\r\n```csharp\r\nusing System;\r\nusing System.Collections.Generic;\r\nusing System.ComponentModel.DataAnnotations;\r\nusing System.Data.Entity;\r\nusing System.Linq;\r\nusing System.Linq.Expressions;\r\nusing LinqKit;\r\n\r\npublic class Order\r\n{\r\n    [Key]\r\n    public int Id { get; set; }\r\n\r\n    [Required]\r\n    public int Amount { get; set; }\r\n\r\n    [Required]\r\n    public DateTime OrderDate { get; set; }\r\n}\r\n\r\n/// \u003csummary\u003e Some simple EF DBContext item for this example\u003c/summary\u003e\r\npublic class MyDbContext : DbContext\r\n{\r\n    static MyDbContext() { Database.SetInitializer\u003cMyDbContext\u003e(null); }\r\n    public MyDbContext() : base(\r\n\t\t\"Server=localhost;Database=MyDatabase;Integrated Security = True;\"){}\r\n    public DbSet\u003cOrder\u003e Orders { get; set; }\r\n}\r\n\r\nclass Program\r\n{\r\n    static void Main(string[] args)\r\n    {\r\n        Expression\u003cFunc\u003cIQueryable\u003cOrder\u003e, decimal?\u003e\u003e expression = \r\n            orders =\u003e orders.Average(o =\u003e (decimal?)o.Amount);\r\n\r\n        using (var context = new MyDbContext())\r\n        {\r\n            IQueryable\u003cOrder\u003e orders = context.Orders;\r\n            var q = from o in orders.AsExpandable()\r\n                    group o by o.OrderDate into g\r\n                    select new\r\n                    {\r\n                        OrderDate = g.Key,\r\n                        AggregatedAmount = expression.Invoke(g.AsQueryable())\r\n                    };\r\n            //ToList or ToListAsync:\r\n            q.ToList().ForEach(Console.WriteLine);\r\n            Console.ReadLine();\r\n        }\r\n    }\r\n}\r\n```\r\nRun. Observe with the SQL profiler that your `expression` is coming outside the EF-context but still executed to the SQL query. There are good tutorial videos and materials on SQL profiling on the internet, and the profiling is highly recommended. SQL Server Management Studio includes SQL Server Profiler.\r\n\r\nMore optimized queries!\r\n=======\r\n\r\nIf you have a lot of logics in your queries, like enterprise applications usually have, let's say for example:\r\n\r\n```csharp\r\n// simulate some dynamic non-database-parameter\r\nvar t = DateTime.Now.Month % 3; \r\n\r\nvar qry1 =\r\n    from o in context.Orders.AsExpandable()\r\n    let myTemp = \r\n        t == 1 ? o.Amount + 10 :\r\n        t == 2 ? o.Amount - 10 :\r\n        o.Amount\r\n    select new\r\n    {\r\n        OrderDate = o.OrderDate,\r\n        FixedAmount = myTemp\r\n    };\r\n\r\nvar qry2 = \r\n    from x in qry1\r\n    where x.FixedAmount \u003c 100\r\n    select x;\r\n\r\nvar res = qry2.ToList();\r\n```\r\n\r\nThis creates query:\r\n\r\n```sql\r\nexec sp_executesql N'\r\nSELECT \r\n    [Extent1].[Amount] AS [Amount], \r\n    [Extent1].[OrderDate] AS [OrderDate], \r\n    CASE WHEN (1 = @p__linq__0) THEN [Extent1].[Amount] + 10 \r\n         WHEN (2 = @p__linq__1) THEN [Extent1].[Amount] - 10 \r\n         ELSE [Extent1].[Amount] END AS [C1]\r\n    FROM [dbo].[Orders] AS [Extent1]\r\n    WHERE (CASE WHEN (1 = @p__linq__0) \r\n         THEN [Extent1].[Amount] + 10 \r\n         WHEN (2 = @p__linq__1) THEN [Extent1].[Amount] - 10 \r\n         ELSE [Extent1].[Amount] END) \u003c 100\r\n',N'@p__linq__0 int,@p__linq__1 int',@p__linq__0=2,@p__linq__1=2\r\n```\r\n\r\nAs you noticed, there are a lot of dynamic parameters. This is good if the parameters vary a lot, but here, they are pretty static, so the SQL server will not be able to perform all caching optimizations. We could optimize away these variables by runtime when LinqKit forms the query.\r\n\r\nThere is a project called [Linq.Expression.Optimizer](https://thorium.github.io/Linq.Expression.Optimizer/) and it is supported by LinqKit.\r\nInstall this Nuget package (and add a reference to F#-core library if required).\r\n\r\n### Use the static option (all calls)\r\nMake this static call once before executing your queries (e.g. to your app startup or static class constructor or Application_Start):\r\n\r\n```csharp\r\nLinqKitExtension.QueryOptimizer = ExpressionOptimizer.visit;\r\n```\r\n\r\nAnd run your query as usual. Observe the difference, now the same query is:\r\n\r\n```sql\r\nSELECT \r\n    [Extent1].[Amount] AS [Amount], \r\n    [Extent1].[OrderDate] AS [OrderDate], \r\n    [Extent1].[Amount] - 10 AS [C1]\r\n    FROM [dbo].[Orders] AS [Extent1]\r\n    WHERE ([Extent1].[Amount] - 10) \u003c 100\r\n```\r\n\r\n### Use the dynamic option (each call separate)\r\nIt's also possible to use the expression optimizer for specific calls only.\r\n\r\n``` csharp\r\n// define the optimizer you want to use\r\nvar optimizer = ExpressionOptimizer.visit;\r\n\r\n// simulate some dynamic non-database-parameter\r\nvar t = DateTime.Now.Month % 3; \r\n\r\n// provide the optimizer in the AsExpandable call\r\nvar qry1 =\r\n    from o in context.Orders.AsExpandable(optimizer)\r\n    let myTemp = \r\n        t == 1 ? o.Amount + 10 :\r\n        t == 2 ? o.Amount - 10 :\r\n        o.Amount\r\n    select new\r\n    {\r\n        OrderDate = o.OrderDate,\r\n        FixedAmount = myTemp\r\n    };\r\n\r\nvar qry2 = \r\n    from x in qry1\r\n    where x.FixedAmount \u003c 100\r\n    select x;\r\n\r\nvar res = qry2.ToList();\r\n```\r\n\r\nNote that if your IQueryable has dynamic parameters from other IQueryables, it can still be complex.\r\n\r\nOriginal source and author\r\n=======\r\n\r\nhttp://www.albahari.com/nutshell/linqkit.aspx\r\n\r\nPermission has been granted to have this repo be the official source for this project.\r\n\r\nContributing\r\n=======\r\nJust send PullRequests to this repository.\r\nTo compile the whole solution you may need .NET Core and UAP installed.\r\n\r\nLicense\r\n=======\r\nLINQKit is free. The source code is issued under a permissive free license, which means you can modify it as you please, and incorporate it into your own commercial or non-commercial software.\r\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fscottksmith95%2FLINQKit","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fscottksmith95%2FLINQKit","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fscottksmith95%2FLINQKit/lists"}