在LINQ中设置DefaultIfEmpty方法的默认值(当默认值具有多个关系时)?
我有三个目标:在LINQ中设置DefaultIfEmpty方法的默认值(当默认值具有多个关系时)?,linq,entity-framework-6,Linq,Entity Framework 6,我有三个目标: 事件(关系:有许多EventProducts) 产品(关系:有许多EventProducts) EventProduct(关系:对于多个事件,对于多个产品,具有销售数量、分配数量等属性,这些属性应存储在每个产品和每个事件中) 在我的应用程序中,当有人单击某个事件时,一个新窗口将初始化为所有产品的列表。从那里,他们可以修改我需要的单元格,以便在WPF中填充数据网格,这样当有人单击事件时。因此,在这段代码的上下文中,事件是已知的且恒定的 我遇到的问题是如何创建EventProduct
EventProduct.Event=currentEvent
实例化一个EventProduct(使用此查询创建的所有EventProducts的currentEvent都将是常量)和EventProduct.Product=Product
(每行的Product都将更改)
当数据库中存在关联的EventProduct时,此代码运行良好。但是如果没有,我的选择返回的产品很好,但是整个EventProduct为空
var query2 = from product in dbContext.Products
join eventProduct in dbContext.EventProducts
on new { pIndex = product.index, eIndex = currentEvent.index }
equals new { pIndex = eventProduct.Product.index, eIndex = eventProduct.Event.index } into temp
from eventProduct in temp.DefaultIfEmpty() // this is my problem line
select new {
Product = product,
EventProduct = eventProduct
};
我曾尝试为EventProduct(Event e,Product p)
创建一个构造函数,并在DefaultIfEmpty()
方法中将值传递给构造函数,但我发现错误,我的构造函数必须有0个参数才能以这种方式使用。我不能这样做,因为如果我这样做,就无法告诉我的EventProduct()对象它应该与哪个事件和产品关联
我也没有尝试过构造函数,只是创建了一个新的EventProduct并设置了它的属性,但是我得到了错误“实体或复杂类型…EventProduct不能在LINQ to Entites查询中构造”
在最终结果中,我想选择我的产品
和事件产品
。如果没有与该Product
和Event
关联的EventProduct
,则我的EventProduct选择应设置为默认值,其中currentEvent、当前行的Product和所有属性都设置为默认值(在本例中均为小数,并且应为0)
编辑:我刚刚尝试了此查询,它还提供了一个不受支持的错误:
var query2 = from product in dbContext.Products
join eventProduct in dbContext.EventProducts
on new { pIndex = product.index, eIndex = currentEvent.index }
equals new { pIndex = eventProduct.Product.index, eIndex = eventProduct.Event.index } into temp
from eventProduct in temp.DefaultIfEmpty()
select new {
Product = product,
EventProduct = eventProduct != null ?
eventProduct : new EventProduct
{
Product = product,
Event = currentEvent,
quantity_allocated = 0,
quantity_sold = 0,
quantity_sampled = 0
}
};
编辑:使用此技术解决:
1) 创建对象,因为匿名对象是只读的:
class Associations
{
public class ProductEventProduct
{
public Product Product { get; set; }
public EventProduct EventProduct { get; set; }
}
}
2) 数据集中的Foreach空对象,替换为默认对象
var query = from product in dbContext.Products
join eventProduct in dbContext.EventProducts
on new { pIndex = product.index, eIndex = currentEvent.index }
equals new { pIndex = eventProduct.Product.index, eIndex = eventProduct.Event.index } into temp
from eventProduct in temp.DefaultIfEmpty()
select new Associations.ProductEventProduct {
Product = product,
EventProduct = eventProduct
};
var dataSource = query.ToList();
foreach (Associations.ProductEventProduct entry in dataSource)
{
if (entry.EventProduct == null)
{
entry.EventProduct = new EventProduct
{
Product = entry.Product,
Event = currentEvent,
quantity_allocated = 0,
quantity_sold = 0,
quantity_sampled = 0
};
}
}
您遇到的问题似乎是,您要求EntityFramework创建一个查询,但它不知道如何创建 请记住,Linq使用延迟执行,因此当您编写查询时,它只是一个占位符,说明在哪里以及如何获取数据,而不是实际数据。直到其他一些代码请求数据时,查询才会实际运行并填充您的信息 对于LINQtoEntities,这意味着它将用DB语言构建一个查询,并一直保持查询,直到您需要数据,然后在调用时通过您使用的任何DB提供程序按预期执行查询,并将其存储在内存中以供使用 因此,当您试图在查询中构造一个新的
EventProduct
来填充空值时,DB不知道这是什么,您会得到错误,因为您仍然处于DB负责对象的查询部分。DB提供程序不知道如何构造新对象,因此EF无法转换查询,因此您会得到错误
解决方案是,在尝试构造新的EventProduct之前,通过调用ToList()
或ToArray
或类似命令来运行无EventProduct构造函数的查询,从而在此时强制运行查询,从而“水合”信息。
然后,在数据水合(在内存中)后,检查您在查询中创建的所有对象,如果EventProduct
为空,则构建一个新对象并动态添加它,不管您想怎么做。(第二个查询可能包含Select语句?)
我有点不了解您的原始查询(生成匿名对象)是否会被DBContext跟踪,但请记住这一点,并对其进行测试。如果添加一组绑定到这些匿名对象的EventProduct
实例,则默认情况下可以在跟踪中包含这些实例,并且调用SaveChanges()
可能会在无意中将它们全部写入数据库。要记住的东西
希望这有帮助,让我知道 您遇到的问题似乎是,您要求Entity Framework创建一个查询,但它不知道如何创建该查询 请记住,Linq使用延迟执行,因此当您编写查询时,它只是一个占位符,说明在哪里以及如何获取数据,而不是实际数据。直到其他一些代码请求数据时,查询才会实际运行并填充您的信息 对于LINQtoEntities,这意味着它将用DB语言构建一个查询,并一直保持查询,直到您需要数据,然后在调用时通过您使用的任何DB提供程序按预期执行查询,并将其存储在内存中以供使用 因此,当您试图在查询中构造一个新的
EventProduct
来填充空值时,DB不知道这是什么,您会得到错误,因为您仍然处于DB负责对象的查询部分。DB提供程序不知道如何构造新对象,因此EF无法转换查询,因此您会得到错误
解决的办法是在你试图欺骗之前先“补充”你的信息
......
select new
{
Product = product,
EventProduct = eventProduct != null
? eventProduct
: new EventProduct { .... set default values}
}
select new
{
Product = product,
EventProduct = new
{
Product = product,
EventId = currentEvent.Id, // possibly you will need to copy every field by hand
quantity_allocated = eventProduct == null ? 0 : eventProduct .quantity_allocated ,
....
}
}
var query2 = from product in dbContext.Products
join eventProduct in dbContext.EventProducts
on new { pIndex = product.index, eIndex = currentEvent.index }
equals new { pIndex = eventProduct.Product.index, eIndex = eventProduct.Event.index } into temp
from eventProduct in temp.DefaultIfEmpty()
select new {
Product = product,
EventProduct = eventProduct
};
var results = query2.ToList();
foreach(var r in results)
{
r.EventProduct = r.EventProduct != null
? r.EventProduct
: new EventProduct { .... set default values}
}