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