Ich habe ein Geschäftsmodell mit vielen Klassen in, einige logische Entitäten in diesem Modell bestehen aus vielen verschiedenen Klassen (Eltern-Kind-Enkelkind). Auf diesen verschiedenen Klassen definiere ich Constraints, die invariant sind, zum Beispiel das Wurzel des Composite sollte einen Wert für Code haben.
Ich habe derzeit jede Klasse implementieren eine Schnittstelle wie folgt ...
%Vor%Das übergeordnete Element würde einen Validierungsfehler hinzufügen, wenn Code nicht festgelegt ist, und dann GetErrors für jedes untergeordnete Element ausführen, was wiederum GetErrors für jedes Enkelkind aufrufen würde.
Jetzt muss ich verschiedene Einschränkungen für verschiedene Operationen validieren, zum Beispiel
Ich habe darüber nachgedacht, der GetErrors-Methode einen "Reason" -Parameter hinzuzufügen, aber aus einem Grund, den ich nicht genau sagen kann, fühle ich mich nicht richtig. Ich habe auch darüber nachgedacht, einen Besucher zu erstellen und eine konkrete Implementierung für OperationX und eine weitere für OperationY zu validieren, aber ich mag das nicht, da einige der Constraint-Prüfungen für mehrere Operationen erforderlich sind, aber nicht für alle (z. B. ein Datum für OperationX + OperationY) aber nicht OperationZ) und ich möchte den Code, der prüft, nicht kopieren müssen.
Irgendwelche Vorschläge würden geschätzt.
Sie haben hier ein Isolationsproblem, da Ihre Klassen selbst für die Validierung zuständig sind. Die Art dieser Validierung hängt jedoch von der Art der Operation ab, die Sie durchführen. Dies bedeutet, dass die Klassen über die Arten von Operationen Bescheid wissen müssen, die an ihnen ausgeführt werden können, was eine ziemlich enge Kopplung zwischen den Klassen und den Operationen, die sie verwenden, erzeugt.
Ein möglicher Entwurf besteht darin, eine parallele Gruppe von Klassen wie folgt zu erstellen:
%Vor%Sie müssen noch klären, wie Sie die Validierungsklassen den verschiedenen zu validierenden Klassen zuordnen möchten. Es hört sich so an, als sollte dies auf der Ebene der Operationen geschehen, da Sie wissen, welche Art von Validierung durchgeführt werden muss. Ohne ein besseres Verständnis Ihrer Architektur ist das schwierig zu sagen.
Ich würde eine Art attributbasierte Validierung durchführen:
%Vor% Jede Eigenschaft, die an das Requires
-Attribut übergeben wird, muss für die Ausführung der Operation gültig sein.
Die Eigenschaften, die das ValidateAlways
-Attribut haben, müssen immer gültig sein - egal welche Operation.
In meinem Pseudocode Property1
muss Property2
und SomeOtherProperty
gültig sein, um OperationX
auszuführen.
Natürlich müssen Sie dem Requires-Attribut eine Option hinzufügen, um die Validierungsattribute auch für ein Kind zu überprüfen. Aber ich kann nicht vorschlagen, wie man das macht, ohne einen Beispielcode zu sehen.
Vielleicht so etwas:
%Vor%Bei Bedarf können Sie auch stark typisierte Eigenschaftszeiger mit Lambda-Ausdrücken anstelle von Strings erreichen ( Beispiel ).
Ich würde vorschlagen, die Bibliothek Fluent Validation For .Net zu verwenden. Mit dieser Bibliothek können Sie Validierer ziemlich einfach und flexibel einrichten, und wenn Sie verschiedene Validierungen für verschiedene Operationen benötigen, können Sie die für diese bestimmte Operation sehr einfach verwenden (und sie ändern).
Ich habe die Validierungs-Engine von Sptring.NET genau aus dem gleichen Grund verwendet - Sie können Conditional Validators verwenden. Sie definieren nur Regeln - welche Validierung anzuwenden und unter welchen Bedingungen und Spring erledigt den Rest. Das Gute daran ist, dass Ihre Geschäftslogik nicht länger durch Schnittstellen für die Validierung belastet wird.
Weitere Informationen finden Sie in der Dokumentation auf springframework.net , die ich einfach kopieren werde das Beispiel für ihr Dokument, um zu zeigen, wie es aussieht:
<v:condition test="StartingFrom.Date >= DateTime.Today" when="StartingFrom.Date != DateTime.MinValue">
<v:message id="error.departureDate.inThePast" providers="departureDateErrors, validationSummary"/>
</v:condition>
In diesem Beispiel wird die StartingFrom-Eigenschaft des Trip-Objekts verglichen, um festzustellen, ob sie später als das aktuelle Datum ist, dh DateTime, aber nur wenn das Datum gesetzt wurde (der Startwert von StartingFrom.Date wurde auf DateTime.MinValue gesetzt) ).
Der Zustandsvalidator könnte als "die Mutter aller Validatoren" betrachtet werden. Sie können damit fast alles erreichen, was mit anderen Validator-Typen erreicht werden kann. In einigen Fällen ist der Testausdruck jedoch möglicherweise sehr komplex, weshalb Sie nach Möglichkeit einen spezifischeren Validator-Typ verwenden sollten. Der Condition Validator ist jedoch immer noch die beste Option, wenn Sie überprüfen müssen, ob ein bestimmter Wert zu einem bestimmten Bereich gehört, oder einen ähnlichen Test durchführen, da diese Bedingungen relativ einfach zu schreiben sind.
Wenn Sie .Net 4.0 verwenden, können Sie Code Contracts verwenden, um einige davon zu kontrollieren .
Versuchen Sie, diesen Artikel von oben nach unten zu lesen, ich habe daraus einige Ideen gewonnen.
Es ist eine attributbasierte Domänenvalidierung mit einer Benachrichtigung, die diese Validierungen bis zu höheren Ebenen umschließt.
Ich würde die Variantenvalidierungslogik trennen, vielleicht mit einem Besucher, wie Sie erwähnt haben. Indem Sie die Validierung von den Klassen trennen, werden Sie Ihre Operationen von Ihren Daten getrennt halten, und das kann wirklich helfen, die Dinge sauber zu halten.
Denken Sie auch so darüber nach - wenn Sie Ihre Validierungen und Operationen mit Ihren Datenklassen mischen, denken Sie darüber nach, wie die Dinge in einem Jahr aussehen werden, wenn Sie eine Erweiterung durchführen und hinzufügen müssen eine neue Operation. Wenn die Validierungsregeln und die Operationslogik einer Operation voneinander getrennt sind, handelt es sich im Wesentlichen nur um ein "Hinzufügen" - Sie erstellen eine neue Operation und einen neuen Validierungs-Besucher, um damit zu arbeiten. Auf der anderen Seite, wenn Sie zurückgehen und viel "if operation == x" Logik in jeder Ihrer Datenklassen berühren müssen, dann haben Sie ein zusätzliches Risiko für Regression Bugs / etc.
Ich verlasse mich auf bewährte Validierungsstrategien, die im .net-Framework in System.ComponentModel.DataAnnotations-Namespace " und zum Beispiel in ASP.NET MVC verwendet.
Dieser Modus bietet eine unauffällige Möglichkeit, Validierungsregeln (mithilfe von Attributen oder Implementieren von IValidatableObject) und Funktionen zum Validieren von Regeln anzuwenden.
Scott Allen beschrieb diesen Weg im großartigen Artikel Manuelle Validierung mit Datenannotationen ".
Tags und Links c# design-patterns