-
Notifications
You must be signed in to change notification settings - Fork 3.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add expression support for abstract properties #30232
Comments
@amyboose Do you want to be able to use both queries using |
Yes, I want both. It will be so useful for many cases using inheritance and repository pattern. I've changed a bit my code to show some hypothetical case. Main changes in repositories: using Microsoft.EntityFrameworkCore;
using System.ComponentModel.DataAnnotations.Schema;
namespace EfCore;
public class Program
{
public static async Task Main(params string[] args)
{
IHost host = Host.CreateDefaultBuilder()
.ConfigureServices(services =>
{
services.AddDbContext<MyContext>(builder =>
{
builder.UseSqlServer("Server=localhost,7438;Database=testdb;TrustServerCertificate=True;User Id=sa;Password=RMfL3Tx%bZ5b;");
});
services.AddScoped(typeof(BlogRepository<>));
services.AddScoped<AnimalBlogRepository>();
})
.Build();
using var scope = host.Services.CreateScope();
var provider = scope.ServiceProvider;
var blogRepository = provider.GetRequiredService<BlogRepository<Blog>>();
var animalBlogRepository = provider.GetRequiredService<AnimalBlogRepository>();
//Example 1
List<Blog> nonEmptyBlogs = await blogRepository.GetNonEmptyBlogsWithPosts();
//Exmaple 2, please comment the first example
List<AnimalBlog> blogsAboutDogs = await animalBlogRepository.GetBlogsByAnimal("dog");
}
}
public abstract class Blog
{
public int Id { get; set; }
public DateTimeOffset PublishedAt { get; set; }
[NotMapped]
public abstract IReadOnlyList<Post> Posts { get; }
}
public abstract class Post
{
public int Id { get; set; }
public int BlogId { get; set; }
[NotMapped]
public abstract Blog Blog { get; }
}
public class AnimalBlog : Blog
{
public List<AnimalPost> AnimalPosts { get; set; } = null!;
public override IReadOnlyList<Post> Posts => AnimalPosts;
}
public class HouseBlog : Blog
{
public List<HousePost> HousePosts { get; set; } = null!;
public override IReadOnlyList<Post> Posts => HousePosts;
}
public class AnimalPost : Post
{
public AnimalBlog AnimalBlog { get; set; } = null!;
public List<string> AnimalTags { get; set; } = null!;
public override Blog Blog => AnimalBlog;
}
public class HousePost : Post
{
public HouseBlog HouseBlog { get; set; } = null!;
public override Blog Blog => HouseBlog;
}
public class MyContext : DbContext
{
public MyContext(DbContextOptions options) : base(options) { }
public DbSet<Blog> Blogs { get; set; }
public DbSet<Post> Posts { get; set; }
public DbSet<AnimalBlog> AnimalBlogs { get; set; }
public DbSet<AnimalPost> AnimalPosts { get; set; }
public DbSet<HouseBlog> HouseBlogs { get; set; }
public DbSet<HousePost> HousePosts { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<AnimalBlog>()
.HasMany(x => x.AnimalPosts)
.WithOne(x => x.AnimalBlog)
.HasForeignKey(x => x.BlogId);
modelBuilder.Entity<AnimalPost>()
.OwnsOne(p => p.AnimalTags)
.ToJson();
modelBuilder.Entity<HouseBlog>()
.HasMany(x => x.HousePosts)
.WithOne(x => x.HouseBlog)
.HasForeignKey(x => x.BlogId);
}
}
public class BlogRepository<TBlog> where TBlog : Blog
{
protected readonly MyContext _context;
public BlogRepository(MyContext context)
{
_context = context;
}
public Task<List<TBlog>> GetNonEmptyBlogsWithPosts()
{
return _context.Set<TBlog>()
.Where(x => x.Posts.Count > 0)
.Include(x => x.Posts)
.ToListAsync();
}
}
public class AnimalBlogRepository : BlogRepository<AnimalBlog>
{
public AnimalBlogRepository(MyContext context) : base(context) { }
public Task<List<AnimalBlog>> GetBlogsByAnimal(string animalTag)
{
return _context.Set<AnimalBlog>()
.Where(x => x.AnimalPosts.Any(post => post.AnimalTags.Any(tag => tag == animalTag)))
.Include(x => x.AnimalPosts)
.ToListAsync();
}
} Also you can see that I've added Include function which can be useful too .Include(x => x.Posts) The exmple throw exceptions I have real cases but it will be too long example to show it. |
That's not currently supported by EF Core; I'll talk to the team about whether we might consider it for the future. |
Notes from triage:
|
I have some problem using inheritance and EF Core.
My code:
But first and second examples throw an exception:
Blog.Posts and Post.Blog are not a part of EF Core model but technically they are the same as its internal properties.
I would like to use these properties in Expressions without using custom creation of expression trees.
It might look like this:
Also it can be EF Core's internal mechanism to add abstract property mapping to its internal property.
This feature is useful a lot for repositories when parent repository contains a whole logic. For example, BlogRepository can contains a whole logic for all child repositories.
Additional information:
Also I've got endless migration after comment [NotMapped] attributes
EF Core version: 7.0.2
Database provider: SQL Provider
Target framework: NET 7.0
Operating system: Windows 10
IDE: Visual Studio 2022 17.4
The text was updated successfully, but these errors were encountered: