如何在保存之前检查 nHibernate 和 DDD 中的唯一约束违规?

发布于 2024-12-08 06:34:03 字数 1448 浏览 0 评论 0原文

我有一个帐户模型对象和帐户名称的唯一约束。在域驱动设计中,使用 nHibernate,在插入或更新实体之前,我应该如何检查名称的唯一性?

我不想依赖 nHibernate 异常来捕获错误。我想向我的用户返回一条比晦涩的无法执行批处理命令更漂亮的错误消息。[SQL:SQL不可用]

在问题我应该在 DDD 中的哪里放置唯一检查?,有人建议使用这样的规范。

Account accountA = _accountRepository.Get(123);
Account accountB = _accountRepository.Get(456);
accountA.Name = accountB.Name;

ISpecification<Account> spec = new Domain.Specifications.UniqueNameSpecification(_accountRepository);
if (spec.IsSatisfiedBy(accountObjA) == false) {
   throw new Domain.UnicityException("A duplicate Account name was found");
}

规范代码为:

public bool IsSatisfiedBy(Account obj)
{
   Account other = _accountRepository.GetAccountByName(obj.Name);
   return (other == null);
}

这适用于插入,但不适用于更新,因为。我尝试将代码更改为:

public bool IsSatisfiedBy(Account obj)
{
   Account other = _accountRepository.GetAccountByName(obj.Name);

   if (obj == null) {  // nothing in DB
      return true;
   }
   else {              // must be the same object.
      return other.Equals(obj);
   }
}

问题是 nHibernate 在执行 GetAccountByName() 时会向数据库发出更新以恢复可能的重复项...

return session.QueryOver<Account>().Where(x => x.Name == accntName).SingleOrDefault();

那么,我应该做什么?该规范不是正确的方法吗?

感谢您的想法!

I've got an Account model object and a UNIQUE constraint on the account's Name. In Domain Driven Design, using nHibernate, how should I check for the name's unicity before inserting or updating an entity?

I don't want to rely on a nHibernate exception to catch the error. I'd like to return a prettier error message to my user than the obscure could not execute batch command.[SQL: SQL not available]

In the question Where should I put a unique check in DDD?, someone suggested using a Specification like so.

Account accountA = _accountRepository.Get(123);
Account accountB = _accountRepository.Get(456);
accountA.Name = accountB.Name;

ISpecification<Account> spec = new Domain.Specifications.UniqueNameSpecification(_accountRepository);
if (spec.IsSatisfiedBy(accountObjA) == false) {
   throw new Domain.UnicityException("A duplicate Account name was found");
}

with the Specification code as:

public bool IsSatisfiedBy(Account obj)
{
   Account other = _accountRepository.GetAccountByName(obj.Name);
   return (other == null);
}

This works for inserts, but not when doing an update because. I tried changing the code to:

public bool IsSatisfiedBy(Account obj)
{
   Account other = _accountRepository.GetAccountByName(obj.Name);

   if (obj == null) {  // nothing in DB
      return true;
   }
   else {              // must be the same object.
      return other.Equals(obj);
   }
}

The problem is that nHibernate will issue an update to the database when it executes GetAccountByName() to recover a possible duplicate...

return session.QueryOver<Account>().Where(x => x.Name == accntName).SingleOrDefault();

So, what should I do? Is the Specification not the right way to do it?

Thanks for your thoughts!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

零度° 2024-12-15 06:34:03

我不喜欢数据访问的规范模式,它总是看起来像是跳圈来完成任何事情。

但是,您所建议的实际上只是归结为:

  1. 检查它是否已经存在。
  2. 如果没有则添加;如果存在,则显示用户友好的消息。

...几乎是完成它的最简单的方法。

如果您的数据库及其 .NET 客户端能够优雅地传播表和数据,那么依赖数据库异常是实现此目的的另一种方法。违反唯一约束的列。我相信大多数驱动程序不会这样做(??),因为他们只是抛出一个通用的 ConstraintException ,表示“在表 ABC 上违反了约束 XYZ”。当然,您可以对您的唯一约束命名有一个约定,例如 UK_MyTable_MyColumn 并执行字符串魔术来拉出表和列。列名出来。

NHibernate 有一个 ISQLExceptionConverter,您可以在设置 NHibernate 时将其插入到 Configuration 对象中。在此期间,您会遇到来自 .NET 数据客户端的异常。您可以使用该异常来提取表和列(也许使用约束名称?)并抛出一个新的异常以及用户友好的消息。

使用数据库异常方式的性能更高,您可以将大量检测唯一约束违规代码推送到基础设施层,而不是逐一处理。

使用 query-first-then-add 方法时值得指出的另一件事是,为了完全事务安全,您需要将事务级别升级为可序列化(这会带来最差的并发性)才能完全安全证明。您是否需要完全防弹,取决于您的应用程序需求。

I'm not a fan of the specification pattern for data access, it always seems like jumping hoops to get anything done.

However, what you've suggested, which really just boils down to:

  1. Check if it already exists.
  2. Add if it doesn't; Show user-friendly message if it does.

... is pretty much the easiest way to get it done.

Relying on database exceptions is the other way of doing it, if your database and it's .NET client gracefully propagates the table & column(s) that were infringing the unique constraint. I believe most drivers don't do so (??), as they just throw a generic ConstraintException that says "Constraint XYZ was violated on table ABC". You can of course have a convention on your unique constraint naming to say something like UK_MyTable_MyColumn and do string magic to pull the table & column names out.

NHibernate has a ISQLExceptionConverter that you can plug into the Configuration object when you set NHibernate up. Inside this, you get exposed to the exception from the .NET data client. You can use that exception to extract the table & columns (using the constraint name perhaps?) and throw a new Exception with a user friendly message.

Using the database exception way is more performant and you can push a lot of the detecting-unique-constraint-violation code to the infrastructure layer, as opposed to handling each one case by case.

Another thing worth pointing out with the query-first-then-add method is that to be completely transaction safe, you need to escalate the transaction level to serializable (which gives the worst concurrency) to be totally bullet proof. Whether you need to be totally bullet proof or not, depends on your application needs.

捂风挽笑 2024-12-15 06:34:03

您需要使用 Session.FlushMode 模式来处理它,以设置为 FlushMode.Commit 并使用事务来回滚(如果触发了所有更新)。

You need to handle it with Session.FlushMode mode to set to FlushMode.Commit and use transaction to rollback if at all update fired.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文