2010-02-19 21 views
9

Tengo un objeto llamado "Cliente" que se utilizará en las otras tablas como claves foráneas.¿Cómo encontrar si un objeto al que se hace referencia se puede eliminar?

El problema es que quiero saber si se puede eliminar un "Cliente" (es decir, no se está haciendo referencia en ninguna otra tabla).

¿Es esto posible con Nhibernate?

+0

Usted dice que 'Customer' está referenciado en" las otras tablas ". ¿El plural está destinado? Es decir, ¿se hace referencia al 'Cliente 'desde * varias * otras clases de entidad? (La respuesta afecta las posibles soluciones.) –

+0

@Jorn, Sí, se hace referencia en muchas otras tablas. –

Respuesta

5

Lo que está preguntando es encontrar la existencia del valor PK Customer en la columna FK de las tablas referenciadas. Hay muchas maneras en que puede ir sobre esto:

  1. como se ha indicado kgiannakakis, tratar de hacer el borrado y si se produce una excepción de reversión. Eficaz pero feo y no útil. Esto también requiere que haya establecido CASCADE = "RESTRICT" en su base de datos. Esta solución tiene el inconveniente de que hay que tratar de eliminar el objeto de averiguar que no se puede

  2. en el mapa los que hacen referencia a entidades Customer como colecciones y luego para cada colección si su Count > 0 entonces no permiten la eliminación. Esto es bueno porque es seguro contra cambios de esquema siempre que la asignación esté completa. También es una mala solución porque se deben realizar selecciones adicionales.

  3. Tienen un método que realiza una consulta como bool IsReferenced(Customer cust). Bueno porque puede tener una sola consulta que usará cuando lo desee. No tan bueno, ya que puede ser susceptible a errores debido a esquema y/o cambios de dominio (en función del tipo de consulta que va a hacer: SQL/HQL/criterios).

  4. Propiedad calculada en la clase en sí con un elemento de correlación como <property name="IsReferenced" type="long" formula="sql-query that sums the Customer id usage in the referenced tables" />. Bien porque es una solución rápida (al menos tan rápido como su base de datos), sin consultas adicionales. No tan bueno, ya que es susceptible a cambios en el esquema modo que cuando cambie su base de datos no hay que olvidar a actualizar esta consulta.

  5. solución loco: crear una vista con destino esquema que hace el cálculo. Realice la consulta cuando lo desee.Bueno porque está vinculado al esquema y es menos susceptible a cambios de esquema, es bueno porque la consulta es rápida, no tan buena porque todavía tiene que hacer una consulta adicional (o correlaciona el resultado de esta vista con la solución 4).

2,3,4 también son buenos porque también se puede proyectar este comportamiento de la interfaz de usuario (no permita que el borrado)

Personalmente me inclinaría por 4,3,5 con esa preferencia

+1

Esta es una buena respuesta, es difícil responder más específicamente que esto porque los detalles de su situación no son muy claros. La respuesta corta a tu pregunta es "sí". Cualquier consulta que pueda imaginar ejecutarse en su base de datos es posible en nHibernate de una manera u otra (use HQL si está interesado). –

0

Puede valer la pena mirar la propiedad de la cascada, en particular todos-eliminar-huérfano en sus archivos hbm.xml y esto puede encargarse de ello.

See here, 16.3 - Cascading Lifecycle

1

No es posible directamente. Presumiblemente, su modelo de dominio incluye objetos relacionados con el cliente, como direcciones, pedidos, etc. Para esto, debe usar el specification pattern.

public class CustomerCanBeDeleted 
{ 

    public bool IsSatisfiedBy(Customer customer) 
    { 
     // Check that related objects are null and related collections are empty 
     // Plus any business logic that determines if a Customer can be deleted 
    } 
} 

Editado para añadir:

Tal vez el método más sencillo sería crear un procedimiento almacenado que realiza esta comprobación y llame antes de eliminar. Puede acceder a un IDbCommand desde NHibernate (ISession.Connection.CreateCommand()) para que la llamada sea independiente de la base de datos.

Consulte también las respuestas a this question.

0

Una solución ingenua será usar una transacción. Comience una transacción y elimine el objeto. Una excepción le informará que el objeto no puede ser eliminado. En cualquier caso, haz un roll-back.

2

Al pensar en entidades y relaciones en lugar de tablas y claves externas, existen estas situaciones diferentes:

  • El cliente tiene una relación de uno a varios que forma parte del cliente, por ejemplo, sus números de teléfono. También deberían eliminarse mediante cascada.
  • El cliente tiene una relación uno a muchos o muchos a muchos que no es parte del cliente, pero el cliente los conoce.
  • Alguna otra entidad tiene una relación con el Cliente. También podría ser de cualquier tipo (que no es una clave externa en la base de datos). Por ejemplo, pedidos del cliente. Los pedidos no son conocidos por el cliente. Este es el caso más difícil.

Por lo que sé, no hay una solución directa de NHibernate. Existe la API de metadatos, que le permite explorar las definiciones de asignación en tiempo de ejecución. En mi humilde opinión, esta es la forma incorrecta de hacerlo.

En mi opinión, es la responsabilidad de la lógica de negocios para validar si una entidad se puede eliminar o no. (Incluso si hay claves externas y restricciones que aseguran la integridad de la base de datos, sigue siendo lógica de negocios).

Implementamos un servicio que se llama antes de la eliminación de una entidad. Otras partes del software se registran para ciertos tipos. Pueden vetar en contra de la eliminación (por ejemplo, lanzando una excepción).

Por ejemplo, el sistema de pedidos se registra para la eliminación de clientes. Si un cliente debe ser eliminado, el sistema de pedidos busca los pedidos de este cliente y los lanza si encuentra uno.

+0

Me hiciste pensar acerca de esto en la lógica comercial –

4

Quiero saber si se puede eliminar un "Cliente" (es decir, no se está haciendo referencia en ninguna otra tabla).

En realidad, no es responsabilidad de la base de datos determinar si el cliente puede ser eliminado. Es más bien parte de su lógica comercial.

Está solicitando verificar la integridad referencial en la base de datos.

Está bien en el mundo no OOP. Pero cuando se trata de objetos (como lo hace), es mejor agregar la lógica a sus objetos (objetos tienen estado y comportamiento; DB - solo el estado).

Por lo tanto, agregaría un método a la clase Cliente para determinar si se puede eliminar o no. De esta manera, puede probar (unidad) correctamente la funcionalidad.

Por ejemplo, supongamos que tenemos una regla El cliente solo puede eliminarse si no tiene pedidos y no ha participado en el foro.

, entonces tendrá objeto Cliente similar a este (caso más simple posible):

public class Customer 
{ 
    public virtual ISet<Order> Orders { get; protected set; } 
    public virtual ISet<ForumPost> ForumPosts { get; protected set; } 

    public virtual bool CanBedeleted 
    { 
     get 
     { 
      return Orders.Count == 0 && ForumPosts.Count == 0 
     } 
    } 
} 

Esto es muy limpio y el diseño que es fácil de usar, simple prueba y no depende en gran medida NHibernate o base de datos subyacente .

Se puede utilizar la siguiente manera:

if (myCustomer.CanBeDeleted) 
    session.Delete(mycustomer) 

Además de que se puede afinar NHibernate para borrar las órdenes relacionadas y otras asociaciones, si es necesario.


La nota: por supuesto, el ejemplo anterior es sólo más simple solución ilustrativa posible. Es posible que desee establecer una regla part of the validation que se debe aplicar al eliminar el objeto.

+1

Estás haciendo (al menos) 2 selects adicionales llamando a la propiedad [collection] .Count. Además, una prueba de unidad mal formada (o una que no existe) pasará una condición. ¡Esto se puede guardar aplicando restricciones a la base de datos! Siempre es una buena práctica que el backend no dependa de la interfaz para la integridad de datos de la lógica de negocios. – Jaguar

+0

Puede ajustar el SQL en el mapeo utilizando fetch fetch o de otra manera. El propósito fue demostrar la idea. En cuanto a "Esto se puede guardar aplicando restricciones a la base de datos", mencioné que también debería ser parte de la validación. –

+0

Argumentaría que verificar la integridad referencial de la base de datos es ** NO ** parte de la lógica comercial: creo que este es solo el caso si su programa es un programa de base de datos. – fostandy

Cuestiones relacionadas