我正在尝试将分组模型转换为使用.Include()
连接到另一个模型的域模型后使用group by。
我们的应用程序使用三层:数据,业务逻辑,服务(Web API)。这些层使用域模型进行通信,api使用数据传输对象与客户端进行通信。我们使用实体框架核心3.1.3进行数据库访问。
api允许使用查询参数进行过滤,排序,计数和分组。我们有一个库将这些请求转换为LINQ表达式,并将其应用于IQueryable<DomainModel>
。为了使这些查询在我们的数据库中执行,我们在Select
处使用IQueryable
创建投影。
现在,我们得到了一对多关系的两个数据模型。
public class InvoiceDataModel
{
public int InvoiceId { get; set; }
public int InvoiceNumber { get; set; }
public DateTime InvoiceDate { get; set; }
public DateTime DueDate { get; set; }
public int CustomerNumber { get; set; }
public string CustomerName { get; set; }
public IEnumerable<DocumentDataModel> Documents { get; set; }
}
public class DocumentDataModel
{
public int DocumentId { get; set; }
public string DocumentNumber { get; set; }
public string Type { get; set; }
public string Category { get; set; }
public InvoiceDataModel Invoice { get; set; }
}
public class DataContext : DbContext
{
public DbSet<InvoiceDataModel> Invoice { get; set; }
public DbSet<DocumentDataModel> Documents { get; set; }
public DataContext(DbContextOptions<DataContext> options)
: base(options)
{}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<InvoiceDataModel>(c =>
{
c.ToTable("Invoice");
c.HasKey(p => p.InvoiceId);
c.HasMany(p => p.Documents)
.WithOne(p => p.Invoice);
});
modelBuilder.Entity<DocumentDataModel>(c =>
{
c.ToTable("Document");
c.HasKey(p => p.DocumentId);
});
}
}
以及相应的领域模型
public class InvoiceDomainModel
{
public int InvoiceId { get; set; }
public int InvoiceNumber { get; set; }
public DateTime InvoiceDate { get; set; }
public DateTime DueDate { get; set; }
public CustomerDomainModel Customer { get; set; }
public IEnumerable<DocumentDomainModel> Documents { get; set; }
}
public class DocumentDomainModel
{
public int DocumentId { get; set; }
public string DocumentNumber { get; set; }
public string Type { get; set; }
public string Category { get; set; }
}
public class CustomerDomainModel
{
public int CustomerNumber { get; set; }
public string CustomerName { get; set; }
}
过滤,排序和计数对此适用。分组抛出InvalidCastException: Unable to cast object of type 'Microsoft.EntityFrameworkCore.Query.SqlExpressions.SqlFunctionExpression' to type 'System.Linq.Expressions.ConstantExpression'.
为了找到问题所在,我决定自己编写LINQ
_dataContext.Invoice
.Include(p => p.Documents)
.Select(x => new InvoiceDomainModel
{
InvoiceId = x.InvoiceId,
InvoiceNumber = x.InvoiceNumber,
DueDate = x.DueDate,
InvoiceDate = x.InvoiceDate,
Customer = new CustomerDomainModel
{
CustomerName = x.CustomerName,
CustomerNumber = x.CustomerNumber
},
// Adding this does throw
// InvalidCastException: Unable to cast object of type
// 'Microsoft.EntityFrameworkCore.Query.SqlExpressions.SqlFunctionExpression' to type
// 'System.Linq.Expressions.ConstantExpression'.
Documents = x.Documents.Select(d => new DocumentDomainModel
{
DocumentId = d.DocumentId,
DocumentNumber = d.DocumentNumber,
Category = d.Category,
Type = d.Type
})
})
// Done at logic
.GroupBy(p => p.InvoiceNumber)
.Select(g => new {g.Key, Count = g.Count()})
.ToList();
问题似乎是我们域模型投影中的文档选择问题。删除它可以使代码生效。
我也可以在分组之前添加.ToList()
来解决此问题。但是,这将在大量的性能问题中泄漏,因为数据库包含超过1.5亿个条目,并且所有这些条目都将被加载到内存中。这不是我们的选择。
我们是否遇到实体框架核心和linq的限制?
我们是否遇到实体框架核心和linq的限制?
[很遗憾,您遇到的是EF Core 3.x GroupBy
翻译错误。他们的GitHub问题跟踪器#20887 GroupBy causes exception中有一个类似的问题,目前,恕我直言,IMHO错误地将其关闭为#19929 Query: Support GroupBy when it is final operator的副本,因此我在此处发布了有关此帖子的评论。
目前的解决方法(不确定是否以及如何将其插入查询管道中)是预先选择(将其分为匿名类型或相同类型)对键/聚集进行分组所需的列,例如在GroupBy
之前插入以下内容:
.Select(p => new { p.InvoiceNumber })
或
.Select(p => new InvoiceDomainModel { InvoiceNumber = p.InvoiceNumber })