-
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
EFCore fails to filter when projecting to a C# record type #27281
Comments
Records can have arbitrary constructors, just like normal C# classes/structs; that means EF cannot translate record instantiation to SQL, so composing LINQ operators (such as filtering) after projecting to a record cannot be translated either. Basically there's no difference here between records and non-record types, which EF doesn't support projecting to (before composing additional operators). |
Perhaps worth clarifying that EF does support projecting to both class and record types but only using initialisation syntax and not constructor syntax. For example, both queries below work, but not if you added constructors to the class and record types and used those instead. using Microsoft.EntityFrameworkCore;
{
using (MyDbContext context = new())
{
context.Database.EnsureDeleted();
context.Database.EnsureCreated();
context.Add(new Person { Name = "Bob" });
context.SaveChanges();
}
using (MyDbContext context = new())
{
var personClassDtos = context.People
.Select(x => new PersonClassDto { Name = x.Name })
.Where(x => x.Name == "Bob")
.ToList();
var personRecordDtos = context.People
.Select(x => new PersonRecordDto { Name = x.Name })
.Where(x => x.Name == "Bob")
.ToList();
}
}
public class MyDbContext : DbContext
{
public DbSet<Person> People { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder
.LogTo(Console.WriteLine, Microsoft.Extensions.Logging.LogLevel.Information)
.UseSqlServer("Data Source=(LocalDb)\\MSSQLLocalDB;Initial Catalog=Projection;Integrated Security=SSPI");
}
public class Person
{
public int Id { get; set; }
public string Name { get; set; } = null!;
}
public class PersonClassDto
{
public string Name { get; set; } = null!;
}
public record PersonRecordDto
{
public string Name { get; set; } = null!;
} |
Thanks @stevendarby, I indeed missed that distinction in my comment. |
I'm almost certainly missing something but I'm curious why that is. Isn't the constructor initialization just yet another expression EF could just inspect? What's preventing this from being possible? |
The code inside the constructor is opaque to EF - it doesn’t know what properties are set by the arguments etc. |
Yes, but it could retrieve the data and then create the object/record instances on the client side by actually calling the constructor and passing it the retrieved values. What makes this approach unfeasible? |
If you want to do it in the client you can call ToList/AsEnumerable etc. We were talking about projecting and doing further filtering etc. on the server. |
Ah yeah, now I get what I was missing. This wouldn't allow further filtering and so on, right. |
Given the following:
Filtering on a projection to a defined type (class) or an anonymous type works:
Both work and produce:
However, filtering on a projection to a C# record fails:
This throws the following:
Workaround is to project to an anonymous type, perform any additional filtering/sorting then project to record type.
Might be related in some form to #11457
EF Core version: 6.0.1
Database provider: Microsoft.EntityFrameworkCore.SqlServer
The text was updated successfully, but these errors were encountered: