说,我有两个实体:
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
我可以使用以下投影并且它可以工作,因为我可以确定 aCustomer
始终存在于 any Order
:
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
以为空之外,最后一个代码片段是唯一的解决方法吗?是否根本不可能
null
在投影中实现可选引用?你有其他想法如何处理这种情况吗?
(我只是为这个问题设置了一般实体框架标签,因为我猜这种行为不是特定于版本的,但我不确定。我已经用 EF 4.2 DbContext
//Code-First 测试了上面的代码片段。编辑:两个添加了更多标签。)