Странное обновление сущностей в Entity Framework Code-First - PullRequest
6 голосов
/ 18 августа 2011

Я решаю проблему с обновлением объекта перед сохранением в базу данных и получаю странное поведение.

Я использую Entity Framework 4.1 Code-First в веб-приложении ASP.NET MVC 3. Вот модель:

public class Order
{
    public int OrderId { get; set; }
    public int CarId { get; set; }
    public DateTime BeginRentDate { get; set; }
    public DateTime EndRentDate { get; set; }
    public decimal RentPrice { get; set; }
    public virtual Car Car { get; set; }
}

public class Car
{
    public int CarId { get; set; }
    public string Brand { get; set; }
    public string Model { get; set; }
    public string NumberPlate { get; set; }
    public decimal RentPrice { get; set; }
}

У каждого автомобиля есть RentPrice. Эта цена должна быть скопирована в RentPrice Заказа при его создании. Автомобиль выбирается пользователем, поэтому изначально Order.RentPrice равен 0.

Здесь я хочу скопировать значение цены:

[HttpPost]
public ActionResult Create(Order order)
{
    order.RentPrice = _context.Cars.Find(order.CarId).RentPrice;

    if (ModelState.IsValid)
    {
        _context.Orders.Add(order);
        _context.SaveChanges();
        return RedirectToAction("Index");
    }

    return View(order);
}

Это не работает из-за ошибки на SaveChanges, что у объекта есть ошибки проверки. ХОРОШО. Я обнаружил, что сначала нужно вызвать UpdateModel(order);, а затем изменить значения.

Так что у меня есть. Рабочий код:

_context.Orders.Add(order);
UpdateModel(order);
order.RentPrice = 777;
_context.SaveChanges();

Не рабочий код:

_context.Orders.Add(order);
UpdateModel(order);
order.RentPrice = _context.Cars.Find(order.CarId).RentPrice;
_context.SaveChanges();

Рабочий код (!):

_context.Orders.Add(order);
UpdateModel(order);
var t = (double)_context.Cars.Find(order.CarId).RentPrice;
order.RentPrice = (decimal)t;
_context.SaveChanges();

Может кто-нибудь объяснить, пожалуйста, что здесь происходит? Особенно магия на 3-й и 4-й строках в последнем блоке кода.

Обновление

Я получаю DbEntityValidationException: «Проверка не удалась для одной или нескольких сущностей. См. Свойство EntityValidationErrors для получения дополнительной информации». Из внутреннего исключения: «OriginalValues ​​нельзя использовать для сущностей в состоянии« Добавлен ».

Ответы [ 5 ]

18 голосов
/ 20 ноября 2012

Когда вы получаете

«Проверка не удалась для одного или нескольких объектов. Для получения дополнительной информации см. Свойство EntityValidationErrors».Из внутреннего исключения: «OriginalValues ​​нельзя использовать для сущностей в состоянии« Добавлен ».

Это означает, что имелись такие ошибки, как пустые столбцы NOT NULL или другие ограничения, проверьте ошибки проверки сущности с помощьюотладка или как

try{
...
catch ( DbEntityValidationException ex )
   {
foreach ( var validationErrors in ex.EntityValidationErrors )
    {
     foreach ( var validationError in validationErrors.ValidationErrors )
     {
      System.Diagnostics.Trace.TraceInformation( "Property: {0} Error: {1}", validationError.PropertyName, validationError.ErrorMessage );
     }
    }
}
1 голос
/ 01 марта 2016

OriginalValues ​​нельзя использовать для сущностей в состоянии добавлено.

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

modelBuilder
    .Entity<T>()
    .HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
    // this can also be achieved using attributes on the entity.

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

SELECT [PrimaryKeyId]
FROM [dbo].[Entity]
WHERE @@ROWCOUNT > 0 AND [PrimaryKeyId] = scope_identity()
/* SP:StmtCompleted... */

Этот оператор не будет возвращать никаких строк для неидентичных столбцов. Следовательно, OriginalValues останется неизменным в добавленном состоянии. Это также объясняет, почему это исключение заключено в OptimisticConcurrencyException, поскольку число затронутых строк используется для обнаружения существующих измененных данных.

0 голосов
/ 23 мая 2014

Я уверен, что проблема была бы исправлена ​​давным-давно, просто хотел внести свои 2 цента.

Я также столкнулся с проблемой с тем же сообщением об ошибке, я использовал Oracle и EF 5.0. Наконец я получил решение после того, как потратил больше 12 часов.

Для меня это было отсутствие разрешений для пользователя в таблице, в которую я пытался вставить значения, а в сообщении об ошибке не было никаких намеков на фактическую проблему с разрешениями, что было действительно странно.

Надеюсь, кто-то найдет это полезным и не тратит часы, как я, на устранение неполадок.

Приветствия

Avi

0 голосов
/ 11 ноября 2011

У меня есть идея, но это скорее предположение ... Может быть, к тому времени, когда вы попадете в эту функцию Create, контекст уже знает об этом Order? Если это произойдет, попытка добавить его снова может привести к этой ошибке.

В своем последнем куске кода (тот, который удивительно работает), вы пытались использовать промежуточную переменную var без приведения к удвоению?

Я также заинтригован этой UpdateModel ... Я использую EF, но не MVC, так что, возможно, это не имеет ничего общего с этим, но если это пользовательская функция, что она делает?

0 голосов
/ 19 августа 2011

Вы объявили первичный ключ где-нибудь? Вы должны сделать это либо с помощью FluentAPI, либо с помощью такого атрибута:

public class Order
{
    [Key]
    public int OrderId { get; set; }
    public int CarId { get; set; }
    public DateTime BeginRentDate { get; set; }
    public DateTime EndRentDate { get; set; }
    public decimal RentPrice { get; set; }
    public virtual Car Car { get; set; }
}

public class Car
{
    [Key]
    public int CarId { get; set; }
    public string Brand { get; set; }
    public string Model { get; set; }
    public string NumberPlate { get; set; }
    public decimal RentPrice { get; set; }
}

Или в своем контексте вы можете использовать Fluent API для объявления ключа, как это

builder.Entity<Car>().HasKey(x=>x.CarId);
builder.Entity<Order>().HasKey(x=>x.OrderId);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...