How to use FluentValidation.AspNetCore and FluentValidation.MVC6? - asp.net-core

How to use FluentValidation.AspNetCore and FluentValidation.MVC6 to validate Entities in AspNetCore , can anyone give me an example ?

This is working for me:
project.json add:
"FluentValidation.AspNetCore": "6.4.0-beta3"
startup.cs
services
.AddMvc()
.AddFluentValidation(fv => fv.RegisterValidatorsFromAssemblyContaining<Startup>());
Validation:
public class Foo
{
public string Bar {get; set;}
}
public class FooValidator : AbstractValidator<Foo>
{
public FooValidator()
{
RuleFor(x => x.Bar).NotEmpty().WithMessage("Error Message");
}
}

first you need to add nuget Install-Package FluentValidation.AspNetCore
you can have an action filter which will handle validation:
public class ValidatorActionFilter : IActionFilter
{
public void OnActionExecuting(ActionExecutingContext filterContext)
{
if (!filterContext.ModelState.IsValid)
{
filterContext.Result = new BadRequestObjectResult(filterContext.ModelState);
}
}
public void OnActionExecuted(ActionExecutedContext filterContext)
{
}
}
Startup.ConfigureServices looks like this to add auto validation and add FluentValidation:
services.AddMvc(opt =>
{
opt.Filters.Add(typeof(ValidatorActionFilter));
}).AddFluentValidation(fvc =>
fvc.RegisterValidatorsFromAssemblyContaining<Startup>())
If you need to read another assembly, name one of its classes instead of startUp
Now you can add a validation to ensure:
public class CreateCustomer
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
}
public class CreateCustomerValidator : AbstractValidator<CreateCustomer>
{
public CreateCustomerValidator()
{
RuleFor(x => x.FirstName).NotNull().WithMessage(Resource.Validaton.FirstNameRequired);
RuleFor(x => x.LastName).NotNull().WithMessage(Resource.Validaton.LastNameRequired);
RuleFor(x => x.Email).Matches(#"\A(?:[a-z0-9!#$%&'*=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*=?^_`{|}~-]+)*#(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?)\Z").WithMessage(Resource.Validaton.EmailFormat);
}
}

Related

Add-Migration Error: Object reference not set to an instance of an object. Asp.Net

[enter image description here][1]
[1]: https://i.stack.imgur.com/QNwMk.png`enter code here`
Migration did not occur if I did not add all packages.I'm new to this job. I would be glad if you help.
PhoneBookContext
namespace PhoneBook.Repository.Concrate.EntityFramework
{
public class PhoneBookContext:DbContext
{
public PhoneBookContext(DbContextOptions<PhoneBookContext>options):base(options)
{
}
public DbSet<Persons> Persons { get; set; }
public DbSet<Informations> Informations { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<PersonsInformation>()
.HasKey(pk => new { pk.PersonID, pk.InformationsID });
}
}
}
EfPersonsRepository
namespace PhoneBook.Repository.Concrate.EntityFramework
{
public class EfPersonsRepository : IPersonsRepository
{
private PhoneBookContext context;
public EfPersonsRepository(PhoneBookContext ctx)
{
context = ctx;
}
public IQueryable<Persons> Persons => context.Persons;
}
}

.net Core 3 / API / DI / Repository pattern

I'm trying to create a web API in .net Core. I would like this API calls a DLL with repository design pattern implemented in it. In this whole thing I tried to use dependency injection but I have some issues to manage context for database in repository.
I would like than the context one new context per lifetime of a call to the API.
When I try to execute my code I have an exception at line CreateHostBuilder(args).Build().Run(); in Main.
This Exception is :
'Some services are not able to be constructed'
InvalidOperationException : Unable to resolve service for type 'BX_Security_AccessBase.Context.SecurityContext' while attempting to activate 'BX_Security_AccessBase.Repository.UsersRepository'.
I know the code is incomplete and won't work completely but at least it should break way later than actually. I think I made a mistake in the architecture.
There is a lot of code below but I couldn't isolate my problem.
Thank you everybody.
In my API I have :
Startup.cs
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddScoped<IUsersRepository, UsersRepository>();
services.AddScoped<IUserService, UserService>();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
User.cs
public class User
{
public User() {}
public int UserId { get; set; }
public string LastName { get; set; }
public string FirstName { get; set; }
public DateTime Birthdate { get; set; }
}
UserService.cs
public class UserService : IUserService
{
private readonly AppSettings _appSettings;
private readonly IUsersRepository _userRepository;
public UserService(IOptions<AppSettings> appSettings, IUsersRepository userRepository)
{
_appSettings = appSettings.Value;
_userRepository = userRepository;
}
public IEnumerable<User> GetAll()
{
return _userRepository.GetAllUsers().Select(u=> new User());
}
}
IUserService.cs
public interface IUserService
{
public IEnumerable<User> GetAll();
}
AppSettings.cs
public class AppSettings
{
public string Secret { get; set; }
}
UsersController.cs
[Route("api/[controller]")]
[ApiController]
public class UsersController : ControllerBase
{
private IUserService _userService { get; }
public UsersController(IUserService userService)
{
_userService = userService;
}
[HttpGet]
public IActionResult GetAll()
{
var users = _userService.GetAll();
return Ok(users);
}
}
In the DLL I have :
SecurityContext.cs
public partial class SecurityContext : DbContext
{
public SecurityContext(DbContextOptions<SecurityContext> options) : base(options) { }
public DbSet<Users> Users { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (!optionsBuilder.IsConfigured)
{
optionsBuilder.UseSqlServer("Data Source=; Database=BXDB; User Id=sa; Password=;");
}
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Users>(entity =>
{
entity.HasKey(e => e.UserId).HasName("PK_User_UserId");
entity.ToTable("Users", "sec");
entity.Property(e => e.Birthdate).HasColumnType("date");
entity.Property(e => e.FirstName)
.HasMaxLength(50)
.IsUnicode(false);
entity.Property(e => e.LastName)
.HasMaxLength(50)
.IsUnicode(false);
});
OnModelCreatingPartial(modelBuilder);
}
partial void OnModelCreatingPartial(ModelBuilder modelBuilder);
}
Users.cs
public class Users
{
public Users() { }
public int UserId { get; set; }
public string LastName { get; set; }
public string FirstName { get; set; }
public DateTime Birthdate { get; set; }
}
IUsersRepository.cs
public interface IUsersRepository
{
public IQueryable<Users> GetAllUsers();
}
UsersRepository.cs
public class UsersRepository : IUsersRepository
{
public readonly SecurityContext _dbContext;
public UsersRepository(SecurityContext dbContext)
{
_dbContext = dbContext;
}
public IQueryable<Users> GetAllUsers()
{
return _dbContext.Users;
}
}
'Some services are not able to be constructed' InvalidOperationException : Unable to resolve service for type 'BX_Security_AccessBase.Context.SecurityContext' while attempting to activate 'BX_Security_AccessBase.Repository.UsersRepository'.
From the error , you should register the DbContext as a service as follows:
public void ConfigureServices(IServiceCollection services)
{
var connection = #"Server=(localdb)\mssqllocaldb;Database=BXDB;Trusted_Connection=True;ConnectRetryCount=0";
services.AddDbContext<DLL.Models.SecurityContext>(options => options.UseSqlServer(connection, x => x.MigrationsAssembly("DLL")));
services.AddControllers();
services.AddScoped<IUsersRepository, UsersRepository>();
services.AddScoped<IUserService, UserService>();
}

Complex class - cross class implementation

System Details
FluentValidation version: FluentValidation, Version=7.0.0.0
Web Framework version :ASP.NET Core 2.1
Issue Description
public class Address
{
public string Address { get; set; }
public string City { get; set; }
}
public class AddressValidator : AbstractValidator<Address>
{
public AddressValidator()
{
RuleFor(x => x.Address).NotEmpty().WithMessage("The Address cannot be empty.");
RuleFor(x => x.City).NotEmpty().WithMessage("The City cannot be empty.");
}
}
public class PersonBase
{
public string FirstName { get; set; }
public string LastName { get; set; }
public Address PermanentAddress { get; set; }
}
public class PersonGroupA : PersonBase
{
public int TotalSaleCount { get; set; }
}
public class PersonGroupB : PersonBase
{
public int TodaySaleCount { get; set; }
}
public class PersonBaseValidator : AbstractValidator<PersonBase>
{
public PersonBaseValidator()
{
RuleFor(x => x.FirstName).NotEmpty().WithMessage("The First Name cannot be empty.");
RuleFor(x => x.LastName).NotEmpty().WithMessage("The Last Name cannot be empty.");
RuleFor(x => x.PermanentAddress).SetValidator(new AddressValidator());
}
}
public class PersonGroupAValidator : AbstractValidator<PersonGroupA>
{
public PersonGroupAValidator()
{
RuleFor(x => x.TotalSaleCount).NotEmpty().WithMessage("The Total Sale Count cannot be empty.");
}
}
public class PersonGroupBValidator : AbstractValidator<PersonGroupB>
{
public PersonGroupBValidator()
{
RuleFor(x => x.TodaySaleCount).NotEmpty().WithMessage("The Today Sale Count cannot be empty.");
}
}
how to use base validator in child validator
This is working when add likebelow
public class PersonGroupAValidator : AbstractValidator<PersonGroupA> {
public PersonGroupAValidator() {
Include(new PersonBaseValidator());
RuleFor(x => x.TotalSaleCount).NotEmpty().WithMessage("The Total Sale Count cannot be empty.");
}
}

Is there a nice pattern for implementing an abstract DbContext in EF7 / .Net Core 3 to avoid duplication of shared entities / config across projects?

I have a number of different projects that all implement the same schema for configuration, security and audit and are looking for a pattern that would allow me to put these schema definitions in an abstract classes (entity, configuration and dbcontext) that can be extended in the concrete implementations if needed. My current POC fails when the base configurations is applied. I get:
A key cannot be configured on 'UserRole' because it is a derived type. The key must be configured on the root type.
Any help / pointers will be greatly appreciated!
I have the following code samples....
Abstract base classes
RoleBase
public abstract class RoleBase
{
public RoleBase()
{
this.UserRoles = new List<UserRoles>();
}
public long Id { get; set; }
public string Name { get; set; }
public virtual IEnumerable<UserRoleBase> UserRoles { get; set; }
}
UserBase
public abstract class UserBase
{
public long Id { get; set; }
public string Username { get; set; }
public string Email { get; set; }
public virtual ICollection<UserRoleBase> UserRoles { get; set; }
}
UserRoleBase
public abstract class UserRoleBase
{
public long Id { get; set; }
public long RoleId { get; set; }
public long UserId { get; set; }
public bool Deleted { get; set; }
public virtual RoleBase Role { get; set; }
public virtual UserBase User { get; set; }
}
Each of these have an abstract configuration class for the base class...
RoleBase Configuration
public abstract class RoleConfiguration<T> : IEntityTypeConfiguration<T>
where T : RoleBase
{
public virtual void Configure(EntityTypeBuilder<T> builder)
{
// Primary Key
builder.HasKey(t => t.Id);
// Properties
builder.Property(t => t.Name)
.IsRequired()
.HasMaxLength(50);
// Table & Column Mappings
builder.ToTable("Role", "Security");
builder.Property(t => t.Id).HasColumnName("Id");
builder.Property(t => t.Name).HasColumnName("Name");
}
}
UserBase Configuration
public abstract class UserConfiguration<TBase> : IEntityTypeConfiguration<TBase>
where TBase : UserBase
{
public virtual void Configure(EntityTypeBuilder<TBase> builder)
{
// Primary Key
builder.HasKey(t => t.Id);
// Properties
builder.Property(t => t.Username).IsRequired().HasMaxLength(255);
builder.Property(t => t.Email).IsRequired().HasMaxLength(255);
// Table & Column Mappings
builder.ToTable("User", "Security");
builder.Property(t => t.Id).HasColumnName("Id");
builder.Property(t => t.Username).HasColumnName("Username");
builder.Property(t => t.Email).HasColumnName("Email");
}
}
UserRoleBase Configuration
public abstract class UserRoleConfiguration<T> : IEntityTypeConfiguration<T>
where T : UserRoleBase
{
public virtual void Configure(EntityTypeBuilder<T> builder)
{
// Primary Key
builder.HasKey(t => t.Id);
// Properties
builder.Property(t => t.RoleId).IsRequired();
builder.Property(t => t.UserId).IsRequired();
builder.Property(t => t.Deleted).IsRequired();
// Table & Column Mappings
builder.ToTable("UserRole", "Security");
builder.Property(t => t.Id).HasColumnName("Id");
builder.Property(t => t.RoleId).HasColumnName("RoleId");
builder.Property(t => t.UserId).HasColumnName("UserId");
builder.Property(t => t.Deleted).HasColumnName("Deleted");
// Relationships
builder.HasOne(t => t.Role)
.WithMany(t => (ICollection<TBase>)t.UserRoles)
.HasForeignKey(d => d.RoleId)
.OnDelete(DeleteBehavior.Restrict);
builder.HasOne(t => t.UserDetail)
.WithMany(t => (ICollection<TBase>)t.UserRoles)
.HasForeignKey(d => d.UserDetailId)
.OnDelete(DeleteBehavior.Restrict);
}
And a concrete implementation of the base classes:
Role
public class Role : RoleBase
{
}
User
public class User : UserBase
{
// Extension properties
public string FirstName { get; set; }
public string LastName { get; set; }
public string Phone { get; set; }
public string Mobile { get; set; }
}
UserRole
public class UserRole : UserRoleBase
{
}
And a concrete implementation of the configuration
RoleConfiguration
public class RoleConfiguration : Base.Configurations.RoleConfiguration<Role>
{
public override void Configure(EntityTypeBuilder<Role> builder)
{
base.Configure(builder);
this.ConfigureEntity(builder);
}
private void ConfigureEntity(EntityTypeBuilder<Role> builder)
{
}
}
UserConfiguration
public class UserConfiguration : Base.Configurations.UserConfiguration<User>
{
public override void Configure(EntityTypeBuilder<User> builder)
{
base.Configure(builder);
this.ConfigureEntity(builder);
}
private void ConfigureEntity(EntityTypeBuilder<User> builder)
{
//Registration of extension properties
builder.Property(t => t.FirstName).HasColumnName("FirstName");
builder.Property(t => t.LastName).HasColumnName("LastName");
builder.Property(t => t.Phone).HasColumnName("Phone");
builder.Property(t => t.Mobile).HasColumnName("Mobile");
}
}
UserRoleConfiguration
public class UserRoleConfiguration : Base.Configurations.UserRoleConfiguration<UserRole>
{
public override void Configure(EntityTypeBuilder<UserRole> builder)
{
base.Configure(builder);
this.ConfigureEntity(builder);
}
private void ConfigureEntity(EntityTypeBuilder<UserRole> builder)
{
}
}
And the base context
public abstract class BaseDbContext: DbContext
{
public BaseDbContext(DbContextOptions<BaseDbContext> options)
: base(options)
{
}
// https://github.com/aspnet/EntityFramework.Docs/issues/594
protected BaseDbContext(DbContextOptions options)
: base(options)
{
}
public DbSet<RoleBase> Roles { get; set; }
public DbSet<UserBase> Users { get; set; }
public DbSet<UserRoleBase> UserRoles { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
}
}
And the concrete context
public class MyDbContext: BaseDbContext
{
public MyDbContext(DbContextOptions<MyDbContext> options)
:base(options)
{
}
protected MyDbContext(DbContextOptions options)
: base(options)
{
}
public new DbSet<Role> Roles { get; set; }
public new DbSet<User> Users { get; set; }
public new DbSet<UserRole> UserRoles { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.ApplyConfiguration(new RoleConfiguration());
modelBuilder.ApplyConfiguration(new UserConfiguration());
modelBuilder.ApplyConfiguration(new UserRoleConfiguration());
base.OnModelCreating(modelBuilder);
}
}
So all this works for items that does not have navigation properties and migrates to the database fine as long as there is no navigation properties. I can see the extension properties on User being done as long as I comment out all the navigation properties.
With the navigation properties present, I get an error on the base configuration class. after concrete implementation called base.Configure(builder);
I get the following error message on builder.HasKey(t => t.Id); and for the above sample code it would be on...
public abstract class UserRoleConfiguration<T> : IEntityTypeConfiguration<T>
where T : UserRoleBase
{
public virtual void Configure(EntityTypeBuilder<T> builder)
{
// Primary Key
builder.HasKey(t => t.Id);
System.InvalidOperationException: 'A key cannot be configured on 'UserRole' because it is a derived type. The key must be configured on the root type 'UserRoleBase'. If you did not intend for 'UserRoleBase' to be included in the model, ensure that it is not included in a DbSet property on your context, referenced in a configuration call to ModelBuilder, or referenced from a navigation property on a type that is included in the model.'
Is there a way in which I can keep these relational configuration in the abstract base class so that I would not need to replicate it in each concrete implementation of the base classes? Or is there a different approach that can be followed to overcome this issue?
System.InvalidOperationException: 'A key cannot be configured on 'UserRole' because it is a derived type. The key must be configured on the root type 'UserRoleBase'. If you did not intend for 'UserRoleBase' to be included in the model, ensure that it is not included in a DbSet property on your context, referenced in a configuration call to ModelBuilder, or referenced from a navigation property on a type that is included in the model.'
From the error, you could use Key attribute on the id of the base model to specify the primary key .
From breaking changes included in EF Core 3.0 , ToTable on a derived type throws an exception , currently it isn't valid to map a derived type to a different table. This change avoids breaking in the future when it becomes a valid thing to do.
You could use Data Annotations on the base model to configure the table that a type maps to:
[Table("Role", Schema = "Security")]
public abstract class RoleBase
{
public RoleBase()
{
this.UserRoles = new List<UserRoles>();
}
[Key]
public long Id { get; set; }
public string Name { get; set; }
public virtual ICollection<UserRoleBase> UserRoles { get; set; }
}

Unit test Fluentvalidation

I have 2 classes as like below
public class A
{
public string Name { get; set;}
}
public class B : A
{
public string Description { get; set;}
}
With the following fluent validators
public class AValidator : AbstractValidator<A>
{
public A()
{
RuleFor(x => x.Name).NotEmpty();
}
}
public class BValidator : AbstractValidator<B>
{
public B(IValidator<AValidator> aValidator)
{
Include(aValidator);
RuleFor(x => x.Description).NotEmpty();
}
}
I'm using StrctureMap for IOC and xUnit for unit tests. Is there any way that I can validate / verify that Include has been setup in the BValidator?