关于 System.Data.EntityState.Add 和 DbSet.Add 之间差异(如果有的话)的令人困惑的文章和文档
Confusing articles and documentation about the differences (if any) between System.Data.EntityState.Add & DbSet.Add
我正在使用 EF 5 开发 C# ASP.NET MVC 5 Web 应用程序。使用 EF 映射我的数据库表会生成 DbContext
class 和 .edmx
文件。今天,我正在阅读a great article about creating generic DAL classes,但我停在了以下句子:
Note that using the Entry method to change the state of an entity will
only affect the actual entity that you pass in to the method. It won’t
cascade through a graph and set the state of all related objects,
unlike the DbSet.Add method.
这与这些问题中提到的矛盾:
- http://forums.asp.net/p/2015170/5803192.aspx
- http://forums.asp.net/p/2060606/5943259.aspx
- Difference between DbSet.Add(entity) and entity.State = EntityState.Added
- What is the difference between IDbSet.Add and DbEntityEntry.State = EntityState.Added?
在上述所有问题的回答中,所有用户都提到使用System.Data.EntityState.Added
与使用DbSet.Add
完全相同。但是我首先提到的文章指出,使用 System.Data.EntityState.Added
不会级联通过图形。
根据我的测试,我得出结论,使用 System.Data.EntityState.Added
会像在 DBset.Add
的情况下一样在图中级联。是文章写错了,还是我的测试和问答?
这些方法是相同的,您可以通过常规测试来验证,或者,如果您想完全确定 - 通过对 EF 6 代码的一些探索。
DbSet.Add
方法 (http://entityframework.codeplex.com/SourceControl/latest#src/EntityFramework/DbSet.cs)
public virtual TEntity Add(TEntity entity)
{
Check.NotNull<TEntity>(entity, "entity");
this.GetInternalSetWithCheck("Add").Add((object) entity);
return entity;
}
这会调用 InternalSet<T>.Add(object)
方法。
DbEntityEntry<T>.State
属性 (http://entityframework.codeplex.com/SourceControl/latest#src/EntityFramework/Infrastructure/DbEntityEntry.cs)
public EntityState State
{
get { return _internalEntityEntry.State; }
set { _internalEntityEntry.State = value; }
}
其中 _internalEntityEntry
属于 InternalEntityEntry
类型。
InternalEntityEntry.State
属性 (http://entityframework.codeplex.com/SourceControl/latest#src/EntityFramework/Internal/EntityEntries/InternalEntityEntry.cs)
public virtual EntityState State
{
get { return IsDetached ? EntityState.Detached : _stateEntry.State; }
set
{
if (!IsDetached)
{
if (_stateEntry.State == EntityState.Modified
&& value == EntityState.Unchanged)
{
// Special case modified to unchanged to be "reject changes" even
// ChangeState will do "accept changes". This keeps the behavior consistent with
// setting modified to false at the property level (once that is supported).
CurrentValues.SetValues(OriginalValues);
}
_stateEntry.ChangeState(value);
}
else
{
switch (value)
{
case EntityState.Added:
_internalContext.Set(_entityType).InternalSet.Add(_entity);
break;
case EntityState.Unchanged:
_internalContext.Set(_entityType).InternalSet.Attach(_entity);
break;
case EntityState.Modified:
case EntityState.Deleted:
_internalContext.Set(_entityType).InternalSet.Attach(_entity);
_stateEntry = _internalContext.GetStateEntry(_entity);
Debug.Assert(_stateEntry != null, "_stateEntry should not be null after Attach.");
_stateEntry.ChangeState(value);
break;
}
}
}
}
你看到如果实体被分离(你的情况)并且状态被添加 - 相同的 InternalSet<T>.Add(object)
被调用。
至于测试验证:
using (var ctx = new TestDBEntities()) {
// just some entity, details does not matter
var code = new Code();
// another entity
var error = new Error();
// Code has a collection of Errors
code.Errors.Add(error);
var codeEntry = ctx.Entry(code);
// modify code entry and mark as added
codeEntry.State = EntityState.Added;
// note we did not do anything with Error
var errorEntry = ctx.Entry(error);
// but it is marked as Added too, because when marking Code as Added -
// navigation properties were also explored and attached, just like when
// you do DbSet.Add
Debug.Assert(errorEntry.State == EntityState.Added);
}
我不认识那个博客的作者。我确实认识 book DbContext 的作者(尽管不是本人)。他们对 EF 了如指掌。所以当他们在第 80 页写
Calling DbSet.Add
and setting the State
to Added
both achieve exactly the same thing.
我知道我在做什么。他们完全一样,即:
If the entity is not tracked by the context, it will start being tracked by the context in
the Added
state. Both DbSet.Add
and setting the State
to Added
are graph operations—
meaning that any other entities that are not being tracked by the context and are reachable
from the root entity will also be marked as Added
.
我也根据经验知道它是这样工作的。但为了消除任何疑问,在 EF 的源代码中,DbSet.Add
和 DbEntityEntry.State
(当设置为 Added
时)都到达了 ObjectContext
中完成实际工作的同一点:
public virtual void AddObject(string entitySetName, object entity)
这是一个继续迷惑开始使用 EF 的开发人员的功能,从 Whosebug 上的大量问题中可以明显看出 "how come my entities are duplicated?"。 Julie Lerman 写了一篇 entire blog 解释了为什么会发生这种情况。
这种持续的错觉使 EF 团队决定 change this behavior 在 EF7 中。
也许您提到的博客的作者是那些受骗的开发人员之一。
我正在使用 EF 5 开发 C# ASP.NET MVC 5 Web 应用程序。使用 EF 映射我的数据库表会生成 DbContext
class 和 .edmx
文件。今天,我正在阅读a great article about creating generic DAL classes,但我停在了以下句子:
Note that using the Entry method to change the state of an entity will only affect the actual entity that you pass in to the method. It won’t cascade through a graph and set the state of all related objects, unlike the DbSet.Add method.
这与这些问题中提到的矛盾:
- http://forums.asp.net/p/2015170/5803192.aspx
- http://forums.asp.net/p/2060606/5943259.aspx
- Difference between DbSet.Add(entity) and entity.State = EntityState.Added
- What is the difference between IDbSet.Add and DbEntityEntry.State = EntityState.Added?
在上述所有问题的回答中,所有用户都提到使用System.Data.EntityState.Added
与使用DbSet.Add
完全相同。但是我首先提到的文章指出,使用 System.Data.EntityState.Added
不会级联通过图形。
根据我的测试,我得出结论,使用 System.Data.EntityState.Added
会像在 DBset.Add
的情况下一样在图中级联。是文章写错了,还是我的测试和问答?
这些方法是相同的,您可以通过常规测试来验证,或者,如果您想完全确定 - 通过对 EF 6 代码的一些探索。
DbSet.Add
方法 (http://entityframework.codeplex.com/SourceControl/latest#src/EntityFramework/DbSet.cs)public virtual TEntity Add(TEntity entity) { Check.NotNull<TEntity>(entity, "entity"); this.GetInternalSetWithCheck("Add").Add((object) entity); return entity; }
这会调用 InternalSet<T>.Add(object)
方法。
DbEntityEntry<T>.State
属性 (http://entityframework.codeplex.com/SourceControl/latest#src/EntityFramework/Infrastructure/DbEntityEntry.cs)public EntityState State { get { return _internalEntityEntry.State; } set { _internalEntityEntry.State = value; } }
其中 _internalEntityEntry
属于 InternalEntityEntry
类型。
InternalEntityEntry.State
属性 (http://entityframework.codeplex.com/SourceControl/latest#src/EntityFramework/Internal/EntityEntries/InternalEntityEntry.cs)
public virtual EntityState State
{
get { return IsDetached ? EntityState.Detached : _stateEntry.State; }
set
{
if (!IsDetached)
{
if (_stateEntry.State == EntityState.Modified
&& value == EntityState.Unchanged)
{
// Special case modified to unchanged to be "reject changes" even
// ChangeState will do "accept changes". This keeps the behavior consistent with
// setting modified to false at the property level (once that is supported).
CurrentValues.SetValues(OriginalValues);
}
_stateEntry.ChangeState(value);
}
else
{
switch (value)
{
case EntityState.Added:
_internalContext.Set(_entityType).InternalSet.Add(_entity);
break;
case EntityState.Unchanged:
_internalContext.Set(_entityType).InternalSet.Attach(_entity);
break;
case EntityState.Modified:
case EntityState.Deleted:
_internalContext.Set(_entityType).InternalSet.Attach(_entity);
_stateEntry = _internalContext.GetStateEntry(_entity);
Debug.Assert(_stateEntry != null, "_stateEntry should not be null after Attach.");
_stateEntry.ChangeState(value);
break;
}
}
}
}
你看到如果实体被分离(你的情况)并且状态被添加 - 相同的 InternalSet<T>.Add(object)
被调用。
至于测试验证:
using (var ctx = new TestDBEntities()) {
// just some entity, details does not matter
var code = new Code();
// another entity
var error = new Error();
// Code has a collection of Errors
code.Errors.Add(error);
var codeEntry = ctx.Entry(code);
// modify code entry and mark as added
codeEntry.State = EntityState.Added;
// note we did not do anything with Error
var errorEntry = ctx.Entry(error);
// but it is marked as Added too, because when marking Code as Added -
// navigation properties were also explored and attached, just like when
// you do DbSet.Add
Debug.Assert(errorEntry.State == EntityState.Added);
}
我不认识那个博客的作者。我确实认识 book DbContext 的作者(尽管不是本人)。他们对 EF 了如指掌。所以当他们在第 80 页写
Calling
DbSet.Add
and setting theState
toAdded
both achieve exactly the same thing.
我知道我在做什么。他们完全一样,即:
If the entity is not tracked by the context, it will start being tracked by the context in the
Added
state. BothDbSet.Add
and setting theState
toAdded
are graph operations— meaning that any other entities that are not being tracked by the context and are reachable from the root entity will also be marked asAdded
.
我也根据经验知道它是这样工作的。但为了消除任何疑问,在 EF 的源代码中,DbSet.Add
和 DbEntityEntry.State
(当设置为 Added
时)都到达了 ObjectContext
中完成实际工作的同一点:
public virtual void AddObject(string entitySetName, object entity)
这是一个继续迷惑开始使用 EF 的开发人员的功能,从 Whosebug 上的大量问题中可以明显看出 "how come my entities are duplicated?"。 Julie Lerman 写了一篇 entire blog 解释了为什么会发生这种情况。
这种持续的错觉使 EF 团队决定 change this behavior 在 EF7 中。
也许您提到的博客的作者是那些受骗的开发人员之一。