{"id":21183371,"url":"https://github.com/koenbeuk/entityframeworkcore.projectables","last_synced_at":"2026-02-13T15:09:35.586Z","repository":{"id":38020956,"uuid":"371158830","full_name":"koenbeuk/EntityFrameworkCore.Projectables","owner":"koenbeuk","description":"Project over properties and functions in your linq queries","archived":false,"fork":false,"pushed_at":"2025-05-04T20:00:25.000Z","size":382,"stargazers_count":349,"open_issues_count":22,"forks_count":25,"subscribers_count":10,"default_branch":"master","last_synced_at":"2025-05-04T20:33:55.879Z","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/koenbeuk.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","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,"zenodo":null}},"created_at":"2021-05-26T20:17:35.000Z","updated_at":"2025-05-04T20:00:29.000Z","dependencies_parsed_at":"2023-02-09T20:45:16.208Z","dependency_job_id":"c41994f9-bdc2-49d5-9dc8-89c8ce8c2501","html_url":"https://github.com/koenbeuk/EntityFrameworkCore.Projectables","commit_stats":{"total_commits":202,"total_committers":13,"mean_commits":"15.538461538461538","dds":0.5594059405940595,"last_synced_commit":"fbd2b8df68ebc0d0819d93e15579f749ea885615"},"previous_names":[],"tags_count":29,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/koenbeuk%2FEntityFrameworkCore.Projectables","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/koenbeuk%2FEntityFrameworkCore.Projectables/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/koenbeuk%2FEntityFrameworkCore.Projectables/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/koenbeuk%2FEntityFrameworkCore.Projectables/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/koenbeuk","download_url":"https://codeload.github.com/koenbeuk/EntityFrameworkCore.Projectables/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254160206,"owners_count":22024567,"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-11-20T18:00:13.095Z","updated_at":"2026-01-12T12:30:37.191Z","avatar_url":"https://github.com/koenbeuk.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# EntityFrameworkCore.Projectables\nFlexible projection magic for EF Core\n\n[![NuGet version (EntityFrameworkCore.Projectables)](https://img.shields.io/nuget/v/EntityFrameworkCore.Projectables.Abstractions.svg?style=flat-square)](https://www.nuget.org/packages/EntityFrameworkCore.Projectables.Abstractions/)\n[![.NET](https://github.com/koenbeuk/EntityFrameworkCore.Projectables/actions/workflows/build.yml/badge.svg)](https://github.com/koenbeuk/EntityFrameworkCore.Projectables/actions/workflows/build.yml)\n\n## NuGet packages\n- EntityFrameworkCore.Projectables.Abstractions [![NuGet version](https://img.shields.io/nuget/v/EntityFrameworkCore.Projectables.Abstractions.svg?style=flat-square)](https://www.nuget.org/packages/EntityFrameworkCore.Projectables.Abstractions/) [![NuGet](https://img.shields.io/nuget/dt/EntityFrameworkCore.Projectables.Abstractions.svg?style=flat-square)](https://www.nuget.org/packages/EntityFrameworkCore.Projectables.Abstractions/)\n- EntityFrameworkCore.Projectables [![NuGet version](https://img.shields.io/nuget/v/EntityFrameworkCore.Projectables.svg?style=flat-square)](https://www.nuget.org/packages/EntityFrameworkCore.Projectables/) [![NuGet](https://img.shields.io/nuget/dt/EntityFrameworkCore.Projectables.svg?style=flat-square)](https://www.nuget.org/packages/EntityFrameworkCore.Projectables/)\n\n\u003e Starting with V2 of this project we're binding against **EF Core 6**. If you're targeting **EF Core 5** or **EF Core 3.1** then you can use the latest v1 release. These are functionally equivalent.\n\n\n## Getting started\n1. Install the package from [NuGet](https://www.nuget.org/packages/EntityFrameworkCore.Projectables/)\n2. Enable Projectables in your DbContext by adding: `dbContextOptions.UseProjectables()`\n3. Implement projectable properties and methods, marking them with the `[Projectable]` attribute.\n4. Explore our [samples](https://github.com/koenbeuk/EntityFrameworkCore.Projectables/tree/master/samples) and checkout our [Blog Post](https://onthedrift.com/posts/efcore-projectables/) for further guidance.\n\n### Example\nAssuming this sample:\n\n```csharp\nclass Order {\n    public int Id { get; set; }\n    public int UserId { get; set; }\n    public DateTime CreatedDate { get; set; }\n\n    public decimal TaxRate { get; set; }\n    \n    public User User { get; set; } \n    public ICollection\u003cOrderItem\u003e Items { get; set; }\n\n    [Projectable] public decimal Subtotal =\u003e Items.Sum(item =\u003e item.Product.ListPrice * item.Quantity);\n    [Projectable] public decimal Tax =\u003e Subtotal * TaxRate;\n    [Projectable] public decimal GrandTotal =\u003e Subtotal + Tax;\n}\n\npublic static class UserExtensions {\n    [Projectable]\n    public static Order GetMostRecentOrderForUser(this User user, DateTime? cutoffDate) =\u003e \n        user.Orders\n            .Where(x =\u003e cutoffDate == null || x.CreatedDate \u003e= cutoffDate)\n            .OrderByDescending(x =\u003e x.CreatedDate)\n            .FirstOrDefault();\n}\n\nvar result = _dbContext.Users\n    .Where(x =\u003e x.UserName == \"Jon\")\n    .Select(x =\u003e new {\n        x.GetMostRecentOrderForUser(DateTime.UtcNow.AddDays(-30)).GrandTotal\n    });\n    .FirstOrDefault();\n```\n\nThe following query gets generated (assuming SQL Server as a database provider)\n```sql\nDECLARE @__sampleUser_UserName_0 nvarchar(4000) = N'Jon';\n\nSELECT (\n    SELECT COALESCE(SUM([p].[ListPrice] * CAST([o].[Quantity] AS decimal(18,2))), 0.0)\n    FROM [OrderItem] AS [o]\n    INNER JOIN [Products] AS [p] ON [o].[ProductId] = [p].[Id]\n    WHERE (\n        SELECT TOP(1) [o0].[Id]\n        FROM [Orders] AS [o0]\n        WHERE [u].[Id] = [o0].[UserId] AND [o0].[FulfilledDate] IS NOT NULL\n        ORDER BY [o0].[CreatedDate] DESC) IS NOT NULL AND (\n        SELECT TOP(1) [o1].[Id]\n        FROM [Orders] AS [o1]\n        WHERE [u].[Id] = [o1].[UserId] AND [o1].[FulfilledDate] IS NOT NULL\n        ORDER BY [o1].[CreatedDate] DESC) = [o].[OrderId]) * (\n    SELECT TOP(1) [o2].[TaxRate]\n    FROM [Orders] AS [o2]\n    WHERE [u].[Id] = [o2].[UserId] AND [o2].[FulfilledDate] IS NOT NULL\n    ORDER BY [o2].[CreatedDate] DESC) AS [GrandTotal]\nFROM [Users] AS [u]\nWHERE [u].[UserName] = @__sampleUser_UserName_0\n```\n\nProjectable properties and methods have been inlined! the generated SQL could be improved but this is what EF Core (v8) gives us.\n\n### How it works\nEssentially, there are two components: We have a source generator that can write companion expressions for properties and methods marked with the Projectable attribute. Then, we have a runtime component that intercepts any query and translates any call to a property or method marked with the Projectable attribute, translating the query to use the generated expression instead.\n\n### FAQ\n\n#### Are there currently any known limitations?\nCurrently, there is no support for overloaded methods. Each method name needs to be unique within a given type.\n\n#### Is this specific to a database provider?\nNo, the runtime component injects itself into the EFCore query compilation pipeline, thus having no impact on the database provider used. Of course, you're still limited to whatever your database provider can do.\n\n#### Are there performance implications that I should be aware of?\nThere are two compatibility modes: Limited and Full (Default). Most of the time, limited compatibility mode is sufficient. However, if you are running into issues with failed query compilation, then you may want to stick with Full compatibility mode. With Full compatibility mode, each query will first be expanded (any calls to Projectable properties and methods will be replaced by their respective expression) before being handed off to EFCore. (This is similar to how LinqKit/LinqExpander/Expressionify works.) Because of this additional step, there is a small performance impact. Limited compatibility mode is smart about things and only expands the query after it has been accepted by EF. The expanded query will then be stored in the Query Cache. With Limited compatibility, you will likely see increased performance over EFCore without projectables.\n\n#### Can I call additional properties and methods from my Projectable properties and methods?\nYes, you can! Any projectable property/method can call into other properties and methods as long as those properties/methods are native to EFCore or marked with a Projectable attribute.\n\n#### Can I use projectable extensions methods on non-entity types?\nYes you can. It's perfectly acceptable to have the following code:\n```csharp\n[Projectable]\npublic static int Squared(this int i) =\u003e i * i;\n```\nAny call to squared given any int will perfectly translate to SQL.\n\n#### How do I deal with nullable properties\nExpressions and Lamdas are different and not equal. Expressions can only express a subset of valid CSharp statements that are allowed in lambda's and arrow functions. One obvious limitation is the null-conditional operator. Consider the following example:\n```csharp\n[Projectable]\npublic static string? GetFullAddress(this User? user) =\u003e user?.Location?.AddressLine1 + \" \" + user?.Location.AddressLine2;\n```\nThis is a perfectly valid arrow function but it can't be translated directly to an expression tree. This Project will generate an error by default and suggest 2 solutions: Either you rewrite the function to explicitly check for nullables or you let the generator do that for you!\n\nStarting from the official release of V2, we can now hint the generator in how to translate this arrow function to an expression tree. We can say:\n```csharp\n[Projectable(NullConditionalRewriteSupport = NullConditionalRewriteSupport.Ignore)]\n``` \nwhich will simply generate an expression tree that ignores the null-conditional operator. This generates: \n```csharp\nuser.Location.AddressLine1 + \" \" + user.Location.AddressLine2\n```\nThis is perfect for a database like SQL Server where nullability is implicit and if any of the arguments were to be null, the resulting value will be null. If you are dealing with CosmosDB (which may result to client-side evaluation) or want to be explicit about things. You can configure your projectable as such: \n```csharp\n[Projectable(NullConditionalRewriteSupport = NullConditionalRewriteSupport.Rewrite)]\n```\nThis will rewrite your expression to explicitly check for nullables. In the former example, this will be rewritten to: \n```csharp \n(user != null ? user.Location != null ? user.Location?.AddressLine1 + (user != null ? user.Location != null ? user.Location.AddressLine2 : null) : null)\n```\nNote that using rewrite (not ignore) may increase the actual SQL query complexity being generated with some database providers such as SQL Server\n\n#### Can I use projectables in any part of my query?\nCertainly, consider the following example: \n```csharp\npublic class User\n{\n    public int Id { get; set; }\n    public string FirstName { get; set; }\n    public string LastName { get; set; }\n\n    [Projectable]\n    public string FullName =\u003e FirstName + \" \" + LastName;\n}\n\nvar query = dbContext.Users\n    .Where(x =\u003e x.FullName.Contains(\"Jon\"))\n    .GroupBy(x =\u003e x.FullName)\n    .OrderBy(x =\u003e x.Key)\n    .Select(x =\u003e x.Key);\n```\nWhich generates the following SQL (SQLite syntax)\n```sql\nSELECT (COALESCE(\"u\".\"FirstName\", '') || ' ') || COALESCE(\"u\".\"LastName\", '')\nFROM \"Users\" AS \"u\"\nWHERE ('Jon' = '') OR (instr((COALESCE(\"u\".\"FirstName\", '') || ' ') || COALESCE(\"u\".\"LastName\", ''), 'Jon') \u003e 0)\nGROUP BY (COALESCE(\"u\".\"FirstName\", '') || ' ') || COALESCE(\"u\".\"LastName\", '')\nORDER BY (COALESCE(\"u\".\"FirstName\", '') || ' ') || COALESCE(\"u\".\"LastName\", '')\n```\n\n#### How does this relate to [Expressionify](https://github.com/ClaveConsulting/Expressionify)?\nExpressionify is a project that was launched before this project. It has some overlapping features and uses similar approaches. When I first published this project, I was not aware of its existence, so shame on me. Currently, Expressionify targets a more focused scope of what this project is doing, and thereby it seems to be more limiting in its capabilities. Check them out though!\n\n#### How does this relate to LinqKit/LinqExpander/...?\nThere are a few projects like [LinqKit](https://github.com/scottksmith95/LINQKit) that were created before we had source generators in .NET. These are great options if you're stuck with classical EF or don't want to rely on code generation. Otherwise, I would suggest that EntityFrameworkCore.Projectables and Expressionify are superior approaches as they can rely on SourceGenerators to do most of the hard work.\n\n#### Is the available for EFCore 3.1, 5 and 6?\nV1 is targeting EF Core 5 and 3.1. V2 and V3 are targeting EF Core 6 and are compatible with EF Core 7. You can upgrade/downgrade between these versions based on your EF Core version requirements.\n\n#### What is next for this project?\nTBD... However, one thing I'd like to improve is our expression generation logic as it's currently making a few assumptions (have yet to experience it breaking). Community contributions are very welcome!\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkoenbeuk%2Fentityframeworkcore.projectables","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkoenbeuk%2Fentityframeworkcore.projectables","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkoenbeuk%2Fentityframeworkcore.projectables/lists"}