Entity Framework 4.3 Присоединение объекта из параметра действия контроллера - PullRequest
1 голос
/ 22 марта 2012

У меня есть ситуация, когда у меня есть объект, который загружается обратно из формы в контроллер MVC через действие. Мы не используем FormCollection, но тот, который использует непосредственно класс.

    [HttpPost]
    public ActionResult AjaxUpdate(Customer customer) { ...

Объект Customer содержит объект под названием customer, который, кажется, обновляется, но при использовании SaveDatabase () в контексте просто не работает.

Чтобы все заработало, мне пришлось использовать в действии:

myDbContext.Customers.Attach(customer) 
//...Code here that set to the customer.SubObject a real object from the database so I am sure that the SubObject contain an id which is valid and the datacontext is aware of it...
myDbContext.Entry(customer).State = EntityState.Modified; 

Тем не менее, у меня возникла исключительная ситуация, связанная с тем, что «оператор обновления магазина, вставки или удаления затронул неожиданное количество строк (0)», который мне удалось удалить с помощью:

Database.ObjectContext().Refresh(RefreshMode.ClientWins,customer);

Итак, чтобы перефразировать мой вопрос, почему мне нужно Присоединить + изменить состояние + вызвать Обновить. Нет ли лучшего способа обновить объект, содержащий объект, на который есть ссылки в другой таблице. Я использую Code сначала Entity Framework (объект Poco). Кроме того, я не люблю использовать Refresh, поскольку он скрыт от моего Databasecontext.

1 Ответ

2 голосов
/ 23 марта 2012

Я сделал консольный тестовый проект с EF 4.3.1. Код - это мое предположение о том, что вы подразумеваете под строкой с комментариями и вашими комментариями под вопросом (но мое предположение, вероятно, неверно, потому что программа не воспроизводит вашу ошибку):

Вы можете скопировать код в program.cs и добавить ссылку на EF 4.3.1:

using System.Data;
using System.Data.Entity;

namespace EFUpdateTest
{
    public class Customer
    {
        public int Id { get; set; }
        public string Name { get; set; }

        public int SubObjectId { get; set; }
        public SubObject SubObject { get; set; }
    }

    public class SubObject
    {
        public int Id { get; set; }
        public string Something { get; set; }
    }

    public class CustomerContext : DbContext
    {
        public DbSet<Customer> Customers { get; set; }
        public DbSet<SubObject> SubObjects { get; set; }
    }

    class Program
    {
        static void Main(string[] args)
        {
            int customerId = 0;
            int subObject1Id = 0;
            int subObject2Id = 0;
            using (var ctx = new CustomerContext())
            {
                // Create customer with subobject
                var customer = new Customer { Name = "John" };
                var subObject = new SubObject { Something = "SubObject 1" };
                customer.SubObject = subObject;
                ctx.Customers.Add(customer);

                // Create a second subobject, not related to any customer
                var subObject2 = new SubObject { Something = "SubObject 2" };
                ctx.SubObjects.Add(subObject2);

                ctx.SaveChanges();

                customerId = customer.Id;
                subObject1Id = subObject.Id;
                subObject2Id = subObject2.Id;
            }

            // New context, simulate detached scenario -> MVC action
            using (var ctx = new CustomerContext())
            {
                // Changed customer name
                var customer = new Customer { Id = customerId, Name = "Jim" };
                ctx.Customers.Attach(customer);

                // Changed reference to another subobject
                var subObject2 = ctx.SubObjects.Find(subObject2Id);
                customer.SubObject = subObject2;

                ctx.Entry(customer).State = EntityState.Modified;

                ctx.SaveChanges();
                // No exception here.
            }
        }
    }
}

Это работает без исключения. Вопрос в том, что отличается в вашем коде, что может вызвать ошибку?

Редактировать

На ваш комментарий, что у вас нет свойства внешнего ключа SubObjectId в классе клиента: если я удалю свойство в примере программы выше, я смогу воспроизвести ошибку.

Решение состоит в том, чтобы загрузить исходный подобъект из базы данных перед изменением отношения:

// Changed customer name
var customer = new Customer { Id = customerId, Name = "Jim" };
ctx.Customers.Attach(customer);

// Load original SubObject from database
ctx.Entry(customer).Reference(c => c.SubObject).Load();

// Changed reference to another subobject
var subObject2 = ctx.SubObjects.Find(subObject2Id);
customer.SubObject = subObject2;

ctx.Entry(customer).State = EntityState.Modified;

ctx.SaveChanges();
// No exception here.

Без свойства внешнего ключа у вас есть Независимая ассоциация , которая требует, чтобы объект , включая все ссылки , представлял состояние в базе данных перед его изменением. Если вы не установите ссылку SubObject в customer EF предполагает, что исходное состояние в базе данных - это то, что клиент не ссылается ни на какой подобъект. Сгенерированный SQL для оператора UPDATE содержит предложение WHERE, например:

WHERE [Customers].[Id] = 1 AND [Customers].[SubObject_Id] IS NULL

Если у клиента есть подобъект в БД [SubObject_Id] равен , а не NULL, условие не выполняется и ОБНОВЛЕНИЕ не происходит (или происходит для "неожиданного числа строк 0").

Проблема не возникает, если у вас есть свойство внешнего ключа ( Ассоциация внешнего ключа ): предложение WHERE в этом случае только:

WHERE [Customers].[Id] = 1

Таким образом, не имеет значения, каково первоначальное значение SubObject и SubObjectId. Вы можете оставить значения null, и, тем не менее, ОБНОВЛЕНИЕ работает.

Следовательно, альтернативное решение для загрузки исходного подобъекта - ввести свойство внешнего ключа в Customer:

public int SubObjectId { get; set; }

Или, если отношения не требуются:

public int? SubObjectId { get; set; }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...