我可以投射的实体的可选参考到投影的结果类型的可选参考? [英] Can I project an optional reference of an entity into an optional reference of the projection's result type?

查看:263
本文介绍了我可以投射的实体的可选参考到投影的结果类型的可选参考?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我说,我有两个实体:

 公共类客户
{
    公众诠释编号{获得;组; }
    公众诠释SalesLevel {获得;组; }
    公共字符串名称{;组; }
    公共字符串市{获得;组; }
}

公共类订单
{
    公众诠释编号{获得;组; }
    公众的DateTime交货期{获得;组; }
    公共字符串ShippingRemark {获得;组; }

    公众诠释?客户ID {获得;组; }
    公众客户客户{获得;组; }
}
 

客户的(可为空)的订单引用(也许是系统支持匿名订单)。

现在,我希望以一个订单的某些属性到视图模型,包括客户如果的顺序有客户的某些属性。我有两个视图模型类的话:

 公共类CustomerViewModel
{
    公众诠释SalesLevel {获得;组; }
    公共字符串名称{;组; }
}

公共类OrderViewModel
{
    公共字符串ShippingRemark {获得;组; }
    公共CustomerViewModel CustomerViewModel {获得;组; }
}
 

如果在客户将是一个的需要的在订单导航属性我可以使用以下投影和它的作品,因为我可以肯定,一个客户总是存在任何订单

  OrderViewModel视图模型= context.Orders
    。凡(O => o.Id == someOrderId)
    。选择(O =>新建OrderViewModel
    {
        ShippingRemark = o.ShippingRemark,
        CustomerViewModel =新CustomerViewModel
        {
            SalesLevel = o.Customer.SalesLevel,
            NAME = o.Customer.Name
        }
    })
    .SingleOrDefault();
 

但是,这并不正常工作时,客户是可选的,ID为 someOrderId 的顺序没有一个客户:

  • EF抱怨,对于物化值 o.Customer.SalesLevel NULL 并不能储存在 INT ,不能为空性 CustomerViewModel.SalesLevel 。这并不奇怪,问题可以通过解决 CustomerViewModel.SalesLevel 类型诠释?(或一般的所有的特性可为空)

  • 不过,我真的preFER的 OrderViewModel.CustomerViewModel 被物化为当为了有没有客户。

要做到这一点,我尝试了以下内容:

  OrderViewModel视图模型= context.Orders
    。凡(O => o.Id == someOrderId)
    。选择(O =>新建OrderViewModel
    {
        ShippingRemark = o.ShippingRemark,
        CustomerViewModel =(o.Customer!= NULL)
            ?新CustomerViewModel
              {
                  SalesLevel = o.Customer.SalesLevel,
                  NAME = o.Customer.Name
              }
            : 空值
    })
    .SingleOrDefault();
 

但是,这引发了臭名昭著的LINQ到实体除外:

  

无法创建类型的常量值'CustomerViewModel。只要   基本类型(例如''的Int32,串UND的Guid'')是   在这方面的支持。

我想这:空是固定值对 CustomerViewModel 这是不允许的。

由于分配似乎没有被允许我试图在引入标记属性 CustomerViewModel

 公共类CustomerViewModel
{
    公共BOOL ISNULL {获得;组; }
    // ...
}
 

然后投影:

  OrderViewModel视图模型= context.Orders
    。凡(O => o.Id == someOrderId)
    。选择(O =>新建OrderViewModel
    {
        ShippingRemark = o.ShippingRemark,
        CustomerViewModel =(o.Customer!= NULL)
            ?新CustomerViewModel
              {
                  ISNULL =假,
                  SalesLevel = o.Customer.SalesLevel,
                  NAME = o.Customer.Name
              }
            新CustomerViewModel
              {
                  ISNULL =真
              }
    })
    .SingleOrDefault();
 

这也不行,并抛出该异常:

  

类型CustomerViewModel出现在两个结构不兼容   在一个单一的LINQ to Entities查询初始化。 A型可   在两个地方初始化中相同的查询,但只有在使用相同   属性被设置在两地和这些属性的设置   顺序相同。

唯一的例外是很清楚如何解决这个问题:

  OrderViewModel视图模型= context.Orders
    。凡(O => o.Id == someOrderId)
    。选择(O =>新建OrderViewModel
    {
        ShippingRemark = o.ShippingRemark,
        CustomerViewModel =(o.Customer!= NULL)
            ?新CustomerViewModel
              {
                  ISNULL =假,
                  SalesLevel = o.Customer.SalesLevel,
                  NAME = o.Customer.Name
              }
            新CustomerViewModel
              {
                  ISNULL = TRUE,
                  SalesLevel = 0,//虚拟价值
                  NAME =空
              }
    })
    .SingleOrDefault();
 

本作品,但它不是一个很好的解决办法,以填补所有属性与虚值或明确。

问题:

  1. 是最后code作出的 CustomerViewModel 可空?

  2. 的所有属性片断中,唯一的解决方法,除了
  3. 这是根本不可能兑现的可选参照在投影?

  4. 你有一个替代知道如何应对这种情况?

(我只设置普通的实体框架的标签这个问题,因为我想这种行为是不特定的版本,但我不知道。我已经测试code片段以上EF 4.2 / 的DbContext / code-首先编辑:。另外两个标签添加)

解决方案

我不能让投影上的IQueryable实施的DBQuery的工作之一。如果你正在寻找一个解决办法又何乐而不为呢投影后的数据被检索的数据库,它不是一个EF的DBQuery了...

  OrderViewModel视图模型= context.Orders
    。凡(O => o.Id == someOrderId)
     //从数据库第一 - 没有更多的DBQuery
    .ToList()
    。选择(O =>新建OrderViewModel
    {
        ShippingRemark = o.ShippingRemark,
        CustomerViewModel = o.Customer == NULL?空:新CustomerViewModel
        {
            SalesLevel = o.Customer.SalesLevel,
            NAME = o.Customer.Name
        }
    })
    .SingleOrDefault();
 

缺点是你获取所有的订单和客户列从DB。您可以从订单只选择您需要的列到一个匿名类型限制这一点,然后...

  OrderViewModel视图模型= context.Orders
    。凡(O => o.Id == someOrderId)
    。选择(O =>新建{ShippingRemark = o.ShippingRemark,客户= o.Customer})
     //从数据库第一 - 没有更多的DBQuery
    .ToList()
    。选择(O =>新建OrderViewModel
    {
        ShippingRemark = o.ShippingRemark,
        CustomerViewModel = o.Customer == NULL?空:新CustomerViewModel
        {
            SalesLevel = o.Customer.SalesLevel,
            NAME = o.Customer.Name
        }
    })
    .SingleOrDefault();
 

Say, I have two entities:

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 is an optional (nullable) reference in Order (maybe the system supports "anonymous" orders).

Now, I want to project some properties of an order into a view model including some properties of the customer if the order has a customer. I have two view model classes then:

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; }
}

If the Customer would be a required navigation property in Order I could use the following projection and it works because I can be sure that a Customer always exists for 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();

But this does not work when Customer is optional and the order with Id someOrderId does not have a customer:

  • EF complains that the materialized value for o.Customer.SalesLevel is NULL and cannot be stored in the int, not nullable property CustomerViewModel.SalesLevel. That's not surprising and the problem could be solved by making CustomerViewModel.SalesLevel of type int? (or generally all properties nullable)

  • But I would actually prefer that OrderViewModel.CustomerViewModel is materialized as null when the order has no customer.

To achieve this I tried the following:

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();

But this throws the infamous LINQ to Entities exception:

Unable to create a constant value of type 'CustomerViewModel'. Only primitive types (for instance ''Int32', 'String' und 'Guid'') are supported in this context.

I guess that : null is the "constant value" for CustomerViewModel which is not allowed.

Since assigning null does not seem to be allowed I tried to introduce a marker property in CustomerViewModel:

public class CustomerViewModel
{
    public bool IsNull { get; set; }
    //...
}

And then the projection:

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();

This doesn't work either and throws the exception:

The type 'CustomerViewModel' appears in two structurally incompatible initializations within a single LINQ to Entities query. A type can be initialized in two places in the same query, but only if the same properties are set in both places and those properties are set in the same order.

The exception is clear enough how to fix the problem:

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();

This works but it's not a very nice workaround to fill all properties with dummy values or null explicitly.

Questions:

  1. Is the last code snippet the only workaround, aside from making all properties of the CustomerViewModel nullable?

  2. Is it simply not possible to materialize an optional reference to null in a projection?

  3. Do you have an alternative idea how to deal with this situation?

(I'm only setting the general entity-framework tag for this question because I guess this behaviour is not version specific, but I am not sure. I have tested the code snippets above with EF 4.2/DbContext/Code-First. Edit: Two more tags added.)

解决方案

I cannot get the projection to work on the IQueryable implementation of DbQuery either. If you're looking for a workaround then why not do the projection after the data has been retrieved from the Db and it's not an E.F. DbQuery anymore...

OrderViewModel viewModel = context.Orders
    .Where(o => o.Id == someOrderId)
     // get from db first - no more DbQuery
    .ToList()
    .Select(o => new OrderViewModel
    {
        ShippingRemark = o.ShippingRemark,
        CustomerViewModel = o.Customer == null ? null : new CustomerViewModel
        {
            SalesLevel = o.Customer.SalesLevel,
            Name = o.Customer.Name
        }
    })
    .SingleOrDefault();

The downside is you're fetching all the Order and Customer columns from the Db. You can limit this by selecting only the columns you require from Order into an anonymous type and then...

OrderViewModel viewModel = context.Orders
    .Where(o => o.Id == someOrderId)
    .Select(o => new { ShippingRemark = o.ShippingRemark, Customer = o.Customer })
     // get from db first - no more DbQuery
    .ToList()
    .Select(o => new OrderViewModel
    {
        ShippingRemark = o.ShippingRemark,
        CustomerViewModel = o.Customer == null ? null : new CustomerViewModel
        {
            SalesLevel = o.Customer.SalesLevel,
            Name = o.Customer.Name
        }
    })
    .SingleOrDefault();

这篇关于我可以投射的实体的可选参考到投影的结果类型的可选参考?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆