我是实体框架和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
prod
和ali.Product
完全相同
- 试图将单个对象强制转换为可枚举对象是错误的
return (IEnumerable<ProductCustomerAlias>)alias
即使alias
变量的类型是正确的,这也会失败,因为它是单个对象而不是集合。
因此,解决方案非常简单-使用相应的DbSet
,应用过滤器(Where
),将结果限制为零或1 (FirstOrDefault{Async}
),然后完成。
有一个小细节。因为你正在查询和返回一个完整的实体,它的导航属性(如Product
和Customer
)被认为是一个相关的数据,并且不会被EF Core自动填充(加载)。您必须明确地选择加入,这被称为渴望加载,并在EF Core官方文档的加载相关数据部分进行了解释(我还建议熟悉导航属性和整个关系概念)。简单来说,这需要使用专门提供的EF Core扩展方法Include
和ThenInclude
。
说了这么多,解决方案是这样的:
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);