为什么DbSet Add返回实体实例而不是void?

14

DbSet<TEntity>.Add 方法返回一个实体。通常我会期望像 Add 这样的操作返回类型为 void

当我查看EntityFramework源代码时,我看到以下实现:

    public virtual TEntity Add(TEntity entity)
    {
        Check.NotNull(entity, "entity");

        GetInternalSetWithCheck("Add").Add(entity);
        return entity;
    }

GetInternalSetWithCheck 返回一个 InternalSet<TEntity>

有趣的是,InternalSet<TEntity>Add 方法在其签名中具有 void 返回类型:

public virtual void Add(object entity)

我的担忧是,当我修改与DbSet相关联的实体时,是否需要注意何时进行修改。

例如,是否存在这样的情况

var entity = new MyEntity();
_dbSet.Add(entity);
entity.SomeDatModifyingMethod();
_dbContext.SaveChanges();

可能会导致不同的行为,而不是

var entity = new MyEntity();
entity.SomeDatModifyingMethod();
_dbSet.Add(entity);
_dbContext.SaveChanges();

或者不同的行为:

var entity = new MyEntity();
entity = _dbSet.Add(entity);
entity.SomeDatModifyingMethod();
_dbContext.SaveChanges();

在基本默认实现中,这并不重要,因为它只返回完全相同的实例。但是,Add 方法是 virtual 的,因此它可以被覆盖(尽管在公共源代码中,唯一的覆盖是一个测试双倍体 - 但我不确定该源代码是否实际包括例如 SqlServer 支持实现)。

为什么 DbSet Add 返回实体实例而不是 void?

2个回答

8
它允许您编写“查找或添加”模式。
var person = context.People.Find(ssn) ?? context.People.Add(new Person
{
   SocialSecurityNumber = ssn,
   FirstName = "John",
   LastName = "Doe"
});

6
TEntity是一个引用类型,所以添加到InternalSet<T>的内容将是实体的引用,而不是值。无论您在将实体添加到集合之前还是之后更改实体的内容,都没有关系,因为它从未在数据库中创建。无论如何,都会执行INSERT或等效操作。
至于为什么Add返回TEntity,我认为这是因为它允许像这样的操作:
_dbSet.Add(new MyEntity()).SomeDatModifyingMethod();
_dbContext.SaveChanges();

这基本上是有道理的 - 只要Add方法永远不会像返回代理那样做某些事情 - 也许是为了更改跟踪? - 在这种情况下,在添加后重新分配引用可能很重要?(例如,最后一个代码示例) - Nathan
虽然仔细想想,我不知道为什么你会需要在新添加的实体上进行更改跟踪... - Nathan
“Add” 可能不是在 “SaveChanges” 之前的最后一次更改。如果有一个 “Remove” 呢? - John Saunders
抱歉,当我说对于新添加的实体不需要更改跟踪时,我的意思是除了事实上它被添加之外,它不需要跟踪这个被添加的实体 - 这意味着即使你使用EF代理更改跟踪,似乎没有理由Add方法需要“代理”已添加的实体(以跟踪单个属性更改)- 因此支持了你的说法,返回实体只是为了进行方法链式调用。 - Nathan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接