EF — свойство навигации имеет значение null даже после перезагрузки объекта, но работает при перезапуске программы.

РЕДАКТИРОВАТЬ

Я провел некоторое тестирование и обнаружил, что свойство навигации по элементам работает только тогда, когда контекст удален/создан новый контекст.

        DataContext context = new DataContext();
        Order ord = context.Orders.FirstOrDefault();
        ord.OrderItem.Add(new OrderItem() { ItemId = 8, Quantity = 2, DateCreated = DateTime.Now });
        // At this point, both Order and Item navigation property of the OrderItem are null

        context.SaveChanges();
        // After saving the changes, the Order navigation property of the OrderItem is no longer null, but points to the order. However, the Item navigation property is still null.

        ord = context.Orders.FirstOrDefault();
        // After retrieving the order from the context once again, Item is still null.

        context.Dispose();
        context = new DataContext();
        ord = context.Orders.FirstOrDefault();
        // After disposing of the context and creating a new one, then reloading the order, both Order and Item navigation props are not null anymore

Может кто-то объяснить это мне?

РЕДАКТИРОВАТЬ КОНЕЦ

В моей программе у пользователя есть список заказов, в который он может добавлять новые заказы. Пользователь также может добавлять элементы заказа в заказ, но это не работает должным образом, поскольку свойство навигации OrderItem -> Item имеет значение null даже после сохранения и перезагрузки заказа.

OrderItem
---------
OrderId
ItemId
Quantity
DateCreated
---------
Item <- navigation property
Order

Когда пользователь внес изменения в заказ и нажал кнопку сохранения, программа получает данные из представления, обновляет activeOrder и отправляет их в orderModel, добавляя его как новый заказ или обновляя существующий.

ПолучитьДанныеПорядкаИзВид() ..

        ...
        // Check for existing item, update if match found, add new item if not
        foreach (ItemViewObject oi in orderItems)
        {
            var existingOrderItem = activeOrder.OrderItem.Where(o => o.ItemId == oi.ItemId).SingleOrDefault();

            if (existingOrderItem != null)
            {
                existingOrderItem.Quantity = oi.Quantity;
            }
            else
            {
                activeOrder.OrderItem.Add(new OrderItem()
                {
                    ItemId = oi.ItemId,
                    Quantity = oi.Quantity,
                    DateCreated = DateTime.Now
                });
            }

Затем в классе orderModel...

    public void AddOrUpdate(Order order)
    {
        if (order.Id == 0)
        {
            context.Orders.Add(order);
        }

        context.SaveChanges();
    }

Затем таблица заказов обновляется, вызывая событие, запускающее метод OrderSelectionChanged(). В этот момент заказ перезагружается, извлекая его из модели заказа (возврат контекста.Заказы.Где...)...

        // Get values from selected order and populate controls
        if (view.OrderTable.SelectedRows.Count != 0)
        {
            OrderViewObject ovm = (OrderViewObject)view.OrderTable.SelectedRows[0].DataBoundItem;
            activeOrder = orderModel.GetById(ovm.OrderId);

            PopulateOrderItemTableControl();

Когда вызывается метод PopulateOrderItemTableControl(), у меня начинаются проблемы..

        foreach (OrderItem oi in activeOrder.OrderItem)
        {
            orderItems.Add(new ItemViewObject(oi));
        }

Потому что при создании нового ItemViewObject мне нужно получить элемент orderItem через его свойство навигации. Однако свойство навигации orderItem.Item имеет значение null, и здесь я получаю исключение...

    public ItemViewObject(OrderItem orderItem)
    {
        dateCreated = orderItem.DateCreated;

        // Retrieve latest item details, with effective date older or equal to the creation date of the order item
        var details = orderItem.Item.ItemDetails.Where(i => i.DateEffective  <= dateCreated)
                                                        .OrderByDescending(i => i.DateEffective)
                                                        .FirstOrDefault();

Затем мне нужно перезапустить программу, но после этого элементы заказа загружаются отлично. Таким образом, это происходит только при добавлении нового элемента заказа в заказ, сохранении заказа, а затем перезагрузке заказа и попытке отобразить его элементы заказа.

Кроме того, это происходит только с новыми товарами, которые ранее не были добавлены в заказ. Поэтому, если я создам новый элемент и добавлю его в заказ, нажму кнопку «Сохранить», я получу нулевое исключение и мне нужно будет перезапустить. После перезагрузки элемент загружается нормально. Если я затем удалю элемент заказа, сохраню заказ, повторно добавлю элемент и снова сохраню его, он не вылетает.

Надеюсь, это имеет смысл для кого-то.

Ваше здоровье!

Примечание. Сначала я использовал модель.


person Lahey    schedule 26.02.2014    source источник
comment
из любопытства, если вы загружаете объект по ключу оба раза, вы получаете одинаковое поведение?   -  person Moho    schedule 26.02.2014
comment
Да, это не имело значения.   -  person Lahey    schedule 26.02.2014


Ответы (1)


Поведение на самом деле ожидаемо и имеет объяснения:

DataContext context = new DataContext();
Order ord = context.Orders.FirstOrDefault();
ord.OrderItem.Add(new OrderItem() {
    ItemId = 8, Quantity = 2, DateCreated = DateTime.Now });
// At this point, both Order and Item navigation property of the OrderItem
// are null
// Explanation: That's clear because you don't set Order and Item property

context.SaveChanges();
// After saving the changes, the Order navigation property of the OrderItem
// is no longer null, but points to the order. However, the Item navigation
// property is still null.
// Explanation: SaveChanges internally fixes relationships with objects that are
// attached to the context. Order is attached, Item is not. That's why only
// the Order property is set

ord = context.Orders.FirstOrDefault();
// After retrieving the order from the context once again, Item is still null.
// Explanation: Because the Order is already attached a new query won't replace
// it. The entity remains the same as before.

context.Dispose();
context = new DataContext();
ord = context.Orders.FirstOrDefault();
// After disposing of the context and creating a new one, then reloading the
// order, both Order and Item navigation props are not null anymore
// Explanation: In a new context the Order will be loaded as a proxy. So, now
// lazy loading will work and load the Item property

Ключевая проблема здесь в том, что вы создаете новый OrderItem с оператором new:

ord.OrderItem.Add(new OrderItem() {
    ItemId = 8, Quantity = 2, DateCreated = DateTime.Now });

Это означает, что это не динамический прокси-сервер, способный загружать свойства навигации путем отложенной загрузки. Чтобы создать прокси вручную, вы должны использовать вместо этого:

var newOrderItem = context.OrderItems.Create();
newOrderItem.ItemId = 8;
newOrderItem.Quantity = 2;
newOrderItem.DateCreated = DateTime.Now;

ord.OrderItem.Add(newOrderItem);

После context.SaveChanges() теперь вы сможете загрузить свойство Item с помощью отложенной загрузки (на основе значения внешнего ключа ItemId). «Перезагружать» заказ с помощью context.Orders.FirstOrDefault() не нужно, и использовать новый контекст тоже не нужно.

person Slauma    schedule 26.02.2014