Czy mogę rzutować opcjonalne odniesienie jednostki na opcjonalne odniesienie typu wyniku projekcji?
Powiedz, mam dwie jednostki:
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
jestopcjonalny (dopuszczalna) odwołanie wOrder
(może system obsługuje zamówienia „anonimowe”).
Teraz chcę wyświetlić niektóre właściwości zamówienia w modelu widoku, w tym niektóre właściwości klientaJeśli zamówienie ma klienta. Mam wtedy dwie klasy modelu widoku:
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; }
}
JeśliCustomer
byłoby awymagany właściwość nawigacji wOrder
Mogłem użyć następującej projekcji i działa, ponieważ mogę być pewien, że aCustomer
zawsze istnieje dla każdegoOrder
:
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();
Ale to nie działa, kiedyCustomer
jest opcjonalne, a zamówienie z IdsomeOrderId
nie ma klienta:
EF skarży się na zmaterializowaną wartośćo.Customer.SalesLevel
jestNULL
i nie mogą być przechowywane wint
, nie dopuszczalna wartośćCustomerViewModel.SalesLevel
. Nie jest to zaskakujące, a problem można rozwiązaćCustomerViewModel.SalesLevel
typuint?
(lub ogólniewszystko właściwości dopuszczalne)
Ale wolałbym toOrderViewModel.CustomerViewModel
jest zmaterializowany jakonull
kiedy zamówienie nie ma klienta.
Aby to osiągnąć, spróbowałem:
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();
Ale to powoduje niesławny wyjątek LINQ to Entities:
Nie można utworzyć stałej wartości typu „CustomerViewModel”. W tym kontekście obsługiwane są tylko typy prymitywne (na przykład „Int32”, „String” i „Guid”).
zgaduję, że: null
jest „stałą wartością”CustomerViewModel
co jest niedozwolone.
Od momentu przypisanianull
nie wydaje się być dozwolone Próbowałem wprowadzić właściwość znacznika wCustomerViewModel
:
public class CustomerViewModel
{
public bool IsNull { get; set; }
//...
}
A potem projekcja:
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();
To również nie działa i zgłasza wyjątek:
Typ „CustomerViewModel” pojawia się w dwóch strukturalnie niezgodnych inicjalizacjach w ramach pojedynczego zapytania LINQ to Entities. Typ można zainicjować w dwóch miejscach w tym samym zapytaniu, ale tylko wtedy, gdy te same właściwości są ustawione w obu miejscach, a te właściwości są ustawione w tej samej kolejności.
Wyjątek jest wystarczająco jasny, jak rozwiązać 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();
To działa, ale nie jest to bardzo przyjemne obejście, aby wypełnić wszystkie właściwości wartościami atrapowymi lubnull
jawnie.
Pytania:
Czy ostatni fragment kodu jest jedynym obejściem, oprócz tworzenia wszystkich właściwościCustomerViewModel
nieważne?
Czy po prostu nie można zmaterializować opcjonalnego odniesienia donull
w projekcji?
Czy masz alternatywny pomysł, jak poradzić sobie z tą sytuacją?
(Ustawiam tylko generałaramy podmiotu tag dla tego pytania, ponieważ myślę, że to zachowanie nie jest specyficzne dla wersji, ale nie jestem pewien. Testowałem fragmenty kodu powyżej z EF 4.2 /DbContext
/ Code-First. Edytuj: dodano jeszcze dwa tagi.)