mirror of
https://github.com/zoriya/EntityFrameworkCore.Projectables.git
synced 2025-12-19 12:05:11 +00:00
Updated readme and added new sample project
This commit is contained in:
@@ -29,6 +29,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EntityFrameworkCore.Project
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EntityFrameworkCore.Projections.FunctionalTests", "tests\EntityFrameworkCore.Projections.FunctionalTests\EntityFrameworkCore.Projections.FunctionalTests.csproj", "{A36F5471-0C16-4453-811B-818326931313}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EntityFrameworkCore.Projections.FunctionalTests", "tests\EntityFrameworkCore.Projections.FunctionalTests\EntityFrameworkCore.Projections.FunctionalTests.csproj", "{A36F5471-0C16-4453-811B-818326931313}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ReadmeSample", "samples\ReadmeSample\ReadmeSample.csproj", "{6F63E04C-9267-4211-8AC7-290C60331D60}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
@@ -63,6 +65,10 @@ Global
|
|||||||
{A36F5471-0C16-4453-811B-818326931313}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{A36F5471-0C16-4453-811B-818326931313}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{A36F5471-0C16-4453-811B-818326931313}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{A36F5471-0C16-4453-811B-818326931313}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{A36F5471-0C16-4453-811B-818326931313}.Release|Any CPU.Build.0 = Release|Any CPU
|
{A36F5471-0C16-4453-811B-818326931313}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{6F63E04C-9267-4211-8AC7-290C60331D60}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{6F63E04C-9267-4211-8AC7-290C60331D60}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{6F63E04C-9267-4211-8AC7-290C60331D60}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{6F63E04C-9267-4211-8AC7-290C60331D60}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
@@ -75,6 +81,7 @@ Global
|
|||||||
{C8038180-36F8-4077-922B-91F428EAC7D9} = {A43F1828-D9B6-40F7-82B6-CA0070843E2F}
|
{C8038180-36F8-4077-922B-91F428EAC7D9} = {A43F1828-D9B6-40F7-82B6-CA0070843E2F}
|
||||||
{2F0DD7D7-867F-4478-9E22-45C114B61C46} = {F5E4436F-87F2-46AB-A9EB-59B4BF21BF7A}
|
{2F0DD7D7-867F-4478-9E22-45C114B61C46} = {F5E4436F-87F2-46AB-A9EB-59B4BF21BF7A}
|
||||||
{A36F5471-0C16-4453-811B-818326931313} = {F5E4436F-87F2-46AB-A9EB-59B4BF21BF7A}
|
{A36F5471-0C16-4453-811B-818326931313} = {F5E4436F-87F2-46AB-A9EB-59B4BF21BF7A}
|
||||||
|
{6F63E04C-9267-4211-8AC7-290C60331D60} = {07584D01-2D30-404B-B0D1-32080C0CC18A}
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
SolutionGuid = {D17BD356-592C-4628-9D81-A04E24FF02F3}
|
SolutionGuid = {D17BD356-592C-4628-9D81-A04E24FF02F3}
|
||||||
|
|||||||
120
README.md
120
README.md
@@ -1 +1,121 @@
|
|||||||
|
# EntitiyFrameworkCore.Projections
|
||||||
|
Flexible projection magic for EFCore
|
||||||
|
|
||||||
|
[](https://www.nuget.org/packages/EntityFrameworkCore.Projections.Abstractions/)
|
||||||
|
|
||||||
|
## NuGet packages
|
||||||
|
- EntityFrameworkCore.Projections.Abstractions [](https://www.nuget.org/packages/EntityFrameworkCore.Projections.Abstractions/) [](https://www.nuget.org/packages/EntityFrameworkCore.Projections.Abstractions/)
|
||||||
|
- EntityFrameworkCore.Projections [](https://www.nuget.org/packages/EntityFrameworkCore.Projections/) [](https://www.nuget.org/packages/EntityFrameworkCore.Projections/)
|
||||||
|
|
||||||
|
## Getting started
|
||||||
TODO
|
TODO
|
||||||
|
|
||||||
|
### Example
|
||||||
|
Assuming this sample:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
class Order {
|
||||||
|
public int Id { get; set; }
|
||||||
|
public int UserId { get; set; }
|
||||||
|
public DateTime CreatedDate { get; set; }
|
||||||
|
|
||||||
|
public decimal TaxRate { get; set; }
|
||||||
|
|
||||||
|
public User User { get; set; }
|
||||||
|
public ICollection<OrderItem> Items { get; set; }
|
||||||
|
|
||||||
|
[Projectable] public decimal Subtotal => Items.Sum(item => item.Product.ListPrice * item.Quantity);
|
||||||
|
[Projectable] public decimal Tax => Subtotal * TaxRate;
|
||||||
|
[Projectable] public decimal GrandTotal => Subtotal + Tax;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class UserExtensions {
|
||||||
|
[Projectable]
|
||||||
|
public static Order GetMostRecentOrderForUser(this User user, DateTime? cutoffDate) =>
|
||||||
|
user.Orders
|
||||||
|
.Where(x => cutoffDate == null || x.CreatedDate >= cutoffDate)
|
||||||
|
.OrderByDescending(x => X.CreatedDate)
|
||||||
|
.FirstOrDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
var result = _dbContext.Users
|
||||||
|
.Where(x => x.UserName == "Jon")
|
||||||
|
.Select(x => new {
|
||||||
|
x.GetMostRecentOrderForUser(DateTime.UtcNow.AddDays(-30)).GrandTotal
|
||||||
|
});
|
||||||
|
.FirstOrDefault();
|
||||||
|
```
|
||||||
|
|
||||||
|
The following query gets generated (assuming SQL Server as a database provider)
|
||||||
|
```sql
|
||||||
|
SELECT (
|
||||||
|
SELECT COALESCE(SUM([p].[ListPrice] * CAST([o].[Quantity] AS decimal(18,2))), 0.0)
|
||||||
|
FROM [OrderItem] AS [o]
|
||||||
|
INNER JOIN [Products] AS [p] ON [o].[ProductId] = [p].[Id]
|
||||||
|
WHERE (
|
||||||
|
SELECT TOP(1) [o0].[Id]
|
||||||
|
FROM [Orders] AS [o0]
|
||||||
|
WHERE ([u].[Id] = [o0].[UserId]) AND ([o0].[CreatedDate] >= DATEADD(day, CAST(-30.0E0 AS int), GETUTCDATE()))
|
||||||
|
ORDER BY [o0].[CreatedDate] DESC) IS NOT NULL AND ((
|
||||||
|
SELECT TOP(1) [o1].[Id]
|
||||||
|
FROM [Orders] AS [o1]
|
||||||
|
WHERE ([u].[Id] = [o1].[UserId]) AND ([o1].[CreatedDate] >= DATEADD(day, CAST(-30.0E0 AS int), GETUTCDATE()))
|
||||||
|
ORDER BY [o1].[CreatedDate] DESC) = [o].[OrderId])) + ((
|
||||||
|
SELECT COALESCE(SUM([p0].[ListPrice] * CAST([o2].[Quantity] AS decimal(18,2))), 0.0)
|
||||||
|
FROM [OrderItem] AS [o2]
|
||||||
|
INNER JOIN [Products] AS [p0] ON [o2].[ProductId] = [p0].[Id]
|
||||||
|
WHERE (
|
||||||
|
SELECT TOP(1) [o3].[Id]
|
||||||
|
FROM [Orders] AS [o3]
|
||||||
|
WHERE ([u].[Id] = [o3].[UserId]) AND ([o3].[CreatedDate] >= DATEADD(day, CAST(-30.0E0 AS int), GETUTCDATE()))
|
||||||
|
ORDER BY [o3].[CreatedDate] DESC) IS NOT NULL AND ((
|
||||||
|
SELECT TOP(1) [o4].[Id]
|
||||||
|
FROM [Orders] AS [o4]
|
||||||
|
WHERE ([u].[Id] = [o4].[UserId]) AND ([o4].[CreatedDate] >= DATEADD(day, CAST(-30.0E0 AS int), GETUTCDATE()))
|
||||||
|
ORDER BY [o4].[CreatedDate] DESC) = [o2].[OrderId])) * (
|
||||||
|
SELECT TOP(1) [o5].[TaxRate]
|
||||||
|
FROM [Orders] AS [o5]
|
||||||
|
WHERE ([u].[Id] = [o5].[UserId]) AND ([o5].[CreatedDate] >= DATEADD(day, CAST(-30.0E0 AS int), GETUTCDATE()))
|
||||||
|
ORDER BY [o5].[CreatedDate] DESC)) AS [GrandTotal]
|
||||||
|
FROM [Users] AS [u]
|
||||||
|
WHERE [u].[UserName] = N'Jon'
|
||||||
|
```
|
||||||
|
|
||||||
|
Projectable properties and methods have been inlined! the generated SQL could be improved but this is what EFCore (v5) gives us.
|
||||||
|
|
||||||
|
### How it works
|
||||||
|
Essentially there are 2 components: We have a source generator that is able to write companion Expression for properties and methods marked with the `Projectable` attribute. We then have a runtime component that intercepts any query and translates any call to a property or method marked with the `Projectable` attribute and translates the query to use the generated Expression instead.
|
||||||
|
|
||||||
|
### FAQ
|
||||||
|
|
||||||
|
#### Are there currently any known limitations?
|
||||||
|
There is currently no support for overloaded methods. Each method name needs to be unique within a given type. This is something that will be fixed before a proper v1 release.
|
||||||
|
|
||||||
|
#### Is this specific to a database provider?
|
||||||
|
No; The runtime component injects itself within the EFCore query compilation pipeline and thus has no impact on the database provider used. Of course you're still limited to whatever your database provider can do.
|
||||||
|
|
||||||
|
#### Are there performance implications that I should be aware of?
|
||||||
|
Yes and no; using EntityFrameworkCore.Projections does not add any measerable overhead on top of normal use of EFCore (Expect a proper benchmark soon...) however it does make it easier to write more complex queries. As such: Always consider the generated SQL and ensure that it's performance implications are acceptable.
|
||||||
|
|
||||||
|
#### Can I call additional properties and methods from my Projectable properties and methods?
|
||||||
|
Yes you can! Any projectable property/method can call into other properties and methods as long as those properties/methods are native to EFCore or as long as they are marked with a `Projectable` attribute.
|
||||||
|
|
||||||
|
#### Can I use projectable extensions methods on non-entity types?
|
||||||
|
Yes you can. It's perfectly acceptable to have the following code:
|
||||||
|
```csharp
|
||||||
|
[Projectable]s
|
||||||
|
public static int Squared(this int i) => i * i;
|
||||||
|
```
|
||||||
|
Any call to squared given any int will perfertly translate to SQL.
|
||||||
|
|
||||||
|
#### How does this relate to [Expressionify](https://github.com/ClaveConsulting/Expressionify)?
|
||||||
|
Expressionify 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 existance so shame on me. Currently Expressionify targets a more focusses scope of what this project is doing and thereby it seems to be more limiting in its capabilities. Check them out though!
|
||||||
|
|
||||||
|
#### How does this relate to LinqKit/LinqExpander/...?
|
||||||
|
There are a few projects like [LinqKit](https://github.com/scottksmith95/LINQKit) that were created before we had code generators in dotnet. 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.Projections and Expresssionify are superior approaches as they are able to rely on SourceGenerators to do most of the hard work.
|
||||||
|
|
||||||
|
#### Is the available for EFCore 3.1, 5 and 6?
|
||||||
|
Yes it is! there is no difference between any of these versions and you can upgrade/downgrade whenever you want.
|
||||||
|
|
||||||
|
#### What is next for this project?
|
||||||
|
TBD...
|
||||||
31
samples/ReadmeSample/ApplicationDbContext.cs
Normal file
31
samples/ReadmeSample/ApplicationDbContext.cs
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Security.Cryptography.X509Certificates;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using EntityFrameworkCore.Projections.Extensions;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using ReadmeSample.Entities;
|
||||||
|
|
||||||
|
namespace ReadmeSample
|
||||||
|
{
|
||||||
|
public class ApplicationDbContext : DbContext
|
||||||
|
{
|
||||||
|
public DbSet<User> Users { get; set; }
|
||||||
|
public DbSet<Product> Products { get; set; }
|
||||||
|
public DbSet<Order> Orders { get; set; }
|
||||||
|
|
||||||
|
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
||||||
|
{
|
||||||
|
optionsBuilder.UseSqlServer("Server=(localdb)\\MSSQLLocalDB;Database=ReadmeSample;Trusted_Connection=True");
|
||||||
|
optionsBuilder.UseProjections();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
modelBuilder.Entity<OrderItem>().HasKey(x => new { x.OrderId, x.ProductId });
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
25
samples/ReadmeSample/Entities/Order.cs
Normal file
25
samples/ReadmeSample/Entities/Order.cs
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using EntityFrameworkCore.Projections;
|
||||||
|
|
||||||
|
namespace ReadmeSample.Entities
|
||||||
|
{
|
||||||
|
public class Order
|
||||||
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
public int UserId { get; set; }
|
||||||
|
public DateTime CreatedDate { get; set; }
|
||||||
|
|
||||||
|
public decimal TaxRate { get; set; }
|
||||||
|
|
||||||
|
public User User { get; set; }
|
||||||
|
public ICollection<OrderItem> Items { get; set; }
|
||||||
|
|
||||||
|
[Projectable] public decimal Subtotal => Items.Sum(item => item.Product.ListPrice * item.Quantity);
|
||||||
|
[Projectable] public decimal Tax => Subtotal * TaxRate;
|
||||||
|
[Projectable] public decimal GrandTotal => Subtotal + Tax;
|
||||||
|
}
|
||||||
|
}
|
||||||
12
samples/ReadmeSample/Entities/OrderItem.cs
Normal file
12
samples/ReadmeSample/Entities/OrderItem.cs
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
namespace ReadmeSample.Entities
|
||||||
|
{
|
||||||
|
public class OrderItem
|
||||||
|
{
|
||||||
|
public int OrderId { get; set; }
|
||||||
|
public int ProductId { get; set; }
|
||||||
|
public int Quantity { get; set; }
|
||||||
|
|
||||||
|
public Order Order { get; set; }
|
||||||
|
public Product Product { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
11
samples/ReadmeSample/Entities/Product.cs
Normal file
11
samples/ReadmeSample/Entities/Product.cs
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
namespace ReadmeSample.Entities
|
||||||
|
{
|
||||||
|
public class Product
|
||||||
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
|
||||||
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
public decimal ListPrice { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
16
samples/ReadmeSample/Entities/User.cs
Normal file
16
samples/ReadmeSample/Entities/User.cs
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace ReadmeSample.Entities
|
||||||
|
{
|
||||||
|
public class User
|
||||||
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
|
||||||
|
public string UserName { get; set; }
|
||||||
|
|
||||||
|
public string EmailAddress { get; set; }
|
||||||
|
|
||||||
|
public ICollection<Order> Orders { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
17
samples/ReadmeSample/Extensions/UserExtensions.cs
Normal file
17
samples/ReadmeSample/Extensions/UserExtensions.cs
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using EntityFrameworkCore.Projections;
|
||||||
|
using ReadmeSample.Entities;
|
||||||
|
|
||||||
|
namespace ReadmeSample.Extensions
|
||||||
|
{
|
||||||
|
public static class UserExtensions
|
||||||
|
{
|
||||||
|
[Projectable]
|
||||||
|
public static Order GetMostRecentOrderForUser(this User user, DateTime? cutoffDate)
|
||||||
|
=> user.Orders.Where(x => x.CreatedDate >= cutoffDate).OrderByDescending(x => x.CreatedDate).FirstOrDefault();
|
||||||
|
}
|
||||||
|
}
|
||||||
49
samples/ReadmeSample/Program.cs
Normal file
49
samples/ReadmeSample/Program.cs
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using ReadmeSample.Entities;
|
||||||
|
using ReadmeSample.Extensions;
|
||||||
|
|
||||||
|
namespace ReadmeSample
|
||||||
|
{
|
||||||
|
class Program
|
||||||
|
{
|
||||||
|
static void Main(string[] args)
|
||||||
|
{
|
||||||
|
using var dbContext = new ApplicationDbContext();
|
||||||
|
|
||||||
|
// recreate database
|
||||||
|
dbContext.Database.EnsureDeleted();
|
||||||
|
dbContext.Database.EnsureCreated();
|
||||||
|
|
||||||
|
// Populate with seed data
|
||||||
|
var sampleUser = new User { UserName = "Jon", EmailAddress = "jon@doe.com" };
|
||||||
|
var sampleProduct = new Product { Name = "Blue Pen", ListPrice = 1.5m };
|
||||||
|
var sampleOrder = new Order {
|
||||||
|
User = sampleUser,
|
||||||
|
TaxRate = .19m,
|
||||||
|
CreatedDate = DateTime.UtcNow,
|
||||||
|
Items = new List<OrderItem> {
|
||||||
|
new OrderItem { Product = sampleProduct, Quantity = 5 }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
dbContext.AddRange(sampleUser, sampleProduct, sampleOrder);
|
||||||
|
dbContext.SaveChanges();
|
||||||
|
|
||||||
|
// Lets try a query!
|
||||||
|
var query = dbContext.Users
|
||||||
|
.Where(x => x.UserName == "Jon")
|
||||||
|
.Select(x => new {
|
||||||
|
x.GetMostRecentOrderForUser(DateTime.UtcNow.AddDays(-30)).GrandTotal
|
||||||
|
});
|
||||||
|
|
||||||
|
var result = query.First();
|
||||||
|
|
||||||
|
Console.WriteLine($"Jons latest order had a grant total of {result.GrandTotal}");
|
||||||
|
Console.WriteLine($"The following query was used to fetch the results:");
|
||||||
|
Console.WriteLine(query.ToQueryString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
23
samples/ReadmeSample/ReadmeSample.csproj
Normal file
23
samples/ReadmeSample/ReadmeSample.csproj
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<TargetFramework>net5.0</TargetFramework>
|
||||||
|
<Nullable>disable</Nullable>
|
||||||
|
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
|
||||||
|
<CompilerGeneratedFilesOutputPath>$(BaseIntermediateOutputPath)Generated</CompilerGeneratedFilesOutputPath>
|
||||||
|
<IsPackable>false</IsPackable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="5.0.6" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Logging" Version="5.0.0" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="5.0.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\..\src\EntityFrameworkCore.Projections.Generator\EntityFrameworkCore.Projections.Generator.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
|
||||||
|
<ProjectReference Include="..\..\src\EntityFrameworkCore.Projections\EntityFrameworkCore.Projections.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
@@ -6,6 +6,7 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace EntityFrameworkCore.Projections.Generator
|
namespace EntityFrameworkCore.Projections.Generator
|
||||||
@@ -32,19 +33,29 @@ namespace EntityFrameworkCore.Projections.Generator
|
|||||||
{
|
{
|
||||||
resultBuilder.Clear();
|
resultBuilder.Clear();
|
||||||
|
|
||||||
foreach (var usingDirective in projectable.UsingDirectives)
|
foreach (var usingDirective in projectable.UsingDirectives.Distinct())
|
||||||
{
|
{
|
||||||
resultBuilder.AppendLine(usingDirective);
|
resultBuilder.AppendLine(usingDirective);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (projectable.TargetClassNamespace is not null && !projectable.UsingDirectives.Contains(projectable.TargetClassNamespace))
|
if (projectable.TargetClassNamespace is not null)
|
||||||
{
|
{
|
||||||
resultBuilder.AppendLine($"using {projectable.TargetClassNamespace};");
|
var targetClassUsingDirective = $"using {projectable.TargetClassNamespace};";
|
||||||
|
|
||||||
|
if (!projectable.UsingDirectives.Contains(targetClassUsingDirective))
|
||||||
|
{
|
||||||
|
resultBuilder.AppendLine(targetClassUsingDirective);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (projectable.ClassNamespace is not null && projectable.ClassNamespace != projectable.TargetClassNamespace && !projectable.UsingDirectives.Contains(projectable.ClassNamespace))
|
if (projectable.ClassNamespace is not null && projectable.ClassNamespace != projectable.TargetClassNamespace)
|
||||||
{
|
{
|
||||||
resultBuilder.AppendLine($"using {projectable.ClassNamespace};");
|
var classUsingDirective = $"using {projectable.TargetClassNamespace};";
|
||||||
|
|
||||||
|
if (!projectable.UsingDirectives.Contains(classUsingDirective))
|
||||||
|
{
|
||||||
|
resultBuilder.AppendLine(classUsingDirective);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var generatedClassName = ProjectionExpressionClassNameGenerator.GenerateName(projectable.ClassNamespace, projectable.NestedInClassNames, projectable.MemberName);
|
var generatedClassName = ProjectionExpressionClassNameGenerator.GenerateName(projectable.ClassNamespace, projectable.NestedInClassNames, projectable.MemberName);
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using EntityFrameworkCore.Projections;
|
using EntityFrameworkCore.Projections;
|
||||||
using System;
|
|
||||||
using Foo;
|
|
||||||
|
|
||||||
namespace EntityFrameworkCore.Projections.Generated
|
namespace EntityFrameworkCore.Projections.Generated
|
||||||
#nullable disable
|
#nullable disable
|
||||||
|
|||||||
Reference in New Issue
Block a user