How to check for a unique violation of restrictions in nHibernate and DDD before saving?

I have an account model object and a UNIQUE constraint for the account name. In Domain Driven Design using nHibernate, how should I check the unicity name before inserting or updating an object?

I do not want to rely on the nHibernate exception to catch the error. I would like to return a more beautiful 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 such a specification.

 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 specification code like:

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

This works for inserts, but not when performing 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 issues an update for the database when it executes GetAccountByName() to restore a possible duplicate ...

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

So what should I do? Is the specification in the wrong way?

Thanks for your thoughts!

+8
nhibernate domain-driven-design
source share
2 answers

I'm not a fan of the data access specification spec template; it always looks like jumping hoops to do something.

However, what you suggested is that it really just comes down to:

  • Check if it exists.
  • Add if it is not; Show a user friendly message.

... is the easiest way to do this.

Relying on exceptions from the database, this is another way to do this if your database and its .NET client gracefully distribute a table and columns (columns) that violate a unique constraint. I believe most drivers do not (??) do this, because they simply throw a general ConstraintException , which reads, "Constraint XYZ was broken on the ABC table." Of course, you can have a convention on your unique naming convention to say something like UK_MyTable_MyColumn and do string magic to output table and column names.

NHibernate has an ISQLExceptionConverter that you can attach to the Configuration object when installing NHibernate. Inside this, you access the .NET data client exception. You can use this exception to retrieve the table and columns (using the name of the constraint, maybe?), And throw a new exception using a user-friendly message.

Using the database exclusion method is more efficient, and you can put a lot of detection-unique restriction-violation code on the infrastructure code, as opposed to processing each case in each case.

Another thing that deserves attention with the query-first-then-add method is that for complete transaction security you need to completely raise the transaction level to serializable (which gives the worst concurrency). Whether you need to be fully bulletproof or not, depends on your application needs.

+5
source share

You need to process it using Session.FlushMode mode to set FlushMode.Commit and use the transaction to roll back, if at all the update is updated.

-2
source share

All Articles