2010-02-19 13 views
9

Ich habe ein Objekt namens "Kunde", das in den anderen Tabellen als Fremdschlüssel verwendet wird.Wie finde ich heraus, ob ein referenziertes Objekt gelöscht werden kann?

Das Problem ist, dass ich wissen möchte, ob ein "Kunde" gelöscht werden kann (dh es wird nicht in anderen Tabellen referenziert).

Ist dies mit Nhibernate möglich?

+0

Sie sagen, dass "Kunde" in "den anderen Tabellen" referenziert wird. Ist der Plural beabsichtigt? Dh, wird "Kunde" von * mehreren * anderen Entitätsklassen referenziert? (Die Antwort beeinflusst die möglichen Lösungen.) –

+0

@Jorn, Ja, es wird in vielen anderen Tabellen referenziert. –

Antwort

5

Was Sie fragen, ist die Existenz der Customer PK-Wert in der referenzierten Tabellen FK-Spalte zu finden. Es gibt viele Möglichkeiten, wie Sie zu diesem gehen können:

  1. als kgiannakakis erwähnt, versuchen, das Löschen zu tun, und wenn eine Ausnahme Rollback ausgelöst. Wirksam, aber hässlich und nicht nützlich. Dies erfordert auch, dass Sie in Ihrer Datenbank eine CASCADE = "RESTRICT" gesetzt haben. Diese Lösung hat den Nachteil, dass Sie versuchen müssen, das Objekt zu löschen, um herauszufinden, dass Sie nicht die Entitäten

  2. Karte können die Customer als Sammlungen verweisen und dann für jede Sammlung, wenn ihre Count > 0 dann nicht zulassen, um den Lösch. Das ist gut, weil dies sicher gegen Schemaänderungen ist, solange das Mapping abgeschlossen ist. Es ist auch eine schlechte Lösung, weil zusätzliche Selektionen vorgenommen werden müssen.

  3. Haben Sie eine Methode, die eine Abfrage wie bool IsReferenced(Customer cust) ausführt. Gut, weil Sie eine einzelne Abfrage haben können, die Sie verwenden, wenn Sie möchten. Nicht so gut, weil es anfällig für Fehler aufgrund von Schema- und/oder Domänenänderungen sein kann (abhängig von der Art der Abfrage, die Sie ausführen werden: sql/hql/criteria).

  4. Eine berechnete Eigenschaft auf der Klasse selbst mit einem Mapping-Element wie <property name="IsReferenced" type="long" formula="sql-query that sums the Customer id usage in the referenced tables" />. Gut, weil es eine schnelle Lösung ist (mindestens so schnell wie deine DB), keine zusätzlichen Abfragen. Nicht so gut, da es anfällig für Schemaänderungen ist. Wenn Sie also Ihre Datenbank ändern, dürfen Sie nicht vergessen, diese Abfrage zu aktualisieren.

  5. verrückte Lösung: Erstellen Sie eine Schema gebundene Ansicht, die die Berechnung macht. Machen Sie die Abfrage, wenn Sie möchten.Gut, weil das Schema gebunden und weniger anfällig für Schemaänderungen ist, gut, weil die Abfrage schnell, nicht so gut ist, weil Sie noch eine zusätzliche Abfrage ausführen müssen (oder Sie das Ergebnis dieser Ansicht auf Lösung 4 abbilden).

2,3,4 sind auch gut, weil Sie dieses Verhalten auch auf Ihre UI projizieren können (nicht zulassen, dass der Lösch)

persönlich würde ich mit dieser Vorliebe für 4,3,5 gehen

+1

Dies ist eine gute Antwort, es ist schwierig, genauer zu antworten als dies, weil die Details Ihrer Situation nicht sehr klar sind. Die kurze Antwort auf Ihre Frage lautet "Ja". Jede Abfrage, die Sie sich vorstellen können, läuft in Ihrer Datenbank in nHibernate auf die eine oder andere Weise (verwenden Sie HQL, wenn Sie davon abhängig sind). –

0

Es könnte sich lohnen, sich die cascade-Eigenschaft, insbesondere all-delete-orphan, in Ihren hbm.xml-Dateien anzuschauen, und dies könnte für Sie erledigt werden.

See here, 16.3 - Cascading Lifecycle

1

Es ist direkt nicht möglich. Vermutlich enthält Ihr Domain-Modell Objekte, die mit dem Kunden in Verbindung stehen, wie Adressen, Bestellungen usw. Sie sollten hierfür die specification pattern verwenden.

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

Edited hinzufügen:

Vielleicht ist die einfachste Methode, eine gespeicherte Prozedur zu erstellen wäre, die diese Prüfung durchführt und sie rufen vor dem Löschen. Sie können auf einen IDbCommand von NHibernate (ISession.Connection.CreateCommand()) zugreifen, so dass der Aufruf datenbankunabhängig ist.

Siehe auch die Antworten auf this question.

0

Eine naive Lösung wird die Verwendung einer Transaktion sein. Starten Sie eine Transaktion und löschen Sie das Objekt. Eine Ausnahme informiert Sie darüber, dass das Objekt nicht gelöscht werden kann. Mach in jedem Fall einen Rollback.

2

Denken in Entitäten und Beziehungen anstelle von Tabellen und Fremdschlüsseln, gibt es diese verschiedenen Situationen:

  • Der Kunde hat eine Eins-zu-viele-Beziehung, die einen Teil des Kunden bildet, zum Beispiel seine Telefonnummern. Sie sollten auch mittels Kaskadierung gelöscht werden.
  • Der Kunde hat eine Eins-zu-Viele- oder Viele-zu-Viele-Beziehung, die nicht Teil des Kunden ist, aber vom Kunden bekannt/erreichbar ist.
  • Eine andere Entität hat eine Beziehung zum Kunden. Es könnte auch ein beliebiger Typ sein (der kein Fremdschlüssel in der Datenbank ist). Zum Beispiel Bestellungen des Kunden. Die Bestellungen sind dem Kunden nicht bekannt. Dies ist der schwierigste Fall.

Soweit ich weiß, gibt es keine direkte Lösung von NHibernate. Es gibt die Metadaten-API, mit der Sie die Mapping-Definitionen zur Laufzeit untersuchen können. IMHO, das ist der falsche Weg, es zu tun.

Meiner Meinung nach ist es die Verantwortung der Geschäftslogik zu validieren, wenn eine Entität gelöscht werden kann oder nicht. (Selbst wenn es Fremdschlüssel und Einschränkungen gibt, die die Integrität der Datenbank sicherstellen, ist es immer noch Geschäftslogik).

Wir haben einen Service implementiert, der vor dem Löschen einer Entität aufgerufen wird. Andere Teile der Software registrieren sich für bestimmte Typen. Sie können ein Veto gegen die Löschung einlegen (z. B. durch Auslösen einer Ausnahme).

Zum Beispiel registriert das Bestellsystem für die Löschung von Kunden. Wenn ein Kunde gelöscht werden soll, sucht das Bestellsystem nach Aufträgen dieses Kunden und wirft, falls es einen gefunden hat.

+0

Sie haben mich darüber nachgedacht in der Geschäftslogik zu sein –

4

Ich möchte wissen, ob ein "Kunde" gelöscht werden kann (dh es wird nicht in anderen Tabellen referenziert).

Es ist nicht wirklich die Verantwortung der Datenbank festzustellen, ob der Kunde gelöscht werden kann. Es ist eher Teil Ihrer Geschäftslogik.

Sie möchten die referentielle Integrität in der Datenbank überprüfen.

Es ist ok in nicht OOP Welt. Aber wenn Sie mit Objekten arbeiten (wie Sie), fügen Sie besser die Logik zu Ihren Objekten hinzu (Objekte haben Zustand und Verhalten; DB - nur der Zustand).

Also würde ich eine Methode hinzufügen, um die Customer-Klasse zu bestimmen, ob es gelöscht werden kann oder nicht. Auf diese Weise können Sie die Funktionalität ordnungsgemäß (Einheit) testen.

Zum Beispiel sagen wir, wir haben eine Regel Der Kunde kann nur gelöscht werden, wenn er keine Bestellungen hat und nicht an Forum teilgenommen hat.

Dann werden Sie Customer-Objekt ähnlich wie diese (einfachster möglicher Fall) haben:

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

Das ist sehr sauber und einfaches Design, das einfach zu bedienen ist, zu testen und stark beruht nicht auf NHibernate oder die zugrunde liegende Datenbank .

Sie können es wie folgt verwenden:

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

Zusätzlich zu, dass man-Feinabstimmung NHibernate Ähnliche Aufträge und andere Verbände zu löschen, falls erforderlich.


Die Anmerkung: natürlich das obige Beispiel nur eine möglichst einfache illustrative Lösung. Möglicherweise möchten Sie eine solche Regel part of the validation machen, die beim Löschen des Objekts erzwungen werden soll.

+1

Sie tun (mindestens) 2 zusätzliche wählt durch den Aufruf der Eigenschaft [Sammlung] .Count. Zusätzlich wird ein schlecht geformter Komponententest (oder einer, der nicht existiert) eine Bedingung bestehen. Dies kann durch Anwendung von Datenbankeinschränkungen gespeichert werden! Es ist immer eine gute Praxis, dass das Backend nicht auf das Frontend für Geschäftslogik Datenintegrität – Jaguar

+0

Sie können die SQL im Mapping mit Hilfe eifriger Abruf oder andere Weise Feinabstimmung, Der Zweck war, die Idee zu demonstrieren. Was "Das kann durch die Anwendung von Datenbankbeschränkungen erreicht werden" - Ich erwähnte, dass dies auch Teil der Validierung sein sollte. –

+0

Ich würde argumentieren, dass Überprüfung der referentiellen Integrität der Datenbank ist ** NICHT ** Teil der Geschäftslogik - ich denke, das ist nur der Fall, wenn Ihr Programm ein Datenbankprogramm ist. – fostandy