FluentValidation - PolymorphicValidator: How to avoid validator duplication? - .net-core

I have an entity called "Role" in my asp.net core 6 Web API application. Below is the DTO of the "Role" entity
public class CreateUpdateRoleDto
{
public Guid Id { get; set; }
.....
}
and I have implemented the Command like mentioned below
public class CreateRoleCommand : CreateUpdateRoleDto, IRequest<Guid>
{
}
with the corresponding Validator
public class CreateRoleCommandValidator : AbstractValidator<CreateRoleCommand>
{
}
Now, I want to implement the batch processing for the same "Role" entity and I have implemented the Command like mentioned below
public class CreateRoleBatchCommand : CreateUpdateRoleDto, IRequest
{
}
with its validator
public class CreateRoleBatchCommandValidator : AbstractValidator<CreateRoleBatchCommand>
{
}
CreateRoleBatchCommandValidator is just a copy of the CreateRoleCommandValidator except that it implements IRequest instead of IRequest but I don't think we need this additional validator as this is just a duplicate code.
Below is the implementation of BatchCommand
public class BatchCommand : IRequest
{
public IEnumerable<IRequest> Requests { get; set; }
}
The batch command validator uses the PolymorphicValidator and sets CreateRoleBatchCommandValidator as a validator for CreateRoleBatchCommand.
public class BatchCommandValidator : AbstractValidator<BatchCommand>
{
public BatchCommandValidator()
{
this.RuleForEach(x => x.Requests).SetAsyncValidator(new PolymorphicValidator<BatchCommand, IRequest>()
.Add<CreateRoleBatchCommand>(new CreateRoleBatchCommandValidator()));
}
}
However, I want to set CreateRoleCommand as a validator for CreateRoleBatchCommand, something like this
public class BatchCommandValidator : AbstractValidator<BatchCommand>
{
public BatchCommandValidator()
{
this.RuleForEach(x => x.Requests).SetAsyncValidator(new PolymorphicValidator<BatchCommand, IRequest>()
.Add<CreateRoleBatchCommand>(new CreateRoleCommandValidator()));
}
}
However it throws the following error.
CS1503 Argument 1: cannot convert from 'CreateRoleCommandValidator' to
'FluentValidation.IValidator'

Related

Singleton service visible from the classes

I have a singleton service class
public class Globals
{
public string serverURL { get; set; } = "";
public string hostURL { get; set; } = "";
}
and I have registered it in the Main function:
builder.Services.AddSingleton<Services.Globals>();
anyway I would like to access it from the rest of the Classes in the project, not only the razor pages.
For instance I have a class inside a PCL library:
public class MyStuff
{
public MyStuff()
{
- How do I access Globals in here?!
}
public void MyStuffMethod()
{
- How do I access Globals in here?!
}
}
How to access a Singleton object from the rest classes in the project ?
Access like this, you just need to add a parameter in constructor of class.
public class HomeController : Controller
{
private Globals _globals;
public HomeController(Globals globals)
{
_globals = globals;
}
}

Validating enums with FluentValidation

I'm using FluentValidation.WebApi 6.2.1.0 in Web API project. Is there a way to validate enum with FluentValidation and return custom message?
my controller action is as following,
public IHttpActionResult Get([FromUri]CheckUpdateVM info)
{
...
}
My Model,
[Validator(typeof(CheckUpdateVMValidator))]
public class CheckUpdateVM
{
public DeviceTypes Device { get; set; }
}
I'm looing for something like this,
public class CheckUpdateVMValidator : AbstractValidator<CheckUpdateVM>
{
public CheckUpdateVMValidator()
{
RuleFor(x => x.Device).Must(x => Enum.IsDefined(typeof(DeviceTypes), x)).WithMessage("xxx");
}
}
With above code, Model binder validate the value of "Device" parameter and response with an error. but I can't customize the error message. (If I set the "Device" property type to string, this works fine.)
Creating custom validator could be better approach in this scenario.
public class DeviceEnumValidator<T> : PropertyValidator {
public DeviceEnumValidator()
: base("Invalid Enum value!") { }
protected override bool IsValid(PropertyValidatorContext context) {
DeviceTypes enumVal= (DeviceTypes) Enum.Parse(typeof(DeviceTypes), context.PropertyValue);
if (!Enum.IsDefined(typeof(DeviceTypes), enumVal)
return false;
return true;
}
}
To use DeviceEnumValidator you can call SetValidator when defining a validation rule.
public CheckUpdateVMValidator()
{
RuleFor(x => x.Device).SetValidator(new DeviceEnumValidator<DeviceTypes>());
}

ASP.NET MVC4: IQueryable does not contain a definition for 'Add'

I have tried to make an MVC news system. I started by the use a pluralsight tutorial which was used to create a department with employees. I changed the idea to fit my own purposes, changing the departments to "category" and employees to "newspost". This all works out fine, but now I want to remove the categories, since I don't need categories for my news system. But I can't add to the database using entityframework when I do this.
I have an interface for the datasource that looks like this:
INewMvcSiteDataSource
public interface INewMvcSiteDataSource
{
IQueryable<NewsPost> NewsPosts { get; }
void Save();
}
This is inherited by my DB Context class:
NewMvcSiteDb
public class NewsMvcSiteDb : DbContext, INewMvcSiteDataSource
{
public NewsMvcSiteDb() : base("DefaultConnection")
{
}
public DbSet<NewsPost> NewsPosts { get; set; }
IQueryable<NewsPost> INewMvcSiteDataSource.NewsPosts
{
get { return NewsPosts; }
}
public void Save()
{
SaveChanges();
}
}
I then want to use it in the controller to add a newspost to the database:
NewsController
var newsPost = new NewsPost()
{
Subject = newsModel.Subject,
Content = newsModel.Content,
ImagePath = newsModel.ImagePath
};
_db.NewsPosts.Add(newsPost);
_db.Save();
But this is where the ADD fails with the message: 'System.Linq.IQueryable' does not contain a definition for 'Add' and no extension method 'Add' accepting a first argument of type 'System.Linq.IQueryable' could be found (are you missing a using directive or an assembly reference?)
Now as the error says, its caused by using IQueryable, but I have no idea how else to do it.
Can you guys help?
Thanks.
If you don't mind exposing DbSet via your interface (some people don't like the ORM bleeding into the application),you should be able to do the following:
public interface INewMvcSiteDataSource
{
DbSet<NewsPost> NewsPosts { get; }
void Save();
}
public class NewsMvcSiteDb : DbContext, INewMvcSiteDataSource
{
public NewsMvcSiteDb() : base("DefaultConnection")
{
}
public DbSet<NewsPost> NewsPosts { get; set; }
public void Save()
{
SaveChanges();
}
}

What is the namespace for IService interface?

I am learning ServiceStack and developing simple demo for helloworld, but could not find namespace for ISservice interface, my code as per below:
public class Hello
{
public string name { get; set; }
}
public class HelloResponse
{
public string Result { get; set; }
}
public class HelloService : **IService**<Hello>
{
public object Execute(Hello request)
{
return new HelloResponse { Result = "Hello" + request.name };
}
}
public class HelloAppHost : AppHostBase
{
public HelloAppHost() : base("Hello Web Services", typeof(HelloService).Assembly) { }
public override void Configure(Funq.Container container)
{
Routes.Add<Hello>("/hello")
.Add<Hello>("/hello/{Name}");
}
}
Can anyone please tell me what namespace or DLL I need to add for IService interface?
ServiceStack's IService<T> is in the ServiceStack.ServiceHost namespace which lives in the ServiceStack.Interfaces.dll, why here's the class:
https://github.com/ServiceStack/ServiceStack/blob/master/src/ServiceStack.Interfaces/ServiceHost/IService.cs
Note: If you're just starting out, it's probably better to inherit from ServiceStack.ServiceInterface.ServiceBase<T> and override the Run() method which is a useful base class that provides things like auto exception handling for you.
If you want to be able run different code for different HTTP Verbs e.g GET/POST/PUT/DELETE (i.e. creating REST web services) than you want to inherit from RestServiceBase instead and override its OnGet/OnPost/OnPut/OnDelete methods.

Ninject giving NullReferenceException

I'm using asp.net MVC 2 and Ninject 2.
The setup is very simple.
Controller calls service that calls repository.
In my controller I use inject to instantiate the service classes with no problem. But the service classes don't instantiate the repositories, giving me NullReferenceException.
public class BaseController : Controller
{
[Inject]
public IRoundService roundService { get; set; }
}
This works. But then this does not...
public class BaseService
{
[Inject]
public IRoundRepository roundRepository { get; set; }
}
Giving a NullReferenceException, when I try to use the roundRepository in my RoundService class.
IList<Round> rounds = roundRepository.GetRounds( );
Module classes -
public class ServiceModule : NinjectModule
{
public override void Load( )
{
Bind<IRoundService>( ).To<RoundService>( ).InRequestScope( );
}
}
public class RepositoryModule : NinjectModule
{
public override void Load( )
{
Bind<IRoundRepository>( ).To<RoundRepository>( ).InRequestScope( );
}
}
In global.axax.cs
protected override IKernel CreateKernel( )
{
return new StandardKernel( new ServiceModule( ),
new RepositoryModule( ) );
}
Have you thought about using constructor injection?
That's how I do my dependency injection with Ninject 2 & ASP.NET MVC 2 and it works all the way down the chain from controller -> service -> repository & beyond.
It also makes sense to me to have the dependencies in the constructor for your object. It makes these dependencies highly visible and obvious to any other object that has to instantiate it. Otherwise you may end up with null reference exceptions... kinda like you have here.
HTHs,
Charles
EDIT: Showing base class injection through constructors in response to the comments.
public class BaseService
{
public IRoundRepository RoundRepo { get; private set; }
public BaseService(IRoundRepository roundRepo)
{
RoundRepo = roundRepo;
}
}
public class SquareService : BaseService
{
public ISquareRepository SquareRepo { get; private set; }
public SquareService(ISquareRepository squareRepo, IRoundRepository roundRepo)
: base(roundRepo)
{
SquareRepo = squareRepo;
}
}
This is just my way of doing things... someone else may have a different idea / opinion.

Resources