Entity Framework Code-First 中实体更新异常

6

我正在解决保存到数据库之前更新实体的问题,并得到了奇怪的行为。

我在ASP.NET MVC 3 Web应用程序中使用Entity Framework 4.1 Code-First。这是模型:

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; }
}

每辆车都有一个租赁价格。在创建订单时,应将此价格复制到订单的租赁价格中。用户选择车辆,因此最初订单的租赁价格为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();

请问有人能解释一下这里发生了什么吗?特别是最后一段代码中第三行和第四行的magic

更新

我得到了DbEntityValidationException:"验证一个或多个实体失败。有关更多详细信息,请参见'EntityValidationErrors'属性。" 从内部异常来看:"Added"状态的实体不能使用OriginalValues。


1
你有没有注意到在代码的倒数第二个块中,order.RentPrice = _context.Cars.Find(order.CarId).RentPrice;; 的末尾有两个分号? - Jack
1
SaveChanges方法上有哪些验证错误?你最初的Create方法中的代码对我来说运行良好。 - agradl
@Jack,发帖时只是打错了几个字。已经修正。 - kyrylomyr
@shiznit123,我已经添加了异常处理代码,可以解决所有无法工作的示例问题。 - kyrylomyr
你的第一个代码片段中的post操作是否也会出现相同的验证错误“无法对添加状态的实体使用OriginalValues”?我简直不敢相信...如果不是,那么这段代码中的验证错误是什么?我相信这里有重要的提示。 - Slauma
@Slauma,是的。完全相同的异常。 - kyrylomyr
5个回答

18

当您遇到以下错误信息:

"Validation failed for one or more entities. See 'EntityValidationErrors' property for more details." From the inner exception: "OriginalValues cannot be used for entities in the Added state."

这意味着存在错误,例如空白的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 );
     }
    }
}

抱歉,我没有对你的原始问题做出解释,只能给你一个提示,如何从DbEntityValidationException中获取错误信息。 - John
在我的情况下,我发现我的注册视图模型和身份模型之间存在不匹配的数据注释。 - Dreamcasting

1

在添加状态下,无法使用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
你是否在任何地方声明了主键?你应该使用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; }
}

或者在您的上下文中,您可以使用流畅的 API 声明键,就像这样

builder.Entity<Car>().HasKey(x=>x.CarId);
builder.Entity<Order>().HasKey(x=>x.OrderId);

EF使用约定优于配置(它使用<name>Id属性)。所以,是的,我的数据库有主键。我检查过了。那么,它如何解释最后一段代码呢?.. - kyrylomyr

0

我相信这个问题早就被解决了,只是想贡献我的意见。

我也遇到了同样错误信息的问题,我使用的是Oracle和EF 5.0。最终,在浪费了12个多小时后,我找到了一个解决方案。

对我来说,问题在于用户对我尝试插入值的表格缺乏权限,而错误信息绝对没有任何关于实际权限问题的提示,这真的很奇怪。

希望有人会发现这个有用,并且不像我一样浪费时间进行故障排除。

祝好,

Avi


0

我有一个想法,但更像是猜测...也许在你进入那个Create函数时,上下文已经知道了那个Order?如果是这样的话,试图重新添加它可能会导致错误。

在你最后一段代码(令人惊讶地工作)中,你是否尝试过使用一个中间变量而不是转换为double?

我也对UpdateModel很感兴趣...我使用EF但不是MVC,所以可能与此无关,但如果它是一个自定义函数,它是做什么的?


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接