Asp.net core mvc 如何将现有实体添加到EF中的新实体?
我目前有两个类相互映射。我收到以下错误: MERGE语句与外键约束冲突 “FK_故事_用户_用户ID”。数据库中发生冲突 “StoriesDb”,表“dbo.Users”,列“Id” 我正在尝试将UserId FK添加到我刚刚创建的新故事实体中。我的类看起来像这样(剪掉了不相关的属性)Asp.net core mvc 如何将现有实体添加到EF中的新实体?,asp.net-core-mvc,entity-framework-core,Asp.net Core Mvc,Entity Framework Core,我目前有两个类相互映射。我收到以下错误: MERGE语句与外键约束冲突 “FK_故事_用户_用户ID”。数据库中发生冲突 “StoriesDb”,表“dbo.Users”,列“Id” 我正在尝试将UserId FK添加到我刚刚创建的新故事实体中。我的类看起来像这样(剪掉了不相关的属性) 公共类故事:ITimestamp { 公共故事(){} 公共int Id{get;set;} 公共字符串说明{get;set;} 公共字符串描述符标记{get;set;} 公共字符串标题{get;set;} 公共
公共类故事:ITimestamp
{
公共故事(){}
公共int Id{get;set;}
公共字符串说明{get;set;}
公共字符串描述符标记{get;set;}
公共字符串标题{get;set;}
公共Guid用户标识{get;set;}
公共虚拟用户用户{get;set;}
公共虚拟日期时间CreatedDate{get;set;}
公共虚拟日期时间修改日期{get;set;}
}
公共类用户:ITimestamp
{
公共Guid Id{get;set;}
公共字符串用户名{get;set;}
公共字符串电子邮件{get;set;}
公共字符串密码哈希{get;set;}
公共字符串密码salt{get;set;}
公共虚拟IList注释{get;set;}=new List();
公共虚拟日期时间CreatedDate{get;set;}
公共虚拟日期时间修改日期{get;set;}
}
我添加如下新故事:
public async Task<StorySummaryViewModel> Create(CreateViewModel model, string username, Guid userId)
{
var story = await StoriesDbContext.Stories.AddAsync(new Story() {
Title = model.Title,
DescriptionMarkdown = model.DescriptionMarkdown,
Description = CommonMarkConverter.Convert(model.DescriptionMarkdown),
UserId = userId
});
var rowCount = await StoriesDbContext.SaveChangesAsync();
[...]
}
public Guid UserId { get; set; }
[ForeignKey(nameof(UserId))]
public virtual User User { get; set; }
公共异步任务创建(CreateViewModel模型、字符串用户名、Guid用户ID)
{
var story=await StoriesDbContext.Stories.AddAsync(new story()){
Title=model.Title,
DescriptionMarkdown=model.DescriptionMarkdown,
Description=CommonMarkConverter.Convert(model.DescriptionMarkdown),
UserId=UserId
});
var rowCount=await StoriesDbContext.SaveChangesAsync();
[...]
}
通过其他一些创建方法,我知道将新实体对象添加到第二个新实体的导航属性将正确保存。但是,此时我不希望在保存故事实体时修改用户实体,这就是我设置UserId属性(标记为FK)的原因。但它似乎不起作用
我还将传递给方法的UserId与数据库数据进行了比较,它们都是相同的值
仅设置FK Id属性的技术是我在EF6中使用的,它按预期工作
我的DbContext或映射中是否缺少允许我按外键id保存新实体更改的内容
这个问题很类似于。但不幸的是,它只处理新创建的实体
编辑
我包括了我对故事和用户的映射
public class StoryMap : IEntityTypeConfiguration<Story>
{
public void Map(EntityTypeBuilder<Story> builder)
{
builder.HasKey(e => e.Id);
builder.Property(s => s.Url).IsRequired();
builder.Property(s => s.Title).IsRequired();
builder.HasOne(e => e.User).WithMany().HasForeignKey(e => e.UserId);
builder.HasMany(e => e.Comments).WithOne(c => c.Story).HasForeignKey(c => c.StoryId);
builder.HasMany(e => e.Votes).WithOne(v => v.Story).HasForeignKey(v => v.StoryId);
}
}
public class UserMap : IEntityTypeConfiguration<User>
{
public void Map(EntityTypeBuilder<User> builder)
{
builder.HasKey(u => u.Id);
builder.HasIndex(u => u.Email);
builder.Property(u => u.PasswordHash).IsRequired();
builder.Property(u => u.PasswordSalt).IsRequired();
builder.Property(u => u.Username).IsRequired();
builder.Property(u => u.Email).IsRequired();
// Navigation
[..snip..]
builder.HasMany(u => u.Stories).WithOne(c => c.User).HasForeignKey(s => s.UserId);
}
}
公共类故事图:IEntityTypeConfiguration
{
公共空白地图(EntityTypeBuilder)
{
builder.HasKey(e=>e.Id);
属性(s=>s.Url).IsRequired();
builder.Property(s=>s.Title).IsRequired();
HasOne(e=>e.User).WithMany().HasForeignKey(e=>e.UserId);
HasForeignKey(c=>c.StoryId);
HasForeignKey(v=>v.StoryId);
}
}
公共类UserMap:IEntityTypeConfiguration
{
公共空白地图(EntityTypeBuilder)
{
builder.HasKey(u=>u.Id);
builder.HasIndex(u=>u.Email);
属性(u=>u.PasswordHash).IsRequired();
属性(u=>u.PasswordSalt).IsRequired();
builder.Property(u=>u.Username).IsRequired();
builder.Property(u=>u.Email).IsRequired();
//航行
[…剪断..]
HasForeignKey(s=>s.UserId);
}
}
为迁移编辑
migrationBuilder.CreateTable(
name: "Stories",
columns: table => new
{
Id = table.Column<int>(nullable: false)
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
CreatedDate = table.Column<DateTime>(nullable: false),
Description = table.Column<string>(nullable: true),
DescriptionMarkdown = table.Column<string>(nullable: true),
ModifiedDate = table.Column<DateTime>(nullable: false),
Title = table.Column<string>(nullable: false),
Url = table.Column<string>(nullable: false),
UserId = table.Column<Guid>(nullable: false),
UserIsAuthor = table.Column<bool>(nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Stories", x => x.Id);
table.ForeignKey(
name: "FK_Stories_Users_UserId",
column: x => x.UserId,
principalTable: "Users",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
});
migrationBuilder.CreateTable(
名称:“故事”,
列:表=>new
{
Id=table.Column(可空:false)
.Annotation(“SqlServer:ValueGenerationStrategy”,SqlServerValueGenerationStrategy.IdentityColumn),
CreatedDate=table.Column(可空:false),
Description=table.Column(可空:true),
DescriptionMarkdown=table.Column(可空:true),
ModifiedDate=table.Column(可空:false),
Title=table.Column(可空:false),
Url=table.Column(可空:false),
UserId=table.Column(可空:false),
UserIsAuthor=table.Column(可空:false)
},
约束:表=>
{
表.PrimaryKey(“PK_故事”,x=>x.Id);
表1.外键(
名称:“FK_故事_用户_用户ID”,
列:x=>x.UserId,
原则性:“用户”,
主栏:“Id”,
onDelete:referentialiction.Restrict);
});
我使用了相同的技术(设置UserId
值而不是fullUser
引用),它工作正常,只需添加一项-我在UserId
和User
属性之间使用显式FK绑定,如下所示:
public async Task<StorySummaryViewModel> Create(CreateViewModel model, string username, Guid userId)
{
var story = await StoriesDbContext.Stories.AddAsync(new Story() {
Title = model.Title,
DescriptionMarkdown = model.DescriptionMarkdown,
Description = CommonMarkConverter.Convert(model.DescriptionMarkdown),
UserId = userId
});
var rowCount = await StoriesDbContext.SaveChangesAsync();
[...]
}
public Guid UserId { get; set; }
[ForeignKey(nameof(UserId))]
public virtual User User { get; set; }
检查您的数据库(或迁移脚本)-是否可以在您的故事
中创建两个不同的用户ID
-类似属性,而不使用此属性
当然,如果愿意,您可以使用fluent API而不是attribute。看看StoryMap类?看起来还好吗?是的,看起来不错。我从未使用过IEntityTypeConfiguration,但它看起来像是正确的fluent配置。您使用迁移吗?为该实体生成了什么代码?或者,检查SQL DB-FLs看起来像您想要的吗?我添加了创建的迁移,用户表