MVC EF - System.Data.Entity.Infrastructure.DbUpdateException
Posted
技术标签:
【中文标题】MVC EF - System.Data.Entity.Infrastructure.DbUpdateException【英文标题】: 【发布时间】:2017-12-10 02:10:31 【问题描述】:我有两个模型: 用户资料
public class UserProfile
public int Id get; set;
public string Name get; set;
public virtual ApplicationUser ApplicationUser get; set;
public virtual ICollection<UserPost> UserPost get; set;
用户帖子:
public class UserPost
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public string Id get; set;
public string PostTitle get; set;
public virtual UserProfile UserProfile get; set;
public string Contents get; set;
UserProfile 与 AspNetUser 是一对一的关系。
UserProfile 与 UserPost 是一对多的关系。
当我在控制器的 Create 方法中将 UserPost 添加到数据库时,我得到一个 System.Data.Entity.Infrastructure.DbUpdateException
[HttpPost]
[ValidateAntiForgeryToken]
[Authorize]
public ActionResult Create([Bind(Include = "PostTitle, Content")] UserPost post)
if (ModelState.IsValid)
UserManager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(db));
var user = UserManager.FindById(User.Identity.GetUserId());
post.UserProfile = user.UserProfile;
db.UserPosts.Add(post);
db.SaveChanges();//error here
return RedirectToAction("Index");
return View(post);
发生 System.Data.Entity.Infrastructure.DbUpdateException H结果=0x80131501 Message=保存不为其关系公开外键属性的实体时发生错误。 EntityEntries 属性将返回 null,因为无法将单个实体标识为异常源。通过在实体类型中公开外键属性,可以更轻松地处理保存时的异常。有关详细信息,请参阅 InnerException。
内部异常1:UpdateException:更新时出错 条目。有关详细信息,请参阅内部异常。
内部异常 2:SqlException:无法将值 NULL 插入 列“Id”,表“aspnet-project12017.dbo.UserPosts”;列确实 不允许空值。插入失败。声明已终止。
【问题讨论】:
要么将 UserPostId
列更改为 int
:public int Id get; set;
,要么将其设置为 [DatabaseGenerated(DatabaseGeneratedOption.None)]
。 AFAIK 您不能将字符串/GUID 列设置为自动生成的值列,它仅适用于int
、long
或其他数字数据类型(请参阅***.com/questions/8855100/…)。
@TetsuyaYamamoto 因此,如果我想将我的 ID 保留为字符串,那么在创建新帖子时如何生成它,因为它不是自动生成的?
您可以从类构造函数中手动生成它,例如public UserPost() Id = Guid.NewGuid().ToString();
。当然,您可以在那里设置另一个值而不是 GUID。
我尝试将 Id 更改为 int,但仍然出现相同的错误。 @TetsuyaYamamoto
这是代码优先,对吧?如果您已经在创建包含数据的数据库并且仍然遇到相同的错误,请使用 EF Code First Migration 更改标识列数据类型。只要目标表中Id
的数据类型不变,EF 仍将该列视为string
而不是int
。
【参考方案1】:
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
表示标有此类属性的列预期具有从 DBMS 自动生成的值,并且作为一般约定,EF 将 string
和 uniqueidentifier
(GUID) 列视为非自动递增的,因此那些列值必须以其他方式分配(即通过构造函数分配)。
解决主键自增标识列的空问题的常用方法是将其设置为数字数据类型(例如int
):
public class UserPost
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id get; set; // change to numeric data type
public string PostTitle get; set;
public virtual UserProfile UserProfile get; set;
public string Contents get; set;
然后,在执行迁移时,请执行以下步骤:
有时 EF 会默认您尝试插入标识列。如果发生这种情况,请将StoreGeneratedPattern.None
和DatabaseGenerationOption.None
临时设置为UserPost.Id
:
protected void OnModelCreating(DbModelBuilder modelBuilder)
// other entities
modelBuilder.Entity<UserPost>()
.HasKey(p => p.Id)
.Property(p => p.Id)
.StoreGeneratedPattern = StoreGeneratedPattern.None;
// other entities
确保模型已修改为包含实际的Id
属性(需要时尝试删除数据库中的UserPost
表)。
通过包管理器控制台运行迁移,如下所示:
Add-Migration "Set Id as Identity Specification"
在从DbMigration
生成的类中,您将看到两种方法:Up()
和Down()
。修改Up()
部分以在此处设置int
数据类型(并在需要时重新运行迁移过程):
public override void Up()
CreateTable(
"dbo.UserPost",
c => new
Id = c.Int(nullable: false, identity: true),
PostTitle = c.String(),
Contents = c.String(),
)
.PrimaryKey(t => t.Id);
如果您仍希望字符串Id
列作为UserPost
的主键,则需要将其设置为[DatabaseGenerated(DatabaseGeneratedOption.None)]
并从类构造函数中手动分配值(请注意,生成的值必须是唯一的,否则 EF 会告诉你“不能在对象中插入重复的键”):
public class UserPost
public UserPost()
Id = [[autogenerated value here]];
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public string Id get; set;
// other properties
相关问题:
MVC5 EF6 Seed Method: Cannot insert NULL into column 'Id' table AspNetUsers
Cannot insert the value NULL into column 'Id' (Database first)
Code first - Cannot insert the value NULL into column 'Id'
Entity Framework Cannot insert the value NULL into column Identity Specification set to No
【讨论】:
以上是关于MVC EF - System.Data.Entity.Infrastructure.DbUpdateException的主要内容,如果未能解决你的问题,请参考以下文章
在 MVC 视图中显示存储过程的结果 (EF 5 / MVC 4)
[实战]MVC5+EF6+MySql企业网盘实战(28)——其他列表