2011-10-06 18 views
8

Tengo un objeto de modelo de cuenta y una restricción ÚNICA en el nombre de la cuenta. En Domain Driven Design, usando nHibernate, ¿cómo debo verificar la unicidad del nombre antes de insertar o actualizar una entidad?¿Cómo verificar la violación de restricción única en nHibernate y DDD antes de guardar?

No deseo confiar en una excepción nHibernate para detectar el error. Me gustaría devolver un mensaje de error más bonito a mi usuario que el could not execute batch command.[SQL: SQL not available] oscuro

En la pregunta Where should I put a unique check in DDD?, alguien sugirió usar una especificación como tal.

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"); 
} 

con el código de especificación como:

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

Esto funciona para inserciones, pero no cuando se hace una actualización debido. He intentado cambiar el código para:

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); 
    } 
} 

El problema es que NHibernate emitirá una actualización de la base de datos cuando se ejecuta GetAccountByName() para recuperar un posible duplicado ...

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

Así que, ¿qué debo ¿hacer? ¿Es la especificación no la forma correcta de hacerlo?

Gracias por su opinión!

Respuesta

5

No soy un fan del patrón de especificación para el acceso a los datos, siempre parece saltar los aros para hacer cualquier cosa.

Sin embargo, lo que se ha sugerido, que en realidad sólo se reduce a:

  1. Comprobar si ya existe.
  2. Agregar si no lo hace; Mostrar mensaje fácil de usar si lo hace.

... es casi la forma más fácil de hacerlo.

Depender de las excepciones de la base de datos es la otra forma de hacerlo, si su base de datos y su cliente .NET propagan graciosamente las columnas de la tabla & que estaban infringiendo la restricción única. Creo que la mayoría de los controladores no lo hacen (??), ya que solo lanzan un genérico ConstraintException que dice "Se violó la restricción XYZ en la tabla ABC". Por supuesto, puede tener una convención sobre su designación de limitación única para decir algo como UK_MyTable_MyColumn y hacer magia de cadena para sacar los nombres de columna de la tabla &.

NHibernate tiene un ISQLExceptionConverter que puede enchufar en el objeto Configuration cuando configura NHibernate. Dentro de esto, se expone a la excepción del cliente de datos .NET. Puede usar esa excepción para extraer las columnas de la tabla & (¿utilizando quizás el nombre de la restricción?) Y lanzar una nueva excepción con un mensaje fácil de usar.

El uso de la excepción de base de datos es más eficaz y puede insertar una gran cantidad del código detecting-unique-constraint-violation en la capa de infraestructura, en lugar de manejar cada caso por caso.

Otra cosa vale la pena señalar con el consulta, primero en el entonces añadir método es que para ser completamente transacción segura, es necesario intensificar el nivel de transacción a serializable (que da el peor de concurrencia) para ser totalmente a prueba de balas . Si necesita ser totalmente a prueba de balas o no, depende de las necesidades de su aplicación.

+1

Gracias Thilak, voy a echar un vistazo a 'ISQLExceptionConverter'. Encontré [esta publicación en el blog] (http://fabiomaulo.blogspot.com/2009/06/improving-ado-exception-management-in.html) y [esta otra pregunta] (http://stackoverflow.com/questions)/1524167/custom-exception-using-nhibernate-isqlexceptionconverter) para ayudar con 'ISQLExceptionConverter' – dstj

-2

Debe manejarlo con el modo Session.FlushMode para establecerlo en FlushMode.Commit y usar la transacción para deshacer si se ha activado la actualización.

+0

He intentado configurar FlushMode para que la ISession se comprometa, pero obtengo el mismo resultado. La actualización se envía a la base de datos antes de la selección y se genera una excepción ... – dstj

+1

@dstj 1, ¿Está utilizando una transacción? 2, si usa FlushMode para confirmar, estoy seguro de que los datos no se actualizarán hasta que llame a transaction.Commit. 3, use el NHProfiler para averiguar cuándo se iniciará la actualización. –

Cuestiones relacionadas