ASP.NET MVC OptimisticConcurrencyException

时间:2017-01-09 12:41:15

标签: c# entity-framework optimistic-concurrency

我正在尝试在一个函数中写入数据库中的两个表,该函数将列表作为上一页的参数。我调用db来检索purchase_order_no,因为该列是在条目时生成的IDENTITY主键。

型号:

PURCHASE_ORDER

    [Key]
    [Column(Order = 0)]
    [DatabaseGenerated(DatabaseGeneratedOption.None)]
    public int client_no { get; set; }

    [Key]
    [Column(Order = 1)]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int purchase_order_no { get; set; }

    [StringLength(60)]
    public string name { get; set; }

    [Key]
    [Column(Order = 2)]
    [DatabaseGenerated(DatabaseGeneratedOption.None)]
    public int order_group_no { get; set; }

    [StringLength(24)]
    public string purchase_order_reference { get; set; }

    [Key]
    [Column(Order = 3)]
    public DateTime order_timestamp { get; set; }

order_detail

    [Key]
    [Column(Order = 0)]
    public long order_detail_no { get; set; }

    [Key]
    [Column(Order = 1)]
    [DatabaseGenerated(DatabaseGeneratedOption.None)]
    public int purchase_order_no { get; set; }

    [Key]
    [Column(Order = 2)]
    [DatabaseGenerated(DatabaseGeneratedOption.None)]
    public int inventory_no { get; set; }

    [Key]
    [Column(Order = 3)]
    public decimal quantity { get; set; }

    public int? vendor_no { get; set; }

尝试将新的“purchase_order”模型插入db:

时收到此错误
  

存储更新,插入或删除语句会影响意外的行数(0)。自实体加载后,实体可能已被修改或删除。有关理解和处理乐观并发异常的信息,请参阅http://go.microsoft.com/fwlink/?LinkId=472540

    [HttpPost]
    public ActionResult orderForm (List<int> quantity, List<string> itemName, List<int> inventory_no, List<int> client_no, List<int> vendorCode, int orderGroupNo)
    {
        using (var db = new db_model())
        {
            var ctx = ((IObjectContextAdapter)db).ObjectContext;
            purchaseOrderVM poVM = new purchaseOrderVM();
            List<order_detail> tempList = new List<order_detail>();
            purchase_order po = new purchase_order();
            po.client_no = client_no[0];
            var purchaseOrder = db.purchase_order.Where(x => x.client_no == po.client_no).Max(x => x.purchase_order_no);
            po.order_group_no = orderGroupNo;
            po.order_timestamp = DateTime.Now;
            db.purchase_order.Add(po);
            try
            {
                db.SaveChanges(); <!-- This is where I get the error -->
            }
            catch (OptimisticConcurrencyException e)
            {
                ctx.Refresh(RefreshMode.ClientWins, db.purchase_order);
                throw e;

            }
            for (int i = 0; i < itemName.Count(); i++)
            {
                order_detail od = new order_detail();
                od.purchase_order_no = db.purchase_order.Where(x => x.client_no == po.client_no).Max(x => x.purchase_order_no);
                od.inventory_no = inventory_no[i];
                od.quantity = quantity[i];
                od.vendor_no = vendorCode[i];
                db.order_detail.Add(od);
                try
                    {
                        db.SaveChanges();
                    }
                catch (OptimisticConcurrencyException e)
                    {
                        ctx.Refresh(RefreshMode.ClientWins, db.order_detail);
                        throw e;
                    }
                tempList.Add(od);
            }
            poVM.purchase_order = po;
            poVM.orderList = tempList;
            return View(poVM);

        }
    }

2 个答案:

答案 0 :(得分:1)

我认为问题出在您的模型类中。在purchase_order中,为什么有这么多列标记为[Key]?由于purchase_order_no被声明为标识列,因此它本身应该是主键。我甚至都不知道为什么EF在启动时没有抱怨,因为你的配置毫无意义。

删除所有其他列上的[Key]属性,它应该可以正常工作。如果需要确保唯一性,可以在其他列上创建唯一索引。

答案 1 :(得分:0)

我不会对您的模型提出疑问,为什么您会首先拥有如此多的列标记为[Key]的列。那无助于您解决问题。此外,出于性能方面的考虑,您可能希望主键中包含所有这些列:当首先将代码与EF一起使用时,默认情况下主键被创建为 clustered

问题可能是不是具有属性[Key]的列的 number 。在主键中只合并了一个DateTime类型的额外列之后,我遇到了同样的问题,并且实体purchase_order的主键组合中还存在一个DateTime列。

当标记为主键的一部分时,EF似乎不能很好地处理DateTime列。我发现了一个有趣的网页,对此进行了确认:https://social.msdn.microsoft.com/Forums/en-US/6f545446-aa26-4a21-83e9-60f7fa5ed3b0/optimisticconcurrencyexception-when-trying-to-insert-a-new-record-in-table-using-entity-framework?forum=adodotnetentityframework

我可以为您的问题想到两种解决方案:

  1. 如果要坚持使用主键定义::将[order_timestamp]截断为数据库可接受的值。如果使用SQL Server,则在将其截断为10毫秒时会很好,C#:

    order_timestamp = DateTime.Now.Truncate(TimeSpan.FromMilliseconds(10));
    
  2. 如果您不想截断时间戳,但愿意将PK组合更改为索引:考虑为主键添加一个自动编号列:

    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    [Key]
    public int Id { get; set; }
    

    并将其他关键属性更改为:

    [Index("IX_purchase_order", Order = 1)],
    [Index("IX_purchase_order", Order = 2)], etc.
    

    为了提高性能,您可能希望将索引创建为群集:

    [Index("IX_purchase_order", IsClustered = true, Order = 1)], etc.
    

    但是,当使用 Migrations 时,请记住,不幸的是EF仍然会尝试将您的PK也作为集群来创建。这将在发出命令 update-database 时产生错误,您首先必须对迁移代码进行一些更改。尽管在代码中您会发现类似以下内容:

    .Index(t => new{client_no, ..., order_timestamp}, clustered: true, name: "IX_purchase_order")
    

    您必须通过将PK部分的迁移代码更改为以下方式来明确声明主键:非集群

    .PrimaryKey(t => t.Id, clustered: false) 
    

我希望这会有所帮助。