实体框架,包含相交表和相应的Linq查询



我是实体框架和Linq的新手。我使用。net Core和EF Core 5。到目前为止,我很喜欢它,但遇到了一些问题,我正在努力解决。这是我真正困惑和不理解的。

我有一些产品表示在一个类中。我有客户在另一个班级购买这些产品。每个客户可能在他们的业务中使用不同的名称来称呼我的一个产品,因此我需要允许他们为我的产品定义一个别名。

这是我的两个父类(Products &客户)

public class Product
{
public int Id {get; set;}
public Guid IdGuid {get; set;}
public string Name {get; set;}
}
public class Customer
{
public int Id {get; set;}
public Guid IdGuid {get; set;}
public string Name {get; set;}
}

在这些类中,我有一个Id列,用于数据库的引用完整性。我不把这个Id传回给用户。相反,每当用户获得这些对象之一时,他们都会获得返回的Guid,以便他们可以唯一地标识DB中的行。连接这两个表的别名类如下:

public class ProductCustomerAlias
{
public Product Product {get; set;}
public Customer Customer {get; set;}
public string Alias {get; set;}
}

因为我需要数据库表使用由Product Id和Customer Id组成的复杂键,所以我必须重写上下文对象的OnModelCreating方法:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<ProductCustomerAlias>()
.HasKey("ProductId", "CustomerId");
}
'''
This ends up creating a Database table that has the following structure (in Oracle)
'''
CREATE TABLE "RTS"."ProductCustomerAlias" 
(
"ProductId" NUMBER(10,0), 
"CustomerId" NUMBER(10,0), 
"Alias" NVARCHAR2(100)
)

到目前为止一切顺利。我现在有一个用于存储Alias的相交表,其中主键为ProductId和CustomerId,它们都是DB上下文中product和Customers的键整数值。

因此,我的下一步是开始创建从这些对象中检索数据的Repo类。请记住,提交请求的最终用户只能将Product传递给我。IdGuid和Customer。IdGuid,因为那是他们所有的东西。在我的Repo中我有以下代码:

public async Task<ProductCustomerAlias> GetProductCustomerAliasAsync(Guid pProductId, Guid pCustomerId)
{
var alias = await (from ali in _context.ProductCustomerAlias
join prod in _context.Products on ali.Product.Id equals prod.Id
join cust in _context.Customers on ali.Customer.Id equals cust.Id
where cust.IdGuid == pCustomerId && prod.IdGuid == pProductId
select new {Product = prod,
Customer = cust,
Alias = ali.Alias}
).FirstOrDefaultAsync();
return (IEnumerable<ProductCustomerAlias>)alias;
} 

我的问题是它给了我以下错误:

Cannot convert type '<anonymous type: Models.Product Product, Models.Customer Customer, string Alias>' to 'System.Collections.Generic.IEnumerable<Models.ProductCustomerAlias>' 

请不要告诉我错误告诉我到底是什么错了。我确信是这样,但如果我明白我在哪里搞砸了,我就不会浪费时间打出这么长得可笑的解释了。那么,我如何将结果从我的Linq查询转换为指定的类型?我还做错了什么吗?如有任何帮助,我将不胜感激。

回答您的具体问题

那么我如何将结果从我的Linq查询转换为指定的类型?

不能,因为LINQ查询结果是匿名类型,与期望的结果类型(具体实体类型)不兼容,因此不能强制转换为它。

还有什么我做错了吗?

很抱歉这么说,但基本上一切都是错的。

不需要
  • Select,因为所需的结果类型是正在查询的实体的确切类型。即这里的ali变量
from ali in _context.ProductCustomerAlias

正是您需要的结果(在应用过滤器和限制运算符之后)

  • 手动连接也不需要,因为它们是由导航属性自动提供的,即这里
join prod in _context.Products on ali.Product.Id equals prod.Id

prodali.Product完全相同

  • 试图将单个对象强制转换为可枚举对象是错误的
return (IEnumerable<ProductCustomerAlias>)alias

即使alias变量的类型是正确的,这也会失败,因为它是单个对象而不是集合。


因此,解决方案非常简单-使用相应的DbSet,应用过滤器(Where),将结果限制为零或1 (FirstOrDefault{Async}),然后完成。

有一个小细节。因为你正在查询和返回一个完整的实体,它的导航属性(如ProductCustomer)被认为是一个相关的数据,并且不会被EF Core自动填充(加载)。您必须明确地选择加入,这被称为渴望加载,并在EF Core官方文档的加载相关数据部分进行了解释(我还建议熟悉导航属性和整个关系概念)。简单来说,这需要使用专门提供的EF Core扩展方法IncludeThenInclude

说了这么多,解决方案是这样的:

public async Task<ProductCustomerAlias> GetProductCustomerAliasAsync(
Guid pProductId, Guid pCustomerId)
{
return await _context.ProductCustomerAlias
.Include(a => a.Product)
.Include(a => a.Customer)
.Where(a => a.Customer.IdGuid == pCustomerId && a.Product.IdGuid == pProductId)
.FirstOrDefaultAsync();
} 

您甚至可以将最后两行替换为对FirstOrDefaultAsync的谓词重载的单个调用,但这不是必需的,因为它只是上述

的快捷方式。

return await _context.ProductCustomerAlias
.Include(a => a.Product)
.Include(a => a.Customer)
.FirstOrDefaultAsync(a => a.Customer.IdGuid == pCustomerId && a.Product.IdGuid == pProductId);

最新更新