Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions EntityFrameworkCore.Projectables.sln
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
ProjectSection(SolutionItems) = preProject
.editorconfig = .editorconfig
Directory.Build.props = Directory.Build.props
Directory.Packages.props = Directory.Packages.props
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EntityFrameworkCore.Projectables", "src\EntityFrameworkCore.Projectables\EntityFrameworkCore.Projectables.csproj", "{EE4D6CC1-78DE-4279-A567-C3D360C479F8}"
Expand Down
5 changes: 5 additions & 0 deletions global.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"sdk": {
"version": "8.0.400"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using Microsoft.EntityFrameworkCore.Metadata.Conventions;

namespace EntityFrameworkCore.Projectables.Infrastructure.Internal;

public class ProjectablePropertiesNotMappedConvention : IEntityTypeAddedConvention
{
public void ProcessEntityTypeAdded(IConventionEntityTypeBuilder entityTypeBuilder, IConventionContext<IConventionEntityTypeBuilder> context)
{
if (entityTypeBuilder.Metadata.ClrType is null)
{
return;
}

foreach (var property in entityTypeBuilder.Metadata.ClrType.GetRuntimeProperties())
{
if (property.GetCustomAttribute<ProjectableAttribute>() is not null)
{
entityTypeBuilder.Ignore(property.Name);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using Microsoft.EntityFrameworkCore.Metadata.Conventions;
using Microsoft.EntityFrameworkCore.Metadata.Conventions.Infrastructure;

namespace EntityFrameworkCore.Projectables.Infrastructure.Internal;

public class ProjectablePropertiesNotMappedConventionPlugin : IConventionSetPlugin
{
public ConventionSet ModifyConventions(ConventionSet conventionSet)
{
conventionSet.EntityTypeAddedConventions.Add(new ProjectablePropertiesNotMappedConvention());
return conventionSet;
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
using EntityFrameworkCore.Projectables.Infrastructure;
using EntityFrameworkCore.Projectables.Infrastructure.Internal;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata.Conventions;
using Microsoft.EntityFrameworkCore.Metadata.Conventions.Infrastructure;
using Microsoft.EntityFrameworkCore.Query;
using Microsoft.EntityFrameworkCore.Query.Internal;
using Microsoft.Extensions.DependencyInjection;
Expand Down Expand Up @@ -36,6 +38,9 @@ public ProjectionOptionsExtension(ProjectionOptionsExtension copyFrom)
[System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "EF1001:Internal EF Core API usage.", Justification = "Needed")]
public void ApplyServices(IServiceCollection services)
{
// Register a convention that will ignore properties marked with the ProjectableAttribute
services.AddScoped<IConventionSetPlugin, ProjectablePropertiesNotMappedConventionPlugin>();

static object CreateTargetInstance(IServiceProvider services, ServiceDescriptor descriptor)
{
if (descriptor.ImplementationInstance is not null)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using EntityFrameworkCore.Projectables.FunctionalTests.Helpers;

namespace EntityFrameworkCore.Projectables.FunctionalTests
{
public class EnumerableProjectableTests
{
public class Product
{
public int Id { get; set; }

public List<ProductPrice> Prices { get; } = [];

[Projectable]
public IEnumerable<ProductPrice> CheapPrices => Prices.Where(x => x.Price < 10D);
}

public class ProductPrice
{
public int Id { get; set; }

public double Price { get; set; }
}

[Fact]
public void ProjectableProperty_IsIgnoredFromMapping()
{
var dbContext = new SampleDbContext<Product>();
var productPriceType = dbContext.Model.GetEntityTypes().Single(x => x.ClrType == typeof(ProductPrice));

// Assert 3 properties: Id, Price, ProductId (synthetic)
Assert.Equal(3, productPriceType.GetProperties().Count());
}
}
}