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

时间:2011-10-06 16:15:01

标签: nhibernate domain-driven-design

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

我不想依赖nHibernate异常来捕获错误。我想向我的用户返回一个比隐藏的could not execute batch command.[SQL: SQL not available]

更漂亮的错误消息

在问题Where should I put a unique check in 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();

那么,我该怎么办?规范不是是正确的方法吗?

感谢您的想法!

2 个答案:

答案 0 :(得分:5)

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

然而,你所建议的,实际上只归结为:

  1. 检查它是否已存在。
  2. 如果没有,则添加;如果有,请显示用户友好的消息。
  3. ......几乎是完成任务的最简单方法。

    如果您的数据库及其.NET客户端正常地传播表格,那么依赖数据库异常是另一种方法。侵犯唯一约束的列。我相信大多数司机不这样做(??),因为他们只是抛出一个通用的ConstraintException,上面写着“约束XYZ在表ABC上被违反”。你当然可以在你的唯一约束命名上有一个约定来说出像UK_MyTable_MyColumn这样的东西,然后用字符串法术来拉动表格和放大器。专栏名称。

    NHibernate有ISQLExceptionConverter,你可以在设置NHibernate时插入Configuration对象。在此内容中,您将接触到.NET数据客户端的异常。您可以使用该例外来提取表格和列(可能使用约束名称?)并使用用户友好消息抛出一个新的异常。

    使用数据库异常方式更具性能,您可以将大量 detect-unique-constraint-violation 代码推送到基础架构层,而不是逐个处理每个代码。 / p>

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

答案 1 :(得分:-2)

您需要使用Session.FlushMode模式处理它以设置为FlushMode.Commit,并在所有更新都被触发时使用事务来回滚。