【发布时间】:2012-06-09 21:18:40
【问题描述】:
说,我有两个实体:
public class Customer
{
public int Id { get; set; }
public int SalesLevel { get; set; }
public string Name { get; set; }
public string City { get; set; }
}
public class Order
{
public int Id { get; set; }
public DateTime DueDate { get; set; }
public string ShippingRemark { get; set; }
public int? CustomerId { get; set; }
public Customer Customer { get; set; }
}
Customer 是Order 中的一个可选(可为空)引用(可能系统支持“匿名”订单)。
现在,我想将订单的一些属性投影到视图模型中,包括客户的一些属性如果订单有客户。那么我有两个视图模型类:
public class CustomerViewModel
{
public int SalesLevel { get; set; }
public string Name { get; set; }
}
public class OrderViewModel
{
public string ShippingRemark { get; set; }
public CustomerViewModel CustomerViewModel { get; set; }
}
如果Customer 是Order 中的必需 导航属性,我可以使用以下投影并且它有效,因为我可以确定任何@987654328 始终存在Customer @:
OrderViewModel viewModel = context.Orders
.Where(o => o.Id == someOrderId)
.Select(o => new OrderViewModel
{
ShippingRemark = o.ShippingRemark,
CustomerViewModel = new CustomerViewModel
{
SalesLevel = o.Customer.SalesLevel,
Name = o.Customer.Name
}
})
.SingleOrDefault();
但是当Customer 是可选的并且ID 为someOrderId 的订单没有客户时,这不起作用:
EF 抱怨
o.Customer.SalesLevel的具体化值是NULL并且不能存储在int中,不可为空的属性CustomerViewModel.SalesLevel。这并不奇怪,问题可以通过使CustomerViewModel.SalesLevel类型为int?(或通常所有属性都可以为空)来解决但我实际上更希望在订单没有客户时将
OrderViewModel.CustomerViewModel具体化为null。
为了实现这一点,我尝试了以下方法:
OrderViewModel viewModel = context.Orders
.Where(o => o.Id == someOrderId)
.Select(o => new OrderViewModel
{
ShippingRemark = o.ShippingRemark,
CustomerViewModel = (o.Customer != null)
? new CustomerViewModel
{
SalesLevel = o.Customer.SalesLevel,
Name = o.Customer.Name
}
: null
})
.SingleOrDefault();
但这会引发臭名昭著的 LINQ to Entities 异常:
无法创建“CustomerViewModel”类型的常量值。仅有的 原始类型(例如 ''Int32'、'String' 和 'Guid'')是 在这种情况下支持。
我猜: null 是CustomerViewModel 的“常量值”,这是不允许的。
由于似乎不允许分配null,我尝试在CustomerViewModel 中引入标记属性:
public class CustomerViewModel
{
public bool IsNull { get; set; }
//...
}
然后是投影:
OrderViewModel viewModel = context.Orders
.Where(o => o.Id == someOrderId)
.Select(o => new OrderViewModel
{
ShippingRemark = o.ShippingRemark,
CustomerViewModel = (o.Customer != null)
? new CustomerViewModel
{
IsNull = false,
SalesLevel = o.Customer.SalesLevel,
Name = o.Customer.Name
}
: new CustomerViewModel
{
IsNull = true
}
})
.SingleOrDefault();
这也不起作用并引发异常:
类型'CustomerViewModel'出现在两个结构上不兼容 单个 LINQ to Entities 查询中的初始化。一个类型可以是 在同一个查询中的两个地方初始化,但前提是相同 两个地方都设置了属性,并且这些属性设置在 相同的顺序。
异常很清楚如何解决问题:
OrderViewModel viewModel = context.Orders
.Where(o => o.Id == someOrderId)
.Select(o => new OrderViewModel
{
ShippingRemark = o.ShippingRemark,
CustomerViewModel = (o.Customer != null)
? new CustomerViewModel
{
IsNull = false,
SalesLevel = o.Customer.SalesLevel,
Name = o.Customer.Name
}
: new CustomerViewModel
{
IsNull = true,
SalesLevel = 0, // Dummy value
Name = null
}
})
.SingleOrDefault();
这可行,但用虚拟值或null 显式填充所有属性并不是一个很好的解决方法。
问题:
除了使
CustomerViewModel的所有属性都可以为空之外,最后一个代码 sn-p 是唯一的解决方法吗?难道根本不可能在投影中实现对
null的可选引用吗?您对如何处理这种情况有其他想法吗?
(我只是为这个问题设置了通用的 entity-framework 标签,因为我猜这个行为不是特定于版本的,但我不确定。我已经测试了上面的代码 sn-ps使用 EF 4.2/DbContext/Code-First。编辑:添加了另外两个标签。)
【问题讨论】:
-
您找到解决方案了吗?如果是这样你介意分享:)
-
@QuintonBernhardt:不,我没有找到解决方案。我主要通过使所有嵌套视图模型属性为空来解决此问题。
标签: .net entity-framework linq-to-entities