如何克服从数据库插入方法设计挑战中返回的 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;
}
上面的方法很简单,但是违反了简洁的编程原则。
- 反对Command/Query方法分离。
- 它做的不止一项工作。
当我评估不同的方法时,我通常会列出优点和缺点,然后选择缺点和权衡最少的方法。因此,老实说,上面的方法看起来比下面列出的替代方法要好。但我仍然想听取 SO 社区的意见,也许我学到了一种最适合这个挑战的新模式。
选择#1
一种选择是有两种方法。当一个插入新记录时,另一个从数据库中获取最后添加的 personId
。但这并不可靠,除非您阻止数据库在您插入记录和从数据库获取其 ID 之间接受新的人员插入。
从数据库中获取记录时,您甚至可以通过 Person
的 属性(例如 Name
)进行过滤,但除了我上面所说的之外,还可以也可以不止一个同名不同PersonId
s.
它还在进行一次数据库访问。我是一个务实的人,不喜欢在没有实际测量的情况下推测性能。但是,如果我可以通过一个简单的改变来阻止某些事情,即使对性能有轻微的贡献,那么我觉得不这样做是愚蠢的。当然,在做的同时,我也考虑了干净的代码实践。
备选方案 #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 多个值时,out
和 ref
更有意义。例如 TryParse()
、returns boolean
,但您也可以使用 out
或 ref
获取解析值。
更新
鉴于一些评论,我决定进一步澄清我的问题。我要问的是如何在不破坏干净代码原则的情况下获得 PersonId
。我正在使用 EF,因此从数据库中获取 ID 不是问题,实际上您可以在我的第一个示例中看到它。抱歉造成混淆。
您是否考虑过举办活动? PersonCreatedEvent
另见:http://blog.ploeh.dk/2014/08/11/cqs-versus-server-generated-ids/
很明显你的设计有问题。你 return 这个 PersonId 的事实意味着你需要它来识别你的域逻辑(如果你愿意的话,业务逻辑)中的人。
问题是:什么数据库 ID 与个人身份有关?它在您的域中有意义吗?
如果没有,则使用其他方式在您的域逻辑中识别 Person。这至少会给您带来以下好处:
你将能够用工厂创建人,(如果它是一个实体并且可能聚合根),让他对你的领域逻辑身份有意义。
您将能够使用 Repository 保存新创建的人员,而不必在您的域逻辑中处理它的数据库 ID。
然后你就可以这样写了:
var newPerson = PersonFactory.Create();
//do some work with person in domain logic
PersonRepository.Persist(newPerson);
可能会很长 post 但请耐心等待。基本思路是这样的:
public int InsertPersonAndGetPersonId(Person person){
_dbContext.Insert(person);
return person.PersonId;
}
上面的方法很简单,但是违反了简洁的编程原则。
- 反对Command/Query方法分离。
- 它做的不止一项工作。
当我评估不同的方法时,我通常会列出优点和缺点,然后选择缺点和权衡最少的方法。因此,老实说,上面的方法看起来比下面列出的替代方法要好。但我仍然想听取 SO 社区的意见,也许我学到了一种最适合这个挑战的新模式。
选择#1
一种选择是有两种方法。当一个插入新记录时,另一个从数据库中获取最后添加的 personId
。但这并不可靠,除非您阻止数据库在您插入记录和从数据库获取其 ID 之间接受新的人员插入。
从数据库中获取记录时,您甚至可以通过 Person
的 属性(例如 Name
)进行过滤,但除了我上面所说的之外,还可以也可以不止一个同名不同PersonId
s.
它还在进行一次数据库访问。我是一个务实的人,不喜欢在没有实际测量的情况下推测性能。但是,如果我可以通过一个简单的改变来阻止某些事情,即使对性能有轻微的贡献,那么我觉得不这样做是愚蠢的。当然,在做的同时,我也考虑了干净的代码实践。
备选方案 #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 多个值时,out
和 ref
更有意义。例如 TryParse()
、returns boolean
,但您也可以使用 out
或 ref
获取解析值。
更新
鉴于一些评论,我决定进一步澄清我的问题。我要问的是如何在不破坏干净代码原则的情况下获得 PersonId
。我正在使用 EF,因此从数据库中获取 ID 不是问题,实际上您可以在我的第一个示例中看到它。抱歉造成混淆。
您是否考虑过举办活动? PersonCreatedEvent
另见:http://blog.ploeh.dk/2014/08/11/cqs-versus-server-generated-ids/
很明显你的设计有问题。你 return 这个 PersonId 的事实意味着你需要它来识别你的域逻辑(如果你愿意的话,业务逻辑)中的人。
问题是:什么数据库 ID 与个人身份有关?它在您的域中有意义吗? 如果没有,则使用其他方式在您的域逻辑中识别 Person。这至少会给您带来以下好处:
你将能够用工厂创建人,(如果它是一个实体并且可能聚合根),让他对你的领域逻辑身份有意义。
您将能够使用 Repository 保存新创建的人员,而不必在您的域逻辑中处理它的数据库 ID。
然后你就可以这样写了:
var newPerson = PersonFactory.Create();
//do some work with person in domain logic
PersonRepository.Persist(newPerson);