My Existing class structure.
AbstractTradeBaseClass – > AbstractTradeClass -> ConcreteTradeClass.
Am using DataAnnotations and IDataErrorInfo to validate my Model in my WPF application.
I have moved the IDataErrorInfo methods to the AbstractTradeBaseClass so that i can be used by all the classes that inherit from the base class.
This is done by reading the attributes dynamically using Linq.
AbstractTradeBaseClass.cs
public abstract class TradeBaseModel<T> : INotifyPropertyChanged, IDataErrorInfo
{
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged( string propertyName )
{
if ( PropertyChanged != null )
{
PropertyChanged( this, new PropertyChangedEventArgs( propertyName ) );
}
}
#endregion
# region Getter/Validator members for Annotations
private static readonly Dictionary<string, Func<T, object>>
propertyGetters = typeof( T ).GetProperties()
.Where( p => GetValidations( p ).Length != 0 )
.ToDictionary( p => p.Name, p => GetValueGetter( p ) );
private static readonly Dictionary<string, ValidationAttribute[]> validators =
typeof( T ).GetProperties()
.Where( p => GetValidations( p ).Length != 0 )
.ToDictionary( p => p.Name, p => GetValidations( p ) );
private static ValidationAttribute[] GetValidations( PropertyInfo property )
{
return ( ValidationAttribute[] )property.GetCustomAttributes( typeof( ValidationAttribute ), true );
}
private static Func<T, object> GetValueGetter( PropertyInfo property )
{
var instance = Expression.Parameter( typeof( T ), "i" );
var cast = Expression.TypeAs( Expression.Property( instance, property ), typeof( object ) );
return ( Func<T, object> )Expression.Lambda( cast, instance ).Compile();
}
# endregion
#region IDataErrorInfo Members
public string Error
{
get
{
var errors = from i in validators
from v in i.Value
where !v.IsValid( propertyGetters[i.Key]( **( T )ModelToValidate()** ) )
select v.ErrorMessage;
return string.Join( Environment.NewLine, errors.ToArray() );
}
}
protected Dictionary<string, string> _errors = new Dictionary<string, string>();
public IDictionary<string, string> Errors
{
get { return _errors; }
}
public string this[string columnName]
{
get
{
string errorMessage = string.Empty;
this.Errors.Remove( columnName );
//string errorMessage = string.Empty;
//switch(columnName)
//{
// case "Range":
// if ( Range > 15 )
// {
// errorMessage = "Out of Range, should be less than 15";
// }
// break;
//}
//return errorMessage;
if ( propertyGetters.ContainsKey( columnName ) )
{
var value = propertyGetters[columnName]( **( T )ModelToValidate()** );
var errors = validators[columnName].Where( v => !v.IsValid( value ) )
.Select( v => v.ErrorMessage ).ToArray();
string error = string.Join( Environment.NewLine, errors );
this.OnPropertyChanged( "Error" );
if ( !string.IsNullOrEmpty( error ) )
{
this.Errors.Add( columnName, error );
}
return error;
}
return string.Empty;
}
}
public abstract object ModelToValidate(); -- ***Gets the child object here, overridden in the child class to return the child object, -- is there a better way to do this using Generics ??***
#endregion
}
AbstractTradeClass
public abstract class TradeClassBase<T> : TradeBaseModel<T>
{
public string EmptyString
{
get { return string.Empty; }
}
}
ConcreteTradeClass
public class CashFlowTrade : TradeClassBase<CashFlowTrade>
{
private int range;
[RangeAttribute(0,10, ErrorMessage="Out of Range, Range should be less than 15")]
public int Range
{
get
{
return this.range;
}
set
{
if ( this.range != value )
{
this.range = value;
this.OnPropertyChanged( "Range" );
}
}
}
public override object ModelToValidate()
{
return this;
}
}
Is there a better way of doing this instead of using an abstract method and overriding it in the child class to pass the actual child class object and casting it to type T.
Usage indicated in above code in BOLD.
If there is a way I can pass the child class object down to the base class, i can then use the actual child class object itself to perform the validation operations.
Updated Working Code
Constraint T with class and use the type casting operator as below.
public abstract class TradeBaseModel<T> : INotifyPropertyChanged, IDataErrorInfo
where T : class
replace call to the abstract method with the type cast.
if ( propertyGetters.ContainsKey( columnName ) )
{
var value = propertyGetters[columnName]( this as T );
yes… directly use this. Even in an abstract class you have access to this reference. The only place where you can’t use it is in static methods.
EDIT:
To enforce type checking, you could add a constraint on T :
EDIT 2:
To sum-up Kans’ comments below, this is not enough : this conversion enables an implicit type conversion from T to base type while a conversion from base type to T is required.
The only solution seems to be a cast into using T the as operator in the above code, and for this, T has to be a class (which is OK if constraint above is added).