How to create a base FluentValidation - c#

namespace Test
{
public class A
{
public string Name { get; set; }
}
public class AValidator : AbstractValidator<A>
{
public AValidator()
{
RuleFor(t => t.Name)
.NotEmpty()
.MinimumLength(10)
.MaximumLength(20);
}
}
public class B
{
public string Name { get; set; }
}
public class BValidator : AbstractValidator<B>
{
public BValidator()
{
RuleFor(t => t.Name)
.NotEmpty()
.MinimumLength(10)
.MaximumLength(20);
}
}
}
tried to create a common like this:
namespace Test2
{
public interface IPerson
{
public string Name { get; set; }
}
public abstract class CommonABValidators<T> :
AbstractValidator<T> where T : IPerson
{
protected CommonABValidators()
{
RuleFor(x => x.Name).NotNull();
}
}
}
but by calling
public class AValidator : CommonABValidators<A>
It should be convertible to IPerson, but in my case I have diffeent props in A that are not convertible to IPerson
Any Idea how to extract common params into common Validator?

What you've done looks correct. Just make sure that your class A implements the IPerson interface and the validator will start working.

Another option that you can try at least is to Include rules. Here you can find the official documentation: https://docs.fluentvalidation.net/en/latest/including-rules.html
All you need is to call Include(new CommonValidator()); and the common rules will be automatically included without inheriting a common base type.

Related

Is it possible to constrain a generic parameter type to this?

Short version
How do I force the BaseClass's TModel generic parameter to be of the same type as the class that derives from it?
public class BaseClass<TModel, TValidator> where TValidator : IValidator<TModel> { }
public class Person : BaseClass<Person, PersonValidator> { }
In this example, how do I force the BaseClass's TModel to be of type Person and not something else?
This is invalid syntax, but it's what I'm imagining:
public class BaseClass<TValidator> where TValidator : IValidator<this> { }
public class Person : BaseClass<PersonValidator> { }
Is this somehow possible or should I use a totally different solution to achieve this?
Long version
I'm trying to extract some validation logic into a base class, but I don't know how to constraint the generic types so the resulting base class is fully fool-proof.
Here's an example of what all the validation logic looks like without a base class. I'm using FluentValidation to validate the object and I'm exposing that validation result via the IDataErrorInfo interface so it can be used by the WPF UI.
Original solution
public class User : IDataErrorInfo
{
private readonly IValidator<Person> _validator = new();
public string Username { get; set; }
public string Password { get; set; }
private string ValidateAndGetErrorForProperty(string propertyName)
{
var result = _validator.Validate(this);
if (result.IsValid)
{
return string.Empty;
}
return result.Errors.FirstOrDefault(a => a.PropertyName == propertyName)?.ErrorMessage ?? string.Empty;
}
//IDataErrorInfo implementation
public string Error => string.Empty;
public string this[string columnName] => ValidateAndGetErrorForProperty(columnName);
}
public class UserValidator : AbstractValidator<User>
{
public UserValidator()
{
RuleFor(a => a.Username)
.EmailAddress();
RuleFor(a => a.Password)
.MinimumLength(12);
}
}
Validation implementation separated into a base class
I'd like to separate the validation logic and IDataErrorInfo implementation into a base class so this boilerplate doesn't have to be repeated in every model class. Here's what I have.
public abstract class ValidationBase<TModel, TValidator> : IDataErrorInfo where TValidator : IValidator<TModel>, new()
{
private readonly TValidator _validator;
public ValidationBase()
{
_validator = Activator.CreateInstance<TValidator>();
}
private string ValidateAndGetErrorForProperty(string propertyName)
{
//I have to check if this is of type TModel since the TModel isn't constraint to this
if (this is not TModel model)
{
throw new InvalidOperationException($"Instance is not of the supported type: {typeof(TModel)}. Type of {GetType()} found instead");
}
var result = _validator.Validate(model);
if (result.IsValid)
{
return string.Empty;
}
return result.Errors.FirstOrDefault(a => a.PropertyName == propertyName)?.ErrorMessage ?? string.Empty;
}
//IDataErrorInfo implementation
public string Error => string.Empty;
public string this[string columnName] => ValidateAndGetErrorForProperty(columnName);
}
And here's how I'm using it:
public class User : ValidationBase<User, UserValidator>
{
public string Username { get; set; }
public string Password { get; set; }
}
The problem
The problem I have with this solution is that you can write this invalid code:
public class InvalidClass : ValidationBase<User, UserValidator>
{
}
Is this what you are looking for?
public interface IValidator<TModel>
{
}
public class BaseClass<TModel, TValidator>
where TModel : BaseClass<TModel, TValidator>
where TValidator
: IValidator<TModel> { }
// Only classes derived from BaseClass can be instantiated
public class Person
: BaseClass<Person, PersonValidator> { }
public class PersonValidator
: IValidator<Person>
{
}
This is a classic pattern where a generic parameter is constrained to the derived class.

Using Collection<Interface> is possible, but does it correct?

The best practices are to program against the interface rather than the concrete class. I want to keep container of class that implements interface IPet. Does it correct? List<IPet> petList = new List<IPet>(); Or it's better to create an abstract class?
public interface IPet
{
string Name { get; set; }
void Introduce();
}
public class Parrot : IPet
{
public string Name { get; set; }
public Parrot(string name)
{
Name = name;
}
public void Introduce()
{
Console.WriteLine($"My name is {Name}. I am a parrot");
}
}
public class Cat : IPet
{
public string Name { get; set; }
public Cat(string name)
{
Name = name;
}
public void Introduce()
{
Console.WriteLine($"My name is {Name}. I am a cat");
}
}
PetShop petShop = new PetShop();
petShop.Add(new Cat("Garfield"));
petShop.Add(new Parrot("Kesha"));
Using interface in generics is the good choice!
Using abstract class force you to place any type in single chain of inheritance that can cause a problem with application evolution.
Besides, if you have a repeating behavoir you can create abstract class wich implements required interface, so you'll get advantages of both ways.
You can easily create an abstract class and put all repeating logic into it. Your classes look the same, only the Introduce() method is different, but you can use this.GetType().Name.ToLower() instead of "cat" and "parrot".
So, you can have the following:
public abstract class Pet : IPet
{
public string Name { get; set; }
protected Pet(string name)
{
Name = name;
}
public void Introduce()
{
Console.WriteLine($"My name is {Name}. I am a {this.GetType().Name.ToLower()}");
}
}
public class Cat : Pet
{
public Cat(string name)
: base(name)
{
}
}

Inherit from generic type as interface

I am attempting to do something similar to:
public interface IView<T> : T where T : class
{
T SomeParam {get;}
}
So that i can later do
public class SomeView : IView<ISomeView>
{
}
Is it possible to specify inheritance using generics in this way or do i have to go the long way round and explicitly specify both interfaces when defining the class and do:
public interface IView<T>
{
T SomeParam {get;}
}
public class SomeView : IView<ISomeView>, ISomeView
{
}
This isn't possible, but your goal may be achievable with conversion operators. It seems that what you're trying to do is make it possible to pass an IView<T> as the T object which it contains. You could write a base class like this:
public abstract class ViewBase<T> {
public abstract T SomeParam { get; }
public static implicit operator T(ViewBase<T> view) {
return view.SomeParam;
}
}
Then, if you define a class like:
public class SomeView : ViewBase<ISomeView> { }
It can be accepted anywhere an ISomeView is expected:
ISomeView view = new SomeView();
Short answer: It is not possible. See this post
An Interface can't derive from a class. However nothing prevent you from doing this:
public interface ISomeView
{
}
public interface IView<out T> where T:class
{
T SomeParam { get; }
}
public class SomeView:IView<ISomeView>
{
public ISomeView SomeParam { get; set; }
}
Edit:
If you don't want to implement the T SomeParam { get; } each time you need to have an implementation, Does this would work?
public interface ISomeView
{
}
public abstract class BaseView<T> where T : class
{
public T SomeParam { get; set; }
}
public class SomeView : BaseView<ISomeView>{
}
In both case this would work:
public class main
{
public class OneOfThoseView : ISomeView
{
}
public main()
{
OneOfThoseView oneOfThose = new OneOfThoseView();
SomeView x = new SomeView();
x.SomeParam = oneOfThose;
}
}
Edit 2:
Not exactly what you want to do but this would force your SomeView class to return a BaseView<SomeView> class
public interface ISomeView
{
}
public abstract class BaseView<T> where T : BaseView<T>
{
public T SomeParam { get; set; }
}
public class SomeView : BaseView<SomeView>
{
}
Now only this would work.
public main()
{
SomeView y= new SomeView ();
SomeView x = new SomeView();
x.SomeParam = y;
}

Constraining T to a common generic property

I have the following:
public interface IEntity
{
IEntityContent Data { get; set; }
}
public interface IEntityContent { }
public interface IThingService<T>
{
void DoThing(T item);
}
public class BaseEntity<T> : IEntity
where T : IEntityContent
{
public abstract T Data { get; set; }
}
public class FooEntity : BaseEntity<FooContent>
{
public override FooContent Data { get; set; }
}
public class FooContent : IEntityContent
{
// Some properties
}
public class ThingService<T> : IThingService<T>
where T : IEntity
{
public void DoThing(T item)
{
Serializer.Instance.Serialize(item.Content);
}
}
The signature of Serializer.Instance.Serialize is:
string Serialize<T>(T from)
But I get the following:
'BaseEntity<T>' does not implement interface member 'IEntity.Data'. 'BaseEntity<T>.Data' cannot implement 'IEntity.Data' because it does not have the matching return type of 'IEntityContent'
Why is this? As it stands, I am forced to create a bunch of near-identical strongly-typed implementations of IThingService - which is a shedload of duplication - just to specify different type arguments which, as far as I can see, should be generic.
Is this somehow related to a lack of covariance in BaseEntity? How can I make ThingService<T> work?
Why not simply have:
public interface IEntity<T>
{
T Data { get; set; }
}
For an implementation of the interface has to match the interface (as you've not declared any contra/covariance), including the return types (hence your error message)

generic where restriction by class field

Is it possible to create generic restriction in C# using where to select only classes, who have Field with some name.
for example, I have AbstractService<T>
and I have a method IEnumerable<T> ProvideData(userId);
inside provide data I should select only instances with the same user bla-bla-bla.Where(d => d.UserId == userId). But d.UserId could not be resolved. How it possible to resolve this?
IMPORTANT: I can't inherit T from class or interface, which have UserID field.
An interface is what your are looking for:
public interface IWithSomeField
{
int UserId { get; set; }
}
public class SomeGenericClasss<T>
: where T : IWithSomeField
{
}
public class ClassA : IWithSomeField // Can be used in SomeGenericClass
{
int UserId { get; set; }
}
public class ClassB // Can't be used in SomeGenericClass
{
}
[Edit] As you edited your question to state you cannot change class to implement an interface, here is some alternatives, but none relies on generic constraint :
Check the type in the constructor :
code :
public class SomeClass<T>{
public SomeClass<T>()
{
var tType = typeof(T);
if(tType.GetProperty("UserId") == null) throw new InvalidOperationException();
}
}
Use code contract invariant (not sure about the syntax) :
code :
public class SomeClass<T>{
[ContractInvariantMethod]
private void THaveUserID()
{
Contract.Invariant(typeof(T).GetProperty("UserId") != null);
}
}
Extend existing classes with partial classes
If your source classes are generated, you can cheat. I used this technique with lots of Web References having the same kind of parameter objects
Imagine the Web references produced this proxy code :
namespace WebServiceA {
public class ClassA {
public int UserId { get; set; }
}
}
namespace WebServiceB {
public partial class ClassB {
public int UserId { get; set; }
}
}
You can wrap them using in your own code:
public interface IWithUserId
{
public int UserId { get; set; }
}
public partial class ClassA : IWithUserId
{
}
public partial class ClassB : IWithUserId
{
}
then, for your service, you can instantiate AbstractService for any of the Class of the several web services :
public class AbstractService<T> where T : IWithUserId
{
}
This technique works great but only applies when you can extend class in the same project because of the partial keyword trick.

Categories

Resources