如何克服从数据库插入方法设计挑战中返回的 ID

How to overcome returning ID from a database insert method design challenge

可能会很长 post 但请耐心等待。基本思路是这样的:

public int InsertPersonAndGetPersonId(Person person){
    _dbContext.Insert(person);
    return person.PersonId;
}

上面的方法很简单,但是违反了简洁的编程原则。

  1. 反对Command/Query方法分离。
  2. 它做的不止一项工作。

当我评估不同的方法时,我通常会列出优点和缺点,然后选择缺点和权衡最少的方法。因此,老实说,上面的方法看起来比下面列出的替代方法要好。但我仍然想听取 SO 社区的意见,也许我学到了一种最适合这个挑战的新模式。

选择#1

一种选择是有两种方法。当一个插入新记录时,另一个从数据库中获取最后添加的 personId 。但这并不可靠,除非您阻止数据库在您插入记录和从数据库获取其 ID 之间接受新的人员插入。

从数据库中获取记录时,您甚至可以通过 Person 的 属性(例如 Name)进行过滤,但除了我上面所说的之外,还可以也可以不止一个同名不同PersonIds.

它还在进行一次数据库访问。我是一个务实的人,不喜欢在没有实际测量的情况下推测性能。但是,如果我可以通过一个简单的改变来阻止某些事情,即使对性能有轻微的贡献,那么我觉得不这样做是愚蠢的。当然,在做的同时,我也考虑了干净的代码实践。

备选方案 #2

另一种方法可以是更改 InsertPersonAndGetPersonId 并具有如下内容:

public class PersonRepository
{
    private int _personId;

    public void InsertPerson(Person person){
        _dbContext.Insert(person);
        _personId = person.PersonId;
    }

    public int GetLastPersonId
        return _personId;
    }
} 

尽管我不喜欢此方法的名称 GetLastPersonId(),它可能会带来与预期不同的 personId 但我们假设它 return 是 id person 个对象。它不好的原因,除了我已经说过的,它正在修改对象的状态,因此有副作用。

选择#3

我们可以简单地使用下面的方法:

public void InsertPerson(Person person){
    _dbContext.Insert(person);
    _personId = person.PersonId;
}

并且由于 person 是引用类型,我们可以像下面这样访问 person.PersonId

var personRepository = new PersonRepository();
var person = new Person() {Name="Hello"};
personRepository.InsertPerson(person);
Console.WriteLine(person.PersonId);

我喜欢吗?不!它太隐蔽且不可预测,除非您检查实现细节然后我们打破抽象之美,否则您不会真正了解它。

选择#4

我们可以像下面这样使用out

public void InsertPerson(Person person, out int personId){
    _dbContext.Insert(person);
    personId = person.PersonId;
}

但这看起来比第一个更愚蠢和麻烦 InsertPersonAndGetPersonId。如果我必须 return 某些东西,那么我会 return 使用 return 并使签名对开发人员更明确。事实上,当我们需要 return 多个值时,outref 更有意义。例如 TryParse()、returns boolean,但您也可以使用 outref 获取解析值。

更新

鉴于一些评论,我决定进一步澄清我的问题。我要问的是如何在不破坏干净代码原则的情况下获得 PersonId 。我正在使用 EF,因此从数据库中获取 ID 不是问题,实际上您可以在我的第一个示例中看到它。抱歉造成混淆。

您是否考虑过举办活动? PersonCreatedEvent 另见:http://blog.ploeh.dk/2014/08/11/cqs-versus-server-generated-ids/

很明显你的设计有问题。你 return 这个 PersonId 的事实意味着你需要它来识别你的域逻辑(如果你愿意的话,业务逻辑)中的人。

问题是:什么数据库 ID 与个人身份有关?它在您的域中有意义吗? 如果没有,则使用其他方式在您的域逻辑中识别 Person。这至少会给您带来以下好处:

  1. 你将能够用工厂创建人,(如果它是一个实体并且可能聚合根),让他对你的领域逻辑身份有意义。

  2. 您将能够使用 Repository 保存新创建的人员,而不必在您的域逻辑中处理它的数据库 ID。

然后你就可以这样写了:

var newPerson = PersonFactory.Create();
//do some work with person in domain logic  
PersonRepository.Persist(newPerson);