Kann ich eine optionale Referenz einer Entität in eine optionale Referenz des Ergebnistyps der Projektion projizieren?
Angenommen, ich habe zwei Entitäten:
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
ist einwahlweise (nullable) Referenz inOrder
(Vielleicht unterstützt das System "anonyme" Bestellungen).
Nun möchte ich einige Eigenschaften eines Auftrags in ein Ansichtsmodell projizieren, einschließlich einiger Eigenschaften des Kundenob Die Bestellung hat einen Kunden. Ich habe dann zwei Ansichtsmodellklassen:
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; }
}
Wenn dasCustomer
wäre einerforderlich Navigationseigenschaft inOrder
Ich könnte die folgende Projektion verwenden und es funktioniert, weil ich sicher sein kann, dass aCustomer
existiert immer für jedenOrder
:
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();
Das geht aber nicht wennCustomer
ist optional und die Bestellung mit IdsomeOrderId
hat keinen Kunden:
EF beschwert sich, dass der materialisierte Wert füro.Customer.SalesLevel
istNULL
und kann nicht im gespeichert werdenint
, nicht nullable EigenschaftCustomerViewModel.SalesLevel
. Das ist nicht überraschend und das Problem könnte gelöst werden, indem man machtCustomerViewModel.SalesLevel
vom Typint?
(oder allgemeinalles Eigenschaften nullable)
Aber das wäre mir eigentlich lieberOrderViewModel.CustomerViewModel
materialisiert sich alsnull
wenn die Bestellung keinen Kunden hat.
Um dies zu erreichen, habe ich Folgendes versucht:
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();
Dies löst jedoch die berüchtigte Ausnahme von LINQ to Entities aus:
Es kann kein konstanter Wert vom Typ 'CustomerViewModel' erstellt werden. In diesem Zusammenhang werden nur primitive Typen (z. B. "Int32", "String" und "Guid") unterstützt.
Ich vermute, dass: null
ist der "konstante Wert" fürCustomerViewModel
was nicht erlaubt ist.
Seit der Zuordnungnull
scheint nicht erlaubt zu sein Ich habe versucht, eine Marker-Eigenschaft in einzufügenCustomerViewModel
:
public class CustomerViewModel
{
public bool IsNull { get; set; }
//...
}
Und dann die Projektion:
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();
Dies funktioniert auch nicht und löst die Ausnahme aus:
Der Typ 'CustomerViewModel' wird in zwei strukturell inkompatiblen Initialisierungen innerhalb einer einzelnen LINQ to Entities-Abfrage angezeigt. Ein Typ kann an zwei Stellen in derselben Abfrage initialisiert werden, jedoch nur, wenn an beiden Stellen dieselben Eigenschaften festgelegt wurden und diese Eigenschaften in derselben Reihenfolge festgelegt wurden.
Die Ausnahme ist klar genug, um das Problem zu beheben:
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();
Dies funktioniert, aber es ist keine sehr gute Lösung, alle Eigenschaften mit Dummy-Werten oder zu füllennull
ausdrücklich.
Fragen:
Ist das letzte Codefragment die einzige Problemumgehung, abgesehen davon, dass alle Eigenschaften desCustomerViewModel
nullable?
Ist es einfach nicht möglich, einen optionalen Verweis auf zu materialisieren?null
in einer projektion?
Haben Sie eine alternative Idee, wie Sie mit dieser Situation umgehen sollen?
(Ich stelle nur den General einEntity-Framework Tag für diese Frage, weil ich vermute, dass dieses Verhalten nicht versionsspezifisch ist, aber ich bin nicht sicher. Ich habe die obigen Code-Schnipsel mit EF 4.2 / getestet.DbContext
/ Code-First. Bearbeiten: Zwei weitere Tags hinzugefügt.)