【问题标题】:How do I add an existing entity to a new entity in EF?如何将现有实体添加到 EF 中的新实体?
【发布时间】:2017-03-10 01:14:24
【问题描述】:

我目前有 2 个相互映射的类。我收到以下错误:

MERGE 语句与 FOREIGN KEY 约束冲突 “FK_Stories_Users_UserId”。数据库发生冲突 “StoriesDb”、表“dbo.Users”、“Id”列。

我正在尝试将 UserId FK 添加到我刚刚创建的新 Story 实体中。我的类看起来像这样(剪掉不相关的属性)

public class Story : ITimestamp
{
    public Story() {}

    public int Id { get; set; }

    public string Description { get; set; }
    public string DescriptionMarkdown { get; set; }
    public string Title { get; set; }
    public Guid UserId { get; set; }

    public virtual User User { get; set; }

    public virtual DateTime CreatedDate { get; set; }
    public virtual DateTime ModifiedDate { get; set; }
}    

public class User : ITimestamp
{
    public Guid Id { get; set; }
    public string Username { get; set; }
    public string Email { get; set; }
    public string PasswordHash { get; set; }
    public string PasswordSalt { get; set; }

    public virtual IList<Comment> Comments { get; set; } = new List<Comment>();
    public virtual DateTime CreatedDate { get; set; }
    public virtual DateTime ModifiedDate { 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();
      [...]
}

通过其他一些 Create 方法,我知道将新实体对象添加到第二个新实体的导航属性将正确保存。但是,此时我不希望在保存 Story 实体时能够修改 User 实体,这就是我设置 UserId 属性(标记为 FK)的原因。但它似乎不起作用。

我还将传递给方法的 UserId 与数据库数据进行了比较,它们都是相同的值。

仅设置 FK Id 属性的技术是我在 EF6 中使用的,它按预期工作。

我是否在我的 DbContext 或我的映射中遗漏了一些允许我通过外键 ID 保存更改新实体的内容?

这个问题与this StackOverflow question 非常相似。但不幸的是,它只处理新创建的实体。

编辑 我包括我对故事和用户的映射。

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);
    }
}

为迁移编辑

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);
                });

【问题讨论】:

    标签: asp.net-core-mvc entity-framework-core


    【解决方案1】:

    我使用相同的技术(设置 UserId 值而不是完整的 User 引用)并且它可以正常工作,只需添加一个 - 我在 UserIdUser 属性之间使用显式 FK 绑定,如下所示:

    public Guid UserId { get; set; }
    
    [ForeignKey(nameof(UserId))]
    public virtual User User { get; set; }
    

    检查您的数据库(或迁移脚本) - 可能是 EF 在您的 Story 中创建了两个不同的类似 UserId 的属性,但没有此属性?

    当然,如果你愿意,你可以使用 fluent API 而不是属性。

    【讨论】:

    • 看看 StoryMap 类?看起来还行吗?
    • 是的,看起来不错。我从未使用过 IEntityTypeConfiguration,但它看起来像是正确的流畅配置。你使用迁移吗?为该实体生成什么代码?或者,检查 SQL DB - FL 看起来像你想要的吗?
    • 我添加了创建的迁移,用户表没有提及 StoryId,这很好。至于 IEntityTypeConfiguration,它是一个在 DbContext 中使用的接口,可以轻松执行映射类。
    • 迁移看起来不错。您的代码应该工作。仔细检查所有内容,尝试新/空数据库(从单个迁移重新生成它)。您错过了一些东西(例如迁移中的UserIsAuthor,它不在您的原始实体中)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-01-26
    • 1970-01-01
    • 2010-09-29
    • 2013-03-03
    • 2022-10-18
    • 1970-01-01
    相关资源
    最近更新 更多