13

たとえば、次の 2 つのエンティティがあるとします。

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オプションの(nullable) 参照です (Orderシステムが「匿名」注文をサポートしている可能性があります) 。

ここで、注文に顧客がいる場合、注文のいくつかのプロパティを顧客のいくつかのプロパティを含むビュー モデルに投影したいと考えています。私は2つのビューモデルクラスを持っています:

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、次のプロジェクションを使用できCustomerます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 は、具体化された値が であり、null 許容ではないプロパティに格納できないと不平を言っo.Customer.SalesLevelていNULLます。それは驚くべきことではなく、型を作成することで問題を解決できます(または、通常はすべてのプロパティを null 可能にします) 。intCustomerViewModel.SalesLevelCustomerViewModel.SalesLevelint?

  • しかし、実際には、注文に顧客がいない場合のように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 クエリ内の構造的に互換性のない 2 つの初期化に表示されます。型は、同じクエリの 2 つの場所で初期化できますが、同じプロパティが両方の場所で設定され、それらのプロパティが同じ順序で設定されている場合に限ります。

例外は、問題を解決する方法を十分に明確にしています。

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これは機能しますが、すべてのプロパティをダミー値または明示的に埋めることはあまり良い回避策ではありません。

質問:

  1. CustomerViewModelnullableのすべてのプロパティを作成する以外に、最後のコード スニペットが唯一の回避策ですか?

  2. null投影でオプションの参照を具体化することは単に不可能ですか?

  3. この状況に対処するための別のアイデアはありますか?

(この動作はバージョン固有ではないと思うため、この質問には一般的なエンティティ フレームワークタグのみを設定していますが、よくわかりません。上記のコード スニペットを EF 4.2/ DbContext/Code-First でテストしました。編集: 2タグが追加されました。)

4

1 に答える 1

3

プロジェクションを DbQuery の IQueryable 実装でも動作させることはできません。回避策を探している場合は、データが Db から取得された後にプロジェクションを実行しないでください。これは EF DbQuery ではなくなりました...

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

欠点は、Db からすべての Order 列と Customer 列を取得していることです。これを制限するには、必要な列のみを Order から匿名型に選択してから...

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();
于 2012-09-04T06:13:41.837 に答える