无法通过`AddOrUpdate`更新'Required'Collection字段

时间:2015-12-13 15:06:05

标签: c# asp.net-mvc entity-framework asp.net-web-api

我正在使用Asp.net web api 2 +实体框架6。

基本上我有2个型号:

public class MyOrderModel
{
    public int Id { get; set; }
    public string OrderNumber { get; set;}
    public string AuthCode { get; set; }
    [Required]
    public List<MyOrderDetailModel> Details { get; set; }
}

public class MyOrderDetailModel
{
    public int Id { get; set; }
    public decimal Amount{ get; set;}
}

运行程序包管理器控制台命令Enable-Migration后,在Configuration.Seed(WaynyCloudTest.Models.ApplicationDbContext context)中,我试图添加一些预先加载的数据:

context.MyOrderModels.AddOrUpdate(
    s => s.OrderNumber,
    new MyOrderModel
    {
        OrderNumber = "0001",
        AuthCode = "ABCDE",
        Details = new List<MyOrderDetailModel>()
        {
            new MyOrderDetailModel()
            {
                Amount = 5.67M
            }
        }
    };

在第一个(初始)Update-database命令之后,一切都很好,我可以看到上面的数据持久存储到数据库2表。

稍后,我想将AuthCode属性值从ABCDE更新为ABCDEXXX, 唯一的变化是价值分配:

context.MyOrderModels.AddOrUpdate(
    s => s.OrderNumber,
    new MyOrderModel
    {
        OrderNumber = "0001",
        // THE ONLY CHANGE!
        AuthCode = "ABCDEXXX",
        Details = new List<MyOrderDetailModel>()
        {
            new MyOrderDetailModel()
            {
                Amount = 5.67M
            }
        }
    };

我希望EntityFramework能够通过在OrderNumber上搜索条件找到数据库中的目标数据行,然后更新AuthCode,但现在我总是在Seed函数中遇到此异常:

  

实体验证失败 - 错误如下:

     

MyTest.Models.MyOrderModel验证失败

     
      
  • 详细信息:“详细信息”字段是必需的。
  •   

显然,这个值是为Details字段提供的,所以我错过了什么?

1 个答案:

答案 0 :(得分:0)

问题在于Id的{​​{1}}字段。在您的环境中,数据库将此字段用作标识(主键)字段,并希望自己生成值。

在您的情况下,有一个简单的解决方法:

PostPayQRCodeFuelOrderModel

可能,context.MyOrderModel.AddOrUpdate( p => p.OrderNumber, new PostPayQRCodeFuelOrderModel { OrderNumber = "00001", Details = new List<MyOrderDetailModel>() { new MyOrderDetailModel() { Amount = 5.67M } } } ); 是唯一的,所以这应该可以正常工作。此外,再次运行种子不会复制此数据。

更新:

可以保留原始OrderNumber,即明确提供MyOrderModel.AddOrUpdate()

首先,您需要阻止自动生成Id的主键值:

MyOrderModel

然后,您需要重新创建public class MyOrderModel { [DatabaseGenerated(DatabaseGeneratedOption.None)] public int Id { get; set; } 表。注意:添加迁移只是为了修改MyOrderModel字段的常用方法不起作用,您需要重新创建表。

多次运行种子有效,即没有重复(我自己检查过)。

更新2:

我没有完整的解释,为什么你的代码不起作用,但是使用下面的代码,你可以按照自己的意愿构建数据库对象的创建和更新。

此代码可以在Seed()中运行多次,无需重复。 Id的更新当然是人为的,但我的观点是将创建和更新分开(以防万一这些需要在最终实现中分开)。

整个项目位于https://github.com/masa67/AspNetNg,分支AuthCode

以下是代码:

SO34252506-1

更新3:

一些建议,如果仍然无效:

  1. 考虑删除var mom = context.MyOrderModel.Where(m => m.OrderNumber == "00001").FirstOrDefault(); if (mom == null) { mom = new PostPayQRCodeFuelOrderModel { OrderNumber = "00001", AuthCode = "ABCDE", Details = new List<MyOrderDetailModel>() { new MyOrderDetailModel { Amount = 5.67M } } }; context.MyOrderModel.AddOrUpdate(p => p.OrderNumber, mom); context.SaveChanges(); } mom.AuthCode = "ABCDEXXX"; context.SaveChanges(); 的{​​{1}}约束并以编程方式处理一致性。我不会在导航属性上使用这个约束(但我只熟悉我们在当前工作项目中使用它的程度,因此可能会有不同的观点)。

  2. 首先通过重新创建数据库来测试您的代码。

  3. 测试我的解决方案(上面的链接)。它对我有用,所以配置可能会有所不同。

  4. 由于Required不是Details,因此EF不使用延迟加载。我期待这会导致问题,因为当从数据库中读取对象时Details变为virtual,但在我的环境中并非如此。您可能会尝试加载,但我怀疑这是否有任何影响:

  5. 渴望加载:

    Details

    更新4:

    如果仍然无效,则删除数据库,但另外重新创建迁移:

    1. 删除现有迁移。
    2. 不要让EF假设它知道数据库的状态,而是强迫它从头开始创建迁移(请参阅其他SO问题以获取建议)。但是,在执行此操作之前,请注意此操作很可能也会覆盖null函数,因此请在操作之前获取该文件的副本。