|
-
Sep 13th, 2015, 04:41 AM
#1
Thread Starter
Frenzied Member
Need help with generics and base classes
Hi!
I am trying to apply some refactoring to a viewmodel class, but I am fairly stuck. We had validation logic in every view model, and the code looked very repetitive, so I figured I should put int in our ViewModelBase class. But it just won't work correctly.
I have two problems:
1) Should I really need a generic base class where T is the same type as the inheriting class? Feels like it is the same thing...
2) In the base class I need to use an extension method that only seems to be available in a derived class. We are using FluentValidator library.
Here is the code:
Code:
Public Class MainViewModel
Inherits ViewModelBase(Of MainViewModel)
Implements IDataErrorInfo
Private _price As System.Nullable(Of Decimal)
Public Property Price() As System.Nullable(Of Decimal)
Get
Return _price
End Get
Set
SetProperty(_price, value)
Debug.WriteLine("Price set to:{0}", value)
End Set
End Property
Public _name As String
Public Property Name() As String
Get
Return _name
End Get
Set
SetProperty(_name, value)
End Set
End Property
Public Sub New(validator As AbstractValidator(Of MainViewModel))
MyBase.New(validator)
End Sub
End Class
Public MustInherit Class ViewModelBase(Of Tv)
Implements INotifyPropertyChanged
Public Event PropertyChanged As PropertyChangedEventHandler
Public Property Validator() As AbstractValidator(Of Tv)
Get
Return m_Validator
End Get
Set
m_Validator = Value
End Set
End Property
Private m_Validator As AbstractValidator(Of Tv)
Private _errors As Dictionary(Of String, String)
Public Sub New(validator__1 As AbstractValidator(Of Tv))
Validator = validator__1
_errors = New Dictionary(Of String, String)()
End Sub
Protected Sub SetProperty(Of T)(ByRef field As T, value As T, <CallerMemberName> Optional name As String = "")
If Not EqualityComparer(Of T).[Default].Equals(field, value) Then
field = value
Dim handler = PropertyChanged
RaiseEvent handler(Me, New PropertyChangedEventArgs(name))
End If
End Sub
Public Default ReadOnly Property Item(propertyName As String) As String
Get
Dim errors As String = String.Empty
If Validator IsNot Nothing Then
_errors.Remove(propertyName)
Dim results = Validator.Validate(Me, propertyName)
If results IsNot Nothing AndAlso results.Errors.Count > 0 Then
errors = String.Join(Environment.NewLine, results.Errors.[Select](Function(x) x.ErrorMessage).ToArray())
_errors.Add(propertyName, errors)
End If
End If
[Error] = [String].Join(Environment.NewLine, _errors.[Select](Function(x) x.Value).ToArray())
Return errors
End Get
End Property
Private _error As String
Public Property [Error]() As String
Get
Return _error
End Get
Set
SetProperty(_error, value)
End Set
End Property
End Class
There is an implementation of MainViewModelValidator where the validation logic is contained. That should be injected to MainViewModel through constructor and passed down to the base class. The real issue I think is the declaration of the property that holds the Validator. From what I understand, this has to be a generic with a specific type, hence I need to pass down the type T of the MainViewModel, so I can declare a property that holds my validator.
The next problem is the Validator.Validate line which won't compile because this particular signature of Validate with 2 parameters is an extensionmethod and not available in the AbstractValidator(of T) base class. Should I do some kind of cast here or what?
It feels like I am on the right track here, but this is my first attempt with generics and I feel kind of lost.
please help me out, it would save us tons of code since we have like 20 viewmodels with the same validator code...
cheers
Henrik
-
Sep 13th, 2015, 09:23 AM
#2
Thread Starter
Frenzied Member
Re: Need help with generics and base classes
Hi again!
As I noted before, the compiler doesn't like the call to validate, which is an extension method definied in FluentValidation namespace.
As far as I can see, the real issue here is that I am trying to call this extension method from a class that is generic. If I put all this code directly in the MainViewModel without the generics, it works just fine. So I guess my question boils down to this:
How can I call Validate from a generic base class? The extension method itself is declared ina static non generic class within the FluentValidation namespace. Do I have to write some kind of wrapper or is there some other coding magic I can use?
I am quite clueless on this one to be honest. The strange thing is alos that intellisense suggests there is a method with 2 parameters, but refuse to accept my call to it. Maybe a bug?
/H
-
Sep 13th, 2015, 09:39 AM
#3
Re: Need help with generics and base classes
I think the generics is (are) a mistake. This looks like a simple parent-child relationship where the base class must be overriden by a child class. For example, the validator seems like a place where polymorphism applies. It looks like your AbstractValidator method should be nothing more than a private member of the derived class. If the base class has a MustOverride AbstractValidator function then the derived class will HAVE to have such a method. The Validator method found in the base class would then call whichever AbstractValidator the child class implemented, depending on what the real type is. This assumes that you'd create a class of type MainViewModel and put it in a variable of type ViewModelBase.
My usual boring signature: Nothing
 
-
Sep 13th, 2015, 09:59 AM
#4
Thread Starter
Frenzied Member
Re: Need help with generics and base classes
Hi!
I am not sure I understand.
AbstractValidator(of T) is a class defined in the nuget package FluentValidation and is nothing I can change. The problem I am facing is that the Validate extension method only seem to work in the exact same class as it is defined in.
For example, if I declare
dim validator as AbstractValidator(of ClassA)
I can only call
validator.Validate(me, "property")
in ClassA.
If I try to do the same from ClassB, the extension method will fail.
If I in my example declare the validator as
public property Validator as AbstractValidator(of ViewModelBase(of Tv))
everything is happy at this end, but I will get problems when I try to assign my AbstractValidator(of MainViewModel) to this property.
I hope I have made this a little more clear.
My main objective is to have the validation logic from FluentAssertions in a base viewmodel, and not the exact same code (from IErrorDataInfo) in every viewmodel.
cheers
H
-
Sep 15th, 2015, 05:39 AM
#5
Re: Need help with generics and base classes
From reading the code, I would say your problem is that when you call Validator.Validate() in ViewModelBase(Of Tv), you are passing it Me as a parameter. Me is of type ViewModelBase(Of Tv), but Validator is an AbstractValidator(Of Tv), so the fist parameter of the Validate method is almost certainly requiring a Tv, and ViewModelBase(Of Tv) is not convertible to that.
Frankly, I'd say inheritance and generics are a poor fit for this problem, but if you want to continue with this approach, I would stop calling Validator.Validate from ViewModelBase(Of Tv), instead call a MustOverride method for validating that the derived classes can implement by simply calling Validator.Validate(Me, whatever), because in MyDerivedClass Validator is typed as AbstractValidator(Of MyDerivedClass) and therefore Me works just fine as an argument as far as the compiler is concerned. I think this is almost what SH suggested.
Posting Permissions
- You may not post new threads
- You may not post replies
- You may not post attachments
- You may not edit your posts
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|