{"id":13458282,"url":"https://github.com/zzzprojects/EntityFramework.DynamicFilters","last_synced_at":"2025-03-24T15:31:02.127Z","repository":{"id":45563186,"uuid":"22524490","full_name":"zzzprojects/EntityFramework.DynamicFilters","owner":"zzzprojects","description":"Global filtering for Entity Framework.","archived":false,"fork":false,"pushed_at":"2024-03-20T20:51:09.000Z","size":686,"stargazers_count":499,"open_issues_count":23,"forks_count":85,"subscribers_count":42,"default_branch":"master","last_synced_at":"2025-03-23T22:14:03.740Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://entityframework-dynamicfilters.net/","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/zzzprojects.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":".github/FUNDING.yml","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},"funding":{"github":["zzzprojects"],"custom":["https://zzzprojects.com/contribute"]}},"created_at":"2014-08-01T19:17:28.000Z","updated_at":"2024-12-14T15:34:09.000Z","dependencies_parsed_at":"2024-07-31T09:09:06.963Z","dependency_job_id":"56c43b56-7355-472b-bb80-9415e7eebf00","html_url":"https://github.com/zzzprojects/EntityFramework.DynamicFilters","commit_stats":{"total_commits":189,"total_committers":12,"mean_commits":15.75,"dds":0.4920634920634921,"last_synced_commit":"3706cebde46fe118cb2b2a6c94babf1aef427b21"},"previous_names":[],"tags_count":11,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zzzprojects%2FEntityFramework.DynamicFilters","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zzzprojects%2FEntityFramework.DynamicFilters/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zzzprojects%2FEntityFramework.DynamicFilters/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zzzprojects%2FEntityFramework.DynamicFilters/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/zzzprojects","download_url":"https://codeload.github.com/zzzprojects/EntityFramework.DynamicFilters/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":245175475,"owners_count":20572787,"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-31T09:00:49.579Z","updated_at":"2025-03-24T15:31:02.089Z","avatar_url":"https://github.com/zzzprojects.png","language":"C#","funding_links":["https://github.com/sponsors/zzzprojects","https://zzzprojects.com/contribute"],"categories":["C\\#","Supported Packages"],"sub_categories":[],"readme":"## Library Powered By\n\nThis library is powered by [Entity Framework Extensions](https://entityframework-extensions.net/?z=github\u0026y=entityframework-dynamicfilters)\n\n\u003ca href=\"https://entityframework-extensions.net/?z=github\u0026y=entityframework-dynamicfilters\"\u003e\n\u003ckbd\u003e\n\u003cimg src=\"https://zzzprojects.github.io/images/logo/entityframework-extensions-pub.jpg\" alt=\"Entity Framework Extensions\" /\u003e\n\u003c/kbd\u003e\n\u003c/a\u003e\n\n# What's Entity Framework Dynamic Filters?\n\nCreate global and scoped filters for Entity Framework queries.  The filters are automatically applied to every query and can be used to support use cases such as Multi-Tenancy, Soft Deletes, Active/Inactive, etc.\n\nFilters can be created using boolean LINQ expressions and also support the Contains() operator.\n\nAccess to DynamicFilters is done via extension methods in the EntityFramework.DynamicFilters namespace on the DbContext and DbModelBuilder classes.\n\nSupports MS SQL Server (including Azure), MySQL, Oracle (*see notes below), and PostgreSQL.\n\n## Do it support EF Core?\n\n**NO**, it doesn't support EF Core and there is no plan to support it.\n\nEF Core already have their one global query filter that will eventually be evolved in [Named query filter](https://github.com/dotnet/efcore/issues/8576)\n\n## Changes in Version 2\n* Added support for creating filters that reference child classes/navigation properties.  See [Issue #65](https://github.com/jcachat/EntityFramework.DynamicFilters/issues/65) for more details.  Requires that FK properties are defined on the models.  It also includes support for Any() and All() on child collections.\n* Filter parameter values can now reference the current DbContext instance.  See [Parameter Expressions](#parameter-expressions).\n* Filters can be enabled conditionally via a delegate, just like parameter values.  See [Conditionally Enabling Filters](#conditionally-enabling-filters).\n* Added options to filters to control how/when they are applied to entities.  See [Filter Options](#filter-options)\n\nPutting this all together, you can now do the following:\n```csharp\nmodelBuilder.Filter(\"NotesForCompany\", (Note n, int orgID) =\u003e n.Person.OrganizationID==orgID, (MyContext ctx) =\u003e ctx.CurrentOrganizationID);\nmodelBuilder.EnableFilter(\"UserOrg\", (MyContext ctx) =\u003e !ctx.UserIsAdmin);\n```\nThis will create a filter to restrict queries on the Notes table to only those records made by people in the current users Organization.  The filter will only be enabled if the current user is not an Administrator.  And both expressions access properties in the current DbContext instance.\n\n\n## Defining Filters\n\nFilters are defined in DbContext.OnModelCreating().  \n\nFilters should always follow any other model configuration - including the call to the base.OnModelCreating() method.  It is best to make the filter definitions the final step of OnModelCreating() to make sure that they are not in effect until the entire model is fully configured.\n\nAll filters have global scope and will be used by all DbContexts.  Each DbContext can also choose to provide a \"scoped\" filter value or can disable the filter via the DisableFilter() extension method.  Scoped parameter changes and filter disabling will apply only to that DbContext and do not affect any existing or future DbContexts.\n\nFilters can be defined on a specific entity class or an interface.  Below is an example of a \"soft delete\" filter created on an ISoftDelete interface.  This filter will apply to any entity that implements ISoftDelete and will automatically filter those entities by applying the condition \"IsDeleted==false\".\n\n```csharp\nmodelBuilder.Filter(\"IsDeleted\", (ISoftDelete d) =\u003e d.IsDeleted, false);\n```\n\nFilter values can also be provided via a delegate/expression instead of a specific value (as shown in the above example).  This can allow you to vary the parameter value dynamically.  For example, a filter can be created on the UserID and be provided per http request.  Below is an example that obtains a \"Person ID\" from the Thread.CurrentPrincipal.  This delegate will be evaluated each time the query is executed, so it will obtain the \"Person ID\" associated with each request.\n```csharp\nmodelBuilder.Filter(\"Notes_CurrentUser\", (Note n) =\u003e n.PersonID, () =\u003e GetPersonIDFromPrincipal(Thread.CurrentPrincipal));\n```\nIn this example, the Note entity is \"owned\" by the current user.  This filter will ensure that all queries made for Note entities will always be restricted to the current user, and it will not be possible for users to retrieve notes for other users.\n\n### Parameter Expressions\nAs of Version 2, parameter delegate expressions can be specified as either a `Func\u003cobject\u003e` (as shown above) or a `Func\u003cDbContext, object\u003e` like this:\n```csharp\nmodelBuilder.Filter(\"Notes_CurrentUser\", (Note n) =\u003e n.PersonID, (MyContext ctx) =\u003e ctx.CurrentPersonID);\n```\nThis allows the parameter value expressions to reference the current DbContext instance.  In this example, the value of the parameter will be set to the value of the CurrentPersonID property in the current MyContext instance.\n\n## Linq Filters\n\nFilters can also be created using LINQ conditions and with multiple parameters.\n\nThis Filter() command creates a filter that limits BlogEntry records by AccountID and an IsDeleted flag.  A parameter is created for each condition with parameter names \"accountID\" and \"isDeleted\":\n```csharp\nmodelBuilder.Filter(\"BlogEntryFilter\", \n                    (BlogEntry b, Guid accountID, bool isDeleted) =\u003e (b.AccountID == accountID) \u0026\u0026 (b.IsDeleted == isDeleted), \n                    () =\u003e GetPersonIDFromPrincipal(Thread.CurrentPrincipal),\n                    () =\u003e false);\n```\n\nThe linq syntax is somewhat limited to boolean expressions but does support the Contains() operator on `Enumerable\u003cT\u003e` to generate sql \"in\" clauses:\n```csharp\nvar values = new List\u003cint\u003e { 1, 2, 3, 4, 5 };\nmodelBuilder.Filter(\"ContainsTest\", (BlogEntry b, List\u003cint\u003e valueList) =\u003e valueList.Contains(b.IntValue.Value), () =\u003e values);\n```\n\nIf you require support for additional linq operators, please create an [issue](https://github.com/jcachat/EntityFramework.DynamicFilters/issues).\n\n## Changing Filter Parameter Values\nWithin a single DbContext instance, filter parameter values can also be changed.  These changes are scoped to only that DbContext instance and do not affect any other DbContext instances.\n\nTo change the Soft Delete filter shown above to return only deleted records, you could do this:\n```csharp\ncontext.SetFilterScopedParameterValue(\"IsDeleted\", true);\n```\n\nIf the filter contains multiple parameters, you must specify the name of the parameter to change like this:\n```csharp\ncontext.SetFilterScopedParameterValue(\"BlogEntryFilter\", \"accountID\", 12345);\n```\n\nParameter values can be set to a specific value or delegate expressions (`Func\u003cobject\u003e` or `Func\u003cDbContext, object\u003e`).\n\nGlobal parameter values can also be changed using the SetFilterGlobalParameterValue extension methods.\n\n\n## Enabling and Disabling Filters\nTo disable a filter, use the DisableFilter extension method like this:\n```csharp\ncontext.DisableFilter(\"IsDeleted\");\n```\n\nFilters can also be globally disabled after they are created in OnModelCreating:\n```csharp\nmodelBuilder.DisableFilterGlobally(\"IsDeleted\");\n```\n\nGlobally disabled filters can then be selectively enabled as needed.  Enabling a globally disabled filter will apply only to that DbContext just like scoped parameter values.\n```csharp\ncontext.EnableFilter(\"IsDeleted\");\n```\n\nYou can also mass enable/disable all filters within a DbContext at once:\n```\ncontext.DisableAllFilters();\ncontext.EnableAllFilters();\n```\n\nHowever, note that if a query is executed with a filter disabled, Entity Framework will cache those entities internally.  If you then enable a filter, cached entities may be included in child collections that otherwise should not be.  Entity Framework caches per DbContext, so if you find this to be an issue, you can avoid it by using a fresh DbContext.\n\nIn order to be able to dynamically enable/disable filters, a special condition is added to the sql query that will look something like:\n```\nOR (@DynamicFilterParam_000001 IS NOT NULL)\n```\nIf the filter is enabled, this condition is dynamically excluded from the sql just before execution but will be present when the filter is disabled (and the parameter value will be set to 1).  In both cases, the parameter will be listed in the parameter list sent in the query.  \n\nIf you never require the need to enable or disable filters at any time during the application life cycle, you can prevent this condition entirely using these two methods:\n```\nmodelBuilder.PreventDisabledFilterConditions(\"IsDeleted\");  // disable a single filter\nmodelBuilder.PreventAllDisabledFilterConditions();          // disable all filters defined up to calling this method\n```\nThis can only be done during OnModelCreating and, once turned off, can not be turned back on.  This is because we only have one opportunity to include this condition in the query, so once the query is compiled, we cannot change it.\n\nIn most cases, this condition should not affect query performance, especially since we exclude it when the filter is enabled. But if additional conditions are used in the where clause and a multi-column index is involved, this condition may cause SQL Server to choose the wrong index or perform a table scan. You should examine your queries and index usage performance to determine if this is an issue for you.\n### Conditionally Enabling Filters\nAs of Version 2, filters can also be enabled conditionally by specifying a delegate expression that will be evaluated at query execution. This can be used to only enable a filter under specific conditions and to define those conditions along with your filter definition.\n\nFor example:\n```csharp\nmodelBuilder.Filter(\"BlogEntryFilter\", (BlogEntry b, Guid accountID) =\u003e (b.AccountID == accountID), \n                    () =\u003e GetPersonIDFromPrincipal(Thread.CurrentPrincipal));\nmodelBuilder.EnableFilter(\"BlogEntryFilter\", () =\u003e !UserIsAdmin(Thread.CurrentPrincipal));\n```\ncreates a filter on BlogEntry records to restrict to only the current uses AccountID.  But the filter will only be enabled if the user is not an Admin user.\n\nThis expression can be specified as either a `Func\u003cobject\u003e` or a `Func\u003cDbContext, object\u003e` expression, just like parameter value expressions.\n\n### Filter Options\nAs of Version 2.9, fluent-style options have been added to allow you to control how filters are applied to entities.  The following options are available:\n\n* SelectEntityTypeCondition: Allows you to specify a delegate that will be called for each Type that is found to match the filter.  You can inspect the type and return true/false to indicate if this filter should be applied to this specific Type.  This allows you create a filter on an Interface but then not apply it to specific entities. \n\n    The following example creates a filter on all entities implementing ISoftDelete except for entities implementing the IExceptMe interface.\n   ```\n   modelBuilder.Filter(\"ISoftDelete\", (ISoftDelete d, bool isDeleted) =\u003e d.IsDeleted == isDeleted, false, opt =\u003e opt.SelectEntityTypeCondition(type =\u003e !typeof(IExceptMe).IsAssignableFrom(type)));\n   ```\n   \n* ApplyToChildProperties: When set to false, the filter will only be applied to the main entity.  It will not be applied to any child properties.\n\n   Default is true (filter is applied to all entities in the query).\n   \n   The following filter will only be applied to the main entity of a query - not to any child properties:\n   ```\n   modelBuilder.Filter(\"ISoftDelete\", (ISoftDelete d, bool isDeleted) =\u003e d.IsDeleted == isDeleted, false, opt =\u003e opt.ApplyToChildProperties(false));\n   ```\n\n* ApplyRecursively: When set to false, the filter will not be applied recursively.  Once it has been applied to a parent entity, it will not be applied again.  If an entity contains two child properties that match the same filter, it will be applied to both properties.  But it would then not be applied to any child properties of either of those entities.\n   \n   Default is true - all filters are applied recursively.\n   \n   The following filter will not be applied recursively:\n   ```\n   modelBuilder.Filter(\"ISoftDelete\", (ISoftDelete d, bool isDeleted) =\u003e d.IsDeleted == isDeleted, false, opt =\u003e opt.ApplyRecursively(false));\n   ```\n\n\n## Oracle Support\nOracle is supported using the [Official Oracle ODP.NET, Managed Entity Framework Driver](https://www.nuget.org/packages/Oracle.ManagedDataAccess.EntityFramework) with the following limitations:\n* The Oracle driver does not support generating an \"in\" expression.  Using the \"Contains\" operator will result in outputting a series of equals/or expressions.\n* Using a DateTime value tends to throw an exception saying: \"The member with identity 'Precision' does not exist in the metadata collection.\"  This seems to be a bug in the Oracle driver.  Using a DateTimeOffset instead of a DateTime works correctly (which also then uses the Oracle TIMESTAMP datatype instead of DATE).\n\n## SQL Server CE Support\nSQL Server CE is supported with the following limitations:\n* The SQL Server CE provider does not support modifying the CommandText property during SQL interception.  That is necessary in order to do some of the dynamic parameter value replacements.  This means that Contains(IEnumerable\u003cT\u003e) is not supported on SQL Server CE and will throw an exception.\n* SQL Server CE does not support the \"like @value+'%'\" syntax (see https://stackoverflow.com/questions/1916248/how-to-use-parameter-with-like-in-sql-server-compact-edition).  So string.StartsWith(@value) is not supported and will throw a Format exception.\n\n## Useful links\n\n- [Website](https://entityframework-dynamicfilters.net/)\n- [Documentation](https://entityframework-dynamicfilters.net/filter)\n- [Online Examples](https://entityframework-dynamicfilters.net/online-examples) \n- [Nuget](https://www.nuget.org/packages/EntityFramework.DynamicFilters)\n\n## Contribute\n\nThe best way to contribute is by **spreading the word** about the library:\n\n - Blog it\n - Comment it\n - Star it\n - Share it\n \nA **HUGE THANKS** for your help.\n\n## More Projects\n\n- Projects:\n   - [EntityFramework Extensions](https://entityframework-extensions.net/)\n   - [Dapper Plus](https://dapper-plus.net/)\n   - [C# Eval Expression](https://eval-expression.net/)\n- Learn Websites\n   - [Learn EF Core](https://www.learnentityframeworkcore.com/)\n   - [Learn Dapper](https://www.learndapper.com/)\n- Online Tools:\n   - [.NET Fiddle](https://dotnetfiddle.net/)\n   - [SQL Fiddle](https://sqlfiddle.com/)\n   - [ZZZ Code AI](https://zzzcode.ai/)\n- and much more!\n\nTo view all our free and paid projects, visit our website [ZZZ Projects](https://zzzprojects.com/).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzzzprojects%2FEntityFramework.DynamicFilters","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fzzzprojects%2FEntityFramework.DynamicFilters","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzzzprojects%2FEntityFramework.DynamicFilters/lists"}