Entity Framework Code First - 通过聚合根添加 1:many
我有一个关于将子实体添加到 Entity Framework 4.1 CF 中的根实体的问题。
考虑以下基础设施实体基类和两个 POCO:
public abstract class EntityBase<TKeyDataType>
{
[Key]
public TKeyDataType Id { get; set; }
// Equality methods ommitted for brevity...
}
public class Foo : EntityBase<int>, IAggregateRoot
{
public string Foo1 { get; set; }
public virtual ICollection<FooSibling> Siblings { get; set; }
}
public class FooSibling : EntityBase<int>
{
public string SiblingPropFoo { get; set; }
public int FooId { get; set; }
public Foo Foo { get; set; }
}
请注意,Foo
实现 IAggregateRoot
(只是一个空接口 - 将其视为“有关数据的数据”上下文中的元数据) 。
到目前为止,一切都很好。如果我运行此命令,EF 将创建具有适当的一对多关系的数据库。
我对这两个实体唯一的流畅映射是:
modelBuilder.Entity<Foo>()
.HasMany(x => x.Siblings)
.WithRequired(x=>x.Foo)
.WillCascadeOnDelete(true);
没有 Foo
就没有 FooSibling
。吹走一个Foo
,你就吹走了它的所有兄弟。这件作品有效。
问题是,将 FooSiblings POCO 添加到 Foo POCO 时,我必须使用唯一的负数,如此服务方法中所示:
public ResponseBase UpdateBy(RequestBase<Foo> request)
{
ResponseBase response = new ResponseBase();
try
{
Foo foo = FooRepository.FirstOrDefault(x => x.Id == request.Entity.Id);
// Dummy adds to test associations.
// These come back on the Foo inside the request, but I'm explicitly putting them here
// for the purpose of this question.
request.Entity.Siblings.Add(new FooSibling() { Id = -2, SiblingPropFoo = "Prop1", SiblingPropFoo2 = "Prop2" });
request.Entity.Siblings.Add(new FooSibling() { Id = -1, SiblingPropFoo = "Prop1", SiblingPropFoo2 = "Prop2" });
// Update Foo's scalars and children (mapping is Foo->Foo)
foo = AutoMapper.Mapper.Map(request.Entity, foo);
UnitOfWork.Commit();
response.Success = true;
}
catch (Exception e)
{
response.Success = false;
response.Message = e.Message;
}
return response;
}
一旦调用 UnitofWork.Commit()
(它只调用 SaveChanges上下文的
- 这里没有魔法),一切都很好......
但是,如果我不使用像这样的唯一负数,只是尝试设置其父级,如下所示:
request.Entity.Siblings.Add(new FooSibling() { Foo = foo, SiblingPropFoo = "Prop1", SiblingPropFoo2 = "Prop2" });
request.Entity.Siblings.Add(new FooSibling() { Foo = foo, SiblingPropFoo = "Prop1", SiblingPropFoo2 = "Prop2" });
只有一个被持久化到数据库中。
我知道不使用负数的唯一其他方法是直接在服务方法中使用 FooSiblings DbSet:
IRepository<FooSibling> siblingRepo = new CookieCutterEntityFrameworkRepository<FooSibling>(UnitOfWork);
siblingRepo.Insert(new FooSibling() { FooId = foo, .... });
我的 CookieCutter 存储库正在抽象所有 DbSet 内容等。
但是......剥离所有抽象和通用巫术为了清楚起见,问题实际上归结为是否有一种方法可以更新我的 Foo POCO(根实体)并通过一个 DbSet 添加新的同级而不使用负数?
供参考(不使用纯 DbContext 进行抽象):
// This works (using multiple DbSets/Repositories always make life easier...)
Ctx.Foos.Update(foo);
Ctx.FooSiblings.Add(new FooSibling() { Foo = foo, ... });
Ctx.FooSiblings.Add(new FooSibling() { Foo = foo, ... });
Ctx.SaveChanges();
// This works too (using negative number trick - foo scalar properties get
// updated and the siblings get persisted to the database properly).
foo.Siblings.Add(new FooSibling() { Id = -2, ....});
foo.Siblings.Add(new FooSibling() { Id = -1, ....});
Ctx.Foos.Update(foo);
Ctx.SaveChanges();
// This doesn't work (but it's what I'm striving for to drive everything off the root).
foo.Siblings.Add(new FooSibling() { Foo = foo });
foo.Siblings.Add(new FooSibling() { Foo = foo });
Ctx.Foos.Update(foo);
Ctx.SaveChanges();
在最后一种情况(非工作情况)中,我努力以一种方式配置它,让它拾取 Foo POCO 本身的任何更改。
我尝试过关闭和打开代理。此外,按照这种设置方式,上下文在整个 HTTP 请求的生命周期内都保留在范围内。
如果不可能,您会提出什么建议?
I have a question regarding adding children to a root entity in Entity Framework 4.1 CF.
Considering the following infrastructure Entity base class and two POCOs:
public abstract class EntityBase<TKeyDataType>
{
[Key]
public TKeyDataType Id { get; set; }
// Equality methods ommitted for brevity...
}
public class Foo : EntityBase<int>, IAggregateRoot
{
public string Foo1 { get; set; }
public virtual ICollection<FooSibling> Siblings { get; set; }
}
public class FooSibling : EntityBase<int>
{
public string SiblingPropFoo { get; set; }
public int FooId { get; set; }
public Foo Foo { get; set; }
}
Notice that Foo
implements IAggregateRoot
(just an empty interface - think of it as metadata in the "data about data" context).
So far, so good. If I run this, EF creates the database with the appropriate 1:many relationship.
The only fluent mapping I have on these two entities are:
modelBuilder.Entity<Foo>()
.HasMany(x => x.Siblings)
.WithRequired(x=>x.Foo)
.WillCascadeOnDelete(true);
No FooSibling
without a Foo
. Blow away a Foo
, you blow away all its sibling. This piece works.
The issue is when adding FooSiblings POCO to the Foo POCO, I have to use unique negative numbers as shown in this service method:
public ResponseBase UpdateBy(RequestBase<Foo> request)
{
ResponseBase response = new ResponseBase();
try
{
Foo foo = FooRepository.FirstOrDefault(x => x.Id == request.Entity.Id);
// Dummy adds to test associations.
// These come back on the Foo inside the request, but I'm explicitly putting them here
// for the purpose of this question.
request.Entity.Siblings.Add(new FooSibling() { Id = -2, SiblingPropFoo = "Prop1", SiblingPropFoo2 = "Prop2" });
request.Entity.Siblings.Add(new FooSibling() { Id = -1, SiblingPropFoo = "Prop1", SiblingPropFoo2 = "Prop2" });
// Update Foo's scalars and children (mapping is Foo->Foo)
foo = AutoMapper.Mapper.Map(request.Entity, foo);
UnitOfWork.Commit();
response.Success = true;
}
catch (Exception e)
{
response.Success = false;
response.Message = e.Message;
}
return response;
}
Once the UnitofWork.Commit()
is called (it just calls SaveChanges
of the context - no magic here), all is well...
However, if I don't use unique negative numbers like and just try to set its parent, like this:
request.Entity.Siblings.Add(new FooSibling() { Foo = foo, SiblingPropFoo = "Prop1", SiblingPropFoo2 = "Prop2" });
request.Entity.Siblings.Add(new FooSibling() { Foo = foo, SiblingPropFoo = "Prop1", SiblingPropFoo2 = "Prop2" });
Only one gets persisted to the database.
The only other way I know of to do it without using negative numbers is to use the FooSiblings DbSet directly in the service method:
IRepository<FooSibling> siblingRepo = new CookieCutterEntityFrameworkRepository<FooSibling>(UnitOfWork);
siblingRepo.Insert(new FooSibling() { FooId = foo, .... });
My CookieCutter repository is abstracting all the DbSet stuff, etc.
But ... stripping off all the abstraction and generic voodoo for clarity, the question really comes down to is there a way to update my Foo POCO (the root Entity) and add new siblings through one DbSet without using negative numbers?
For reference (without abstraction using pure DbContext):
// This works (using multiple DbSets/Repositories always make life easier...)
Ctx.Foos.Update(foo);
Ctx.FooSiblings.Add(new FooSibling() { Foo = foo, ... });
Ctx.FooSiblings.Add(new FooSibling() { Foo = foo, ... });
Ctx.SaveChanges();
// This works too (using negative number trick - foo scalar properties get
// updated and the siblings get persisted to the database properly).
foo.Siblings.Add(new FooSibling() { Id = -2, ....});
foo.Siblings.Add(new FooSibling() { Id = -1, ....});
Ctx.Foos.Update(foo);
Ctx.SaveChanges();
// This doesn't work (but it's what I'm striving for to drive everything off the root).
foo.Siblings.Add(new FooSibling() { Foo = foo });
foo.Siblings.Add(new FooSibling() { Foo = foo });
Ctx.Foos.Update(foo);
Ctx.SaveChanges();
In the last case (the non-working case), I'm striving to configure it in a fashion where it picks up any changes to the Foo POCO itself.
I've tried with Proxies turned off and on. Also, in the way this is setup, the context remains in scope for the life of the entire HTTP request.
If not possible, what suggestion(s) would you give?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
如果我正确地阅读了本文,则问题预计是通过 dbcontext 或 objectcontext 产生的。
首先我想看看你最后一组例子..没有抽象。当您将图表附加到上下文时,图表中的所有内容都将作为“未更改”附加。时期。在上下文对它们有任何了解之前,上下文并不关心您如何将“子级”添加或附加到根(例如 foo 的兄弟姐妹)。如果上下文已经知道 foo ,然后您添加,那么上下文知道您如何进行链接..您说“添加”,因此它将它们标记为已添加,并且它们将被插入。即使根是不新鲜。您必须将其状态更改为“未更改”。但就您而言,您正在获取一个断开连接的图表并将其附加,因此图表中的所有内容都被视为未更改。您必须显式地将每个同级的状态更改为您想要的状态,以便 SaveChanges 注意到它需要插入。
现在到您的存储库。我不知道负数到底是怎么回事。 :) 我没有时间进行实验。 (好奇这是如何欺骗 savechanges 的,但这是一个臭气熏天的黑客:))这取决于 FooRepository 查询背后发生的事情。如果返回的 foo 被上下文跟踪,那么我期望当您添加同级时,上下文会知道它们已添加并会创建一个插入。但如果它没有被跟踪,那么当您添加兄弟姐妹时,上下文就不会知道“ADD”。无论何时您将图(foo + 它的同级图)重新附加到上下文,它们都将“未更改”,因此您不会获得所需的插入。
这完全取决于对存储库中发生的其他情况以及影响自动映射器的因素(如果有)的猜测。我猜测您正在上下文意识到图表之前完成所有工作,所以最后,我认为您正在调用 ATTACH 这将导致没有插入被发送到数据库。
If I'm reading this correctly, the problem is expected via dbcontext or objectcontext.
FIrst I want to look at your last set of examples ..no abstraction. When you attach a graph to a context, everything in the graph is attached as Unchanged. Period. The context doesn't care how you added or attached "children" to the root (e.g. siblings to a foo) before the context had ANY knowledge of them. If the foo was aalready known by the context and THEN you ADD, then the context is aware of HOW you did the linking..you said "ADD" so it marks them as Added and they'll get inserted.Even if the root is not new. You'ld have to change its state to "Unchanged". But in your case, you are taking a disconnected graph and attaching it, so everyting in the graph is seen as unchanged. You have to explicitly change the state of each of the siblings to what you want for SaveChanges to notice it needs to INSERT.
Now to your repository. I have no idea wtf is up with the negative numbers. :) And I don't have time to experiment with that. (curious how that's tricking savechanges, but it's a smelly hack :) ) It depends on what's happening behind your FooRepository query. If that returned foo is beingi tracked by the context, then I would expect that when you ADD siblings, the context will know they are added and will create an insert. But if it is not being tracked, then when you ADD the siblings, context is not there to know about "ADD". At whatever point you reattach the graph (foo + its siblings) to a context, they are all going to be "UNchanged" so you're not getting the desired insert.
This is all dependent on a GUESS at whatever else is going on in your repository and what, if any, affect the automapper has. I am guessing that you are doing all of the owrk before the context is aware of the graph so in the end , I think you're calling ATTACH which would result in no INSERTS being sent to the db.