Laden...

[abstract class] Validation auch in abgeleiteten Klassen

Erstellt von IamTheBug vor 8 Jahren Letzter Beitrag vor 8 Jahren 1.611 Views
I
IamTheBug Themenstarter:in
401 Beiträge seit 2006
vor 8 Jahren
[abstract class] Validation auch in abgeleiteten Klassen

Hallo,

ich stehe im Moment ein wenig auf dem Schlauch. Ich denke das Problem ist eigentlich sehr gering ich weiß nur momentat nicht wie ich es angehen soll.

Ich habe eine abstrakte Klasse.


 public abstract class ObjectWithName
        {
            public virtuall string Name
            {
                get;
                set
                {
                    if (value == "Max Mustermann") throw new Exception();
                }
            }

Jetzt gibt es Klassen die davon ableiten:

z.b.
public class Person : ObjectWithName
public class Car : ObjectWithName

Das Ziel ist das alle Klassen die davon ableiten, nie den Namen "Max Mustermann" erhalten dürfen.
Ist das vorgehen so korrekt?
Was passiert, wenn eine Subklasse aber das property Name überschreibt? Dann hebelt er doch diese Validierung aus oder? Wie kann ich es umsetzen das auch in den Subklassen diese Validierung durch die Basisklasse erzwungen wird.

Viele Grüße

Mfg

IamTheBug

W
872 Beiträge seit 2005
vor 8 Jahren

Dafür gibt es das Schlüsselwort sealed.

1.040 Beiträge seit 2007
vor 8 Jahren

Alternativ einfach das Schlüsselwort virtual weglassen.

Falls es notwendig ist, die Property Name zu überschreiben, ist die Prüfung nur sichergestellt, wenn base.Name aufgerufen wird.

I
IamTheBug Themenstarter:in
401 Beiträge seit 2006
vor 8 Jahren

Ich möchte es aber nicht als sealed haben.
Was ist, wenn eine Subklasse das Property überschreibt? Einfach, weile es noch eine weitere Überprüfung hinzufügt. Oder einfach nur, weil es im Setter noch etwas anderes machen möchte?

Alternativ einfach das Schlüsselwort virtual weglassen.

Falls es notwendig ist, die Property Name zu überschreiben, ist die Prüfung nur sichergestellt, wenn base.Name aufgerufen wird.

Ok. Also entweder den anderen meine Implementierung aufzwingen oder damit leben, wenn sie es überschreiben selber noch base aufzurufen?

Mfg

IamTheBug

F
10.010 Beiträge seit 2004
vor 8 Jahren

Ich persönlich finde es ist der komplett falsche Ansatz.

Exceptions in Properties ist ziemlich schlecht, da fast alles beim DataBinding Exceptions schluckt.
Du wunderst dich warum es nicht geht und übersiehst die ganzen Exceptions.

IDataErrorInfo ist extra dafür da fehler nach aussen zu melden und wird sowohl von Windowsforms als auch von WPF unterstützt.

Wenn man sich schon Gedanken über Businessrules macht, sollte man das auch richtig machen.
Dann benutzt man eine BusinessRules Engine die das Validieren übernimmt und macht nicht so halbe Sachen.

W
196 Beiträge seit 2008
vor 8 Jahren

Ich persönlich finde es ist der komplett falsche Ansatz....

Naja, das ganze ist ja sicher nur ein Beispiel und man muss nicht immer mit Kanonen auf die kleinen Spatzen schießen. Ich finde nichts verwerfliches an einer Prüfung von Werten im setter - was die Exception angeht sind wir aber d'accord, das ist sicher keine sehr geschickte Lösung...

@IamTheBug:
Zuerst einmal gibt es keinen sealed setter. Einen setter kann man nur als sealed markieren, wenn man gleichzeitig override verwendet. Was aber gänge, wäre soetwas:


    public abstract class BaseClass
    {
        public virtual string Name { get; set; }
    }
    
    public abstract class ObjectWithName : BaseClass
    {
        private string _name;

        public sealed override string Name
        {
            get { return _name; }
            set
            {
                if (value == "Max Mustermann") throw new Exception();
                    _name = value;
            }
        }
    }

Ok. Also entweder den anderen meine Implementierung aufzwingen oder damit leben, wenn sie es überschreiben selber noch base aufzurufen?

Vielleicht solltest Du Dich mal mit Interfaces und dem Facade-Pattern auseinandersetzen...

I
IamTheBug Themenstarter:in
401 Beiträge seit 2006
vor 8 Jahren

Hallo,

danke für die bisherigen Antworten.

Eventuell sollte ich nochmal genauer erkläre was ich mir vorgestellt habe.

Ich wollte ein Programm schreiben mit dem (ich halte es jetzt mal sehr abstrakt) Objekte erstellt werden können. Diese besitzen immer einen Namen und jetzt eben noch verschiedene Eigenschaften.

Dabei gibt es bestimmte vorgaben für die Objekte. z.B. muss ein Name immer mit einem "A" anfangen.
Jetzt wollte ich eine Core dll schreiben in dem die Funktionalität der Ojekte untereinander abgebildet werd. Dort wollte ich dann z.B. im Setter des Namens eine Exception werfen.

Jetzt sieht man natürlich noch nichts.
Es soll zwei Varianten des Programms geben mit verschiedenen Oberflächen usw. Aber die Regeln in der Core Komponente bleiben ja gleich.
Deshalb hätte ich dann in XAML z.B. eine Oberfläche programmiert und ein Viewmodel dazu. So geschichten wie IDataErrorInfo oder andere Validierungswäre hätte ich dann im XAML/ViewModel abgefackelt.
Somit soll an dieser Stelle eben der Fehler abgefangen werden.

Aber jeder der die Core Komponente erhält und seine eigene Oberfläche drumherum schustert soll immer eine Exception um die Ohren geflogen bekommen sobald ein Name nicht mit einem "A" anfängt.
Denn jeman der die Core Komponente nicht direkt kennt und die Doku nur halbherzig gelesen hat soll die Konsequenzen sofort spüren 😄

Mfg

IamTheBug

16.835 Beiträge seit 2008
vor 8 Jahren

Ich würde das über DataAnnotations lösen und einer CustomRule.
Das ist dann wiederverwendbar und einfach zu pflegen.

public class MyClassA
{

   [RequiresStartWith("A")]
   public String Name {get;set;}
}

Exceptions in Properties - egal wie die Rechtfertigung aussieht - ist schlechtes Design.

Denn jeman der die Core Komponente nicht direkt kennt und die Doku nur halbherzig gelesen hat soll die Konsequenzen sofort spüren 😄

Auch sowas empfinde ich als schlechtes Design.

I
IamTheBug Themenstarter:in
401 Beiträge seit 2006
vor 8 Jahren

Ich denke das mit den DataAnnotations ist ein sehr guter Hinweis.
Das hatte ich so nicht auf dem Schirm.
Ich habe dazu auch ein passendes Video gefunden, das eigentlich das abfackelt was ich vorhabe.
Danke für den Tip.

Hier das Video:
C# DataAnnotations explained

Allerdings stört mich noch eine Sache daran. Jemand der die Core komponente verwendet muss selber von Hand die Überprüfung durchführen. Sprich es ist trotzdem möglich einen Namen der nicht mit "A" beginnt zu vergeben oder? Sprich man muss den Namen vergeben und anschließend immer prüfen ob dies mgl. ist oder?

Wie bekommt man eigentlich mit, welche Merkmale durch DataAnnotations eingeschränkt wurden, wenn man die Quelldaten nicht hat?

Mfg

IamTheBug

16.835 Beiträge seit 2008
vor 8 Jahren

Du implementierst zB. noch IValidatable und stellst eine IsValid-Methode zur Verfügung.
Im Prinzip willst Du auch nichts anderes als eine Validierung. Was Dir nur fehlt ist das korrekte Mitteilen von Fehlern.
Über Exceptions macht man das halt nicht; dafür gibt es eben die Annotations oder wie Fzelle gesagt hat IDataErrorInfo.

Beispiel aus meinem Code

    public abstract class ValidatableEntity : Entity, IValidatableEntity
    {
        public bool IsValid()
        {
            IEnumerable<ValidationResult> errors;
            return IsValid( out errors );
        }

        public bool IsValid( out IEnumerable<ValidationResult> errors )
        {
            errors = Validate();
            return !errors.Any();
        }

        public IEnumerable<ValidationResult> Validate()
        {
            try
            {
               return Validate( new ValidationContext( this, null, null ) );
            }
            catch( Exception e )
            {
                // Validation failed, handle smth here
                return new List<ValidationResult>();
            }
        }
        
        public IEnumerable<ValidationResult> Validate( ValidationContext validationContext )
        {
            List<ValidationResult> results = new List<ValidationResult>();
            Validator.TryValidateObject( this, validationContext, results, true );
            return results;
        }
    }
    
    public class User : ValidatableEntity
    {
        [Email]
        public String EMail {get;set;}
    }
I
IamTheBug Themenstarter:in
401 Beiträge seit 2006
vor 8 Jahren

Hm, ok.

Dann werde ich diesen Weg einschlagen und versuchen umzusetzen.

Ich habe noch eine Frage zu deinem Beispiel.
Leitet IValidatableEntity von IValidatable ab.
Was ist der Unterschied zwischen IValidatable und IValidatableObject. Das habe ich auch noch gefunden.

Wo kommt die IsValid Methode her? Auch aus einem Standard-Interface oder wurde es von dir "eingeführt"?

Danke und viele Grüße

Mfg

IamTheBug

16.835 Beiträge seit 2008
vor 8 Jahren

Ich meine IValidatableObject.
Nimm das.

Hab heute mit IValidatable arbeiten müssen; daher kam die Verwechslung.

IsValid siehste ja, dass die Implementierung von mir kommt.
Sonst wäre hier ein override zu sehen.

I
IamTheBug Themenstarter:in
401 Beiträge seit 2006
vor 8 Jahren

Hi,

ich habe mich mal daran gemacht und die ersten Schritte unternommen.
Ich habe noch eine Frage, eventuell hast du einen einen Tipp.

Ich wollte in meinem Programm eine Art Fenster "Fehlerliste" erstellen. Ähndlich der Fehlerliste im Visual Studio.
Dort sollen immer alle Fehler aller Objekt sofort aufgesammelt und angezeigt werden.
Jetzt stellt sich mir die Frage, wenn ich z.B. IValidatableObject ode etwas ähnliches eigenes Implementiere, wie bekommt die GUI (WPF) mit das sich der validierte Zustand eventuell geändert hat.
Sollte ich an der Stelle selber ein event einführen, welches darüber benachrichtigt?

Wie macht das IDataErrorInfo? Wann ruft dieses die Fehlermeldungen wieder ab und aktualisiert sich. Ist das an INotifyPropertyChanged gekoppelt?

Mfg

IamTheBug