你能在entity framework中以一对多的关系保存父实体吗?
Can you save the parent entity in a one-to-many relationship in entity framework?
我在两个实体之间有一个简单的一对多关系。
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
,将新的 Message
添加到 Messages
属性,并且可以毫不费力地保存。如果我加载该联系人,我会收到与其关联的所有消息,没问题。
我想知道如何反向执行此操作。我想创建一条新消息(数据库中尚不存在),将 Contact
属性 设置为新的联系人对象并保存。我最终得到一个外键约束(这是有道理的。在保存联系人之前无法保存消息)。但我认为 entity framework 足够聪明,可以弄清关系并知道在 消息之前插入联系人 。我配置有问题吗?
更新
这是我要通过的单元测试
[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;
}
取决于您使用的 EF7 版本。对于 beta7 及更早版本;
Unlike in previous versions of EF, currently calling Add() on an
object using EF7 won't mark any of its related objects as added.
这似乎在 beta8 中得到了解决。更多信息 here.
在 beta8 及更高版本中,DbSet.Add()
仅添加实体 及其子实体 。因为 Contact
是 Message
的父级,所以您需要先明确添加它。
service.Save(message.Contact);
service.Save(message);
有关详细信息,请参阅 https://github.com/aspnet/EntityFramework/pull/2979。
我在两个实体之间有一个简单的一对多关系。
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
,将新的 Message
添加到 Messages
属性,并且可以毫不费力地保存。如果我加载该联系人,我会收到与其关联的所有消息,没问题。
我想知道如何反向执行此操作。我想创建一条新消息(数据库中尚不存在),将 Contact
属性 设置为新的联系人对象并保存。我最终得到一个外键约束(这是有道理的。在保存联系人之前无法保存消息)。但我认为 entity framework 足够聪明,可以弄清关系并知道在 消息之前插入联系人 。我配置有问题吗?
更新
这是我要通过的单元测试
[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;
}
取决于您使用的 EF7 版本。对于 beta7 及更早版本;
Unlike in previous versions of EF, currently calling Add() on an object using EF7 won't mark any of its related objects as added.
这似乎在 beta8 中得到了解决。更多信息 here.
在 beta8 及更高版本中,DbSet.Add()
仅添加实体 及其子实体 。因为 Contact
是 Message
的父级,所以您需要先明确添加它。
service.Save(message.Contact);
service.Save(message);
有关详细信息,请参阅 https://github.com/aspnet/EntityFramework/pull/2979。