我正在使用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
字段提供的,所以我错过了什么?
答案 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:
一些建议,如果仍然无效:
考虑删除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}}约束并以编程方式处理一致性。我不会在导航属性上使用这个约束(但我只熟悉我们在当前工作项目中使用它的程度,因此可能会有不同的观点)。
首先通过重新创建数据库来测试您的代码。
测试我的解决方案(上面的链接)。它对我有用,所以配置可能会有所不同。
由于Required
不是Details
,因此EF不使用延迟加载。我期待这会导致问题,因为当从数据库中读取对象时Details
变为virtual
,但在我的环境中并非如此。您可能会尝试加载,但我怀疑这是否有任何影响:
渴望加载:
Details
更新4:
如果仍然无效,则删除数据库,但另外重新创建迁移:
null
函数,因此请在操作之前获取该文件的副本。