您可以在实体框架中以一对多关系保存父实体吗



我在两个实体之间有一个简单的一对多关系。

public class Contact
{
    public string Id { get; set; }
    public string FirstName { get; set; }
    // the children
    public List<Message> Messages { get; set; }
}
public class Message
{
    public string Id { get; set; }
    public string ContactId { get; set; }
    public string Source { get; set; }
    // the parent
    public Contact Contact { get; set; }
}

以下是的迁移情况

migrationBuilder.CreateTable(
                name: "Contact",
                columns: table => new
                {
                    Id = table.Column<string>(nullable: false),
                    FirstName = table.Column<string>(nullable: true)
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_Contact", x => x.Id);
                    table.UniqueConstraint("UK_Id", x => x.Id);
                });
            migrationBuilder.CreateTable(
                name: "Message",
                columns: table => new
                {
                    Id = table.Column<string>(nullable: false),
                    ContactId = table.Column<string>(nullable: true),                        
                    Source = table.Column<string>(nullable: true)
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_Message", x => x.Id);
                    table.UniqueConstraint("UK_Id", x => x.Id);
                    table.ForeignKey(
                        name: "FK_Message_Contact_ContactId",
                        column: x => x.ContactId,
                        principalTable: "Contact",
                        principalColumn: "Id");
                });

现在,我可以创建一个新的Contact,在Messages属性中添加一个新Message,并且可以毫不费力地保存。如果我加载该联系人,我会得到与之相关的所有消息,没问题。

我想知道的是如何反过来做。我想创建一条新消息(数据库中还不存在),将Contact属性设置为一个新的联系人对象并保存。我最终遇到了外键约束(这很有道理。在保存联系人之前无法保存消息)。但我认为实体框架足够聪明,可以弄清楚关系,并知道在消息之前插入联系人。我是否配置错误?

更新

这是我试图通过的单元测试

    [TestMethod]
    public void ShouldSaveEntityParentRelationshipsCorrectly()
    {
        var message = new Message
        {
            Id = "2848"
            , IsUrgent = true
            , MessageType = MessageType.Inbox
            , Note = "One ring to rule them all"
            , Contact = new Contact
            {
                Id = "454545"
                , FirstName = "Frodo"
                , LastName = "Baggins"
            }
        };
        service.Save(message); //Foreign key constraint error
        var entity = service.Find<Message>()
            .Include(c => c.Contact)
            .First(p => p.Id == "2848");
        Assert.AreEqual("Frodo", entity.Contact.FirstName);
        Assert.AreSame(entity, message, "Messages are not the same");
        Assert.IsNotNull(entity.Contact);
        Assert.AreSame(message.Contact, entity.Contact, "Contacts are not the same");
    }

以下是service.Save在引擎盖下的功能

public virtual void Save<T>(T entity) where T : class, IEntity
    {
        var context = Context();
        var entry = context.Entry(entity);
        var state = entry.State;
        if (state == EntityState.Detached)
            Add(entity);
        else if (state == EntityState.Deleted)
            Remove(entity);
        else
            Update(entity);
        SaveChanges();
    }
    public virtual void SaveChanges()
    {
        try
        {
            Context().SaveChanges();
        }
        catch (DbUpdateConcurrencyException ex)
        {
            Logger.Current.Log(ex);
            throw ex;
        }
    }
    public T Add<T>(T entity) where T : class, IEntity
    {
        return Context().Set<T>().Add(entity).Entity;
    }

在beta8及更高版本中,DbSet.Add()仅添加实体及其子级。因为ContactMessage的父级,所以需要先显式添加它。

    service.Save(message.Contact);
    service.Save(message);

请参阅https://github.com/aspnet/EntityFramework/pull/2979了解更多详细信息。

取决于您使用的EF7版本。对于beta7及更早版本;

与以前版本的EF不同,当前在使用EF7的对象不会将其任何相关对象标记为已添加。

这似乎在beta8中得到了解决。点击此处了解更多信息。

最新更新