How to trigger model validation inside IValidateOptions<T> implementation? - c#

I have a .Net 5 app and want to add validators for my configurations. Given this sample options
public sealed class DatabaseOptions
{
public string ConnectionString { get; set; }
}
I currently validate it with this implementation
public sealed class DatabaseOptionsValidator : IValidateOptions<DatabaseOptions>
{
public ValidateOptionsResult Validate(string name, DatabaseOptions databaseOptions)
{
List<string> validationFailures = new List<string>();
if (string.IsNullOrEmpty(databaseOptions.ConnectionString))
validationFailures.Add($"{nameof(databaseOptions.ConnectionString)} is required.");
// ...
if (validationFailures.Any())
{
return ValidateOptionsResult.Fail(validationFailures);
}
return ValidateOptionsResult.Success;
}
}
I would like to avoid implementing my own validation checks and error messages since I know data annotations already get the job done.
I modified the options model to this
public sealed class DatabaseOptions
{
[Required]
[MinLength(9999999)] // for testing purposes
public string ConnectionString { get; set; }
}
and was hoping to find a way to trigger the model validation
public sealed class DatabaseOptionsValidator : IValidateOptions<DatabaseOptions>
{
public ValidateOptionsResult Validate(string name, DatabaseOptions databaseOptions)
{
List<string> validationFailures = new List<string>();
// trigger the model validation and add every error to the validationFailures list
if (validationFailures.Any())
{
return ValidateOptionsResult.Fail(validationFailures);
}
return ValidateOptionsResult.Success;
}
}
but unfortunately I wasn't able to do so. The debugger hits the validator but how can I trigger the validation inside the Validate method?

Please have a look at the comments since my solution is already available!
Based on Rodrigo Rodrigues answer I created my own options validator based on data annotations
public sealed class OptionsValidator<TOptions> : IValidateOptions<TOptions> where TOptions : class
{
public ValidateOptionsResult Validate(string name, TOptions options)
{
ValidationContext validationContext = new ValidationContext(options);
List<ValidationResult> validationResults = new List<ValidationResult>();
bool noValidationErrorsOccured = Validator.TryValidateObject(options, validationContext, validationResults, true);
if (noValidationErrorsOccured) {
return ValidateOptionsResult.Success;
}
IEnumerable<string> validationFailures = validationResults.Select(validationResult => validationResult.ErrorMessage);
return ValidateOptionsResult.Fail(validationFailures);
}
}
So whenever I want to add a validator to my DI container I can make use of this extension method
public static IServiceCollection AddOptionsValidator<TOptions>(this IServiceCollection serviceCollection) where TOptions : class
=> serviceCollection.AddSingleton<IValidateOptions<TOptions>, OptionsValidator<TOptions>>();

There is a technique I use for validating data annotations in my netcore apps, not using IValidateOptions, but implementing a custom validator, and registering it as PostConfigure.
You can find valuable assets in the namespace System.ComponentModel.DataAnnotations.
Something like this:
// Custom validator for data annotations
public static class Validation {
public static void ValidateDataAnotations<TOptions>(TOptions options) {
var context = new ValidationContext(options);
var results = new List<ValidationResult>();
Validator.TryValidateObject(options, context, results, validateAllProperties: true);
if (results.Any()) {
var aggrErrors = string.Join(' ', results.Select(x => x.ErrorMessage));
var count = results.Count;
var configType = typeof(TOptions).Name;
throw new ApplicationException($"Found {count} configuration error(s) in {configType}: {aggrErrors}");
}
}
}
Then, you register this static method in you composition root (probably Startup.cs):
public void ConfigureServices(IConfiguration configuration, IServiceCollection serviceCollection) {
// (...)
serviceCollection.Configure<DatabaseOptions>(configuration.GetSection(nameof(DatabaseOptions)));
// invalid configuration values will break at this point
serviceCollection.PostConfigure<DatabaseOptions>(Validation.ValidateDataAnotations);
}

Related

Getting AutoRegisteringInputObjectGraphType and AutoRegisteringObjectGraphType working for GraphQL.NET v5

I'm trying to get the auto registering functionality working in GraphQL.NET. However getting some issues with List types. Please note that I'm very new to the world of GraphQL and have never used this framework before, if you see any improvements to the code, please let me know. Appreciate any help with this.
The following error message is received.
"Message": "The GraphQL type for field 'BasicInformationResponse.status' could not be derived implicitly. Could not find type mapping from CLR type 'ScroW.Application.Features.Information.Queries.BasicInformation.Status' to GraphType. Did you forget to register the type mapping with the 'ISchema.RegisterTypeMapping'?",
I have registered the following Schema and Types. Please note that BasicInformationResponse contains a List of statuses as per below.
public class BasicInformationResponseType : AutoRegisteringObjectGraphType<BasicInformationResponse>
{
public BasicInformationResponseType() : base()
{
Name = "BasicInformationResponseType";
}
}
public class StatusType : AutoRegisteringObjectGraphType<Status>
{
public StatusType() : base()
{
Name = "StatusType";
}
}
public class BasicInformationResponse
{
public string OrganizationNumber { get; set; }
public string Name { get; set; }
public List<Status> Status { get; set; }
}
Not sure what kind of field I'm registering here? (rn as a StringGraphType, but I'm pretty sure it should be something else).
public class Query : ObjectGraphType
{
public Query(IMediator mediator)
{
FieldAsync<StringGraphType>(
Name = "basicInformation",
arguments: new QueryArguments(new QueryArgument<AutoRegisteringInputObjectGraphType<BasicInformationResponse>> { Name = "organizationNumber" }),
resolve: async x =>
{
return await mediator.Send(new BasicInformationQuery
{
OrganizationNumber = x.GetArgument<String>("organizationNumber")
});
});
}
}
My schema definition
public class ScroWSchema : Schema
{
public ScroWSchema(IServiceProvider provider)
: base(provider)
{
Query = (Query)provider.GetRequiredService(typeof(Query)) ?? throw new InvalidOperationException();
// Mutation = (StarWarsMutation)provider.GetService(typeof(StarWarsMutation)) ?? throw new InvalidOperationException();
// FieldMiddleware.Use(new InstrumentFieldsMiddleware());
}
}
And lastly startup. I'm not really sure around what is needed to be registered as a singleton and not and was confused by different guides out there. If any can be removed please let me know.
services.AddGraphQL(builder => builder
// .AddSchema<ScroWSchema>()
.AddSelfActivatingSchema<ScroWSchema>()
.AddClrTypeMappings()
.AddNewtonsoftJson()
.AddGraphTypes(typeof(ScroWSchema).Assembly)
);
services.AddSingleton<IDocumentExecuter, DocumentExecuter>();
services.AddSingleton<IGraphQLSerializer, GraphQLSerializer>();
services.AddSingleton<ISchema, ScroWSchema>(services => new ScroWSchema(new SelfActivatingServiceProvider(services)));
services.AddSingleton(typeof(AutoRegisteringObjectGraphType<>));
services.AddSingleton(typeof(AutoRegisteringInputObjectGraphType<>));
services.AddSingleton<BasicInformationResponseType>();
services.AddSingleton<StatusType>();
Alright, so managed to solve this with a lot of trial and error etc.
In general, the AutoRegisteringObjectGraphType was correctly setup:
public class BasicInformationResponseType : AutoRegisteringObjectGraphType<BasicInformationResponse>
{
public BasicInformationResponseType()
{
}
}
Scema was also correct:
public class ScroWSchema : Schema
{
public ScroWSchema(IServiceProvider provider)
: base(provider)
{
Query = (Query)provider.GetRequiredService(typeof(Query)) ?? throw new InvalidOperationException();
}
}
Issue was the Query class, which wasn't correctly setup, the below got it working:
public class Query : ObjectGraphType
{
public Query(IMediator mediator)
{
FieldAsync<BasicInformationResponseType>(
Name = "basicInformation",
arguments: new QueryArguments(new QueryArgument<NonNullGraphType<StringGraphType>> { Name = "organizationNumber" }),
resolve: async x =>
{
return await mediator.Send(new BasicInformationQuery
{
OrganizationNumber = x.GetArgument<string>("organizationNumber")
});
});
}
}
In regards to startup services were automatically added through the GraphQL registration, no need to add specific services.
services.AddGraphQL(builder => builder
.AddSelfActivatingSchema<ScroWSchema>()
.AddClrTypeMappings()
.AddSystemTextJson()
.AddGraphTypes(typeof(ScroWSchema).Assembly)
);

OData ignore certain properties unless explicitly stated to be returned

I have this model with following attributes. (Simplified)
public class Blog {
private string Code { get; set; }
private string Name { get; set; }
private byte[] Image { get; set; }
}
When I make a request to the OData URL for ex: http://localhost/api/odata/Blog, I want only Code and Name properties to be returned, ignoring the Image. And if I make
a request something like http://localhost/api/odata/Blog?$select=(Code,Name,Image) then I want the Image to be returned. How can I make this work?
Using attributes like [IgnoreDataMember] makes it unavailable for OData query to be accessed, therefore it is not a suitable solution.
First, probably properties of the Blog class are public, not private.
I had a similar scenario and resolve it by implementing a custom serializer:
Serializer provider class:
public class MyODataSerializerProvider : DefaultODataSerializerProvider
{
MyResourceSerializer myResourceSerializer;
public MyODataSerializerProvider(IServiceProvider serviceProvider) : base(serviceProvider)
{
myResourceSerializer = new MyResourceSerializer(this);
}
public override ODataEdmTypeSerializer GetEdmTypeSerializer(IEdmTypeReference edmType)
{
if (edmType.IsEntity())
{
return myResourceSerializer;
}
return base.GetEdmTypeSerializer(edmType);
}
}
Serializer class:
public class MyResourceSerializer : ODataResourceSerializer
{
public MyResourceSerializer(ODataSerializerProvider serializerProvider) : base(serializerProvider) { }
public override ODataResource CreateResource(SelectExpandNode selectExpandNode, ResourceContext resourceContext)
{
var resource = base.CreateResource(selectExpandNode, resourceContext);
if (selectExpandNode.SelectAllDynamicProperties)
{
resource.Properties = resource.Properties.Where(p => p.Name != "Image");
}
return resource;
}
}
And configuration of course:
routeBuilder.MapODataServiceRoute("OData", "odata", b =>
{
b.AddService(Microsoft.OData.ServiceLifetime.Singleton, sp => edmModel);
var conventions = ODataRoutingConventions.CreateDefault();
//Workaround for https://github.com/OData/WebApi/issues/1622
conventions.Insert(0, new AttributeRoutingConvention("OData", app.ApplicationServices, new DefaultODataPathHandler()));
//Custom Convention
b.AddService<IEnumerable<IODataRoutingConvention>>(Microsoft.OData.ServiceLifetime.Singleton, a => conventions);
b.AddService(Microsoft.OData.ServiceLifetime.Singleton, typeof(ODataSerializerProvider), sp => new MyODataSerializerProvider(sp));
});

Dependency Injection for custom validation attributes

I created a custom validation attribute that I want to use for my API controller DTOs. This attribute needs values from the configured options, that's why I'm injecting them in the constructor, so that I can use the options service later on in the IsValid and FormatErrorMessage method.
internal class MyValidationAttribute : ValidationAttribute
{
private readonly IOptionsMonitor<MyOptions> myOptionsMonitor;
public MyValidationAttribute(IOptionsMonitor<MyOptions> myOptionsMonitor)
{
this.myOptionsMonitor = myOptionsMonitor;
}
public override bool IsValid(object value)
{
// ... use myOptionsMonitor here ...
return false;
}
public override string FormatErrorMessage(string name)
{
// ... use myOptionsMonitor here ...
return string.Empty;
}
}
Unfortunately when I want to use this as an attribute in my DTO
internal class MyDTO
{
[MyValidationAttribute]
public string Foo { get; set; }
}
I get the error message
There is no argument given that corresponds to the required formal
parameter 'myOptionsMonitor' of
'MyValidationAttribute.MyValidationAttribute(IOptionsMonitor)'
Is there a way I can use dependency injection for validation attributes? I know that I can use the ValidationContext like so
internal class MyValidationAttribute : ValidationAttribute
{
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
if (value != null)
{
IOptionsMonitor<MyOptions> myOptionsMonitor = validationContext.GetService<IOptionsMonitor<MyOptions>>();
// ...
return ValidationResult.Success;
}
return new ValidationResult("Something failed");
}
}
But I want to use the FormatErrorMessage method from the base class and this has no access to the options service.
My current solution
For now, this is the code I'm using
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
internal class CustomValidationAttribute : ValidationAttribute
{
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
IOptionsMonitor<MyOptions> myOptionsMonitor = validationContext.GetService<IOptionsMonitor<MyOptions>>();
Dictionary<string, string> myMap = myOptionsMonitor.CurrentValue.MyMap;
string key = value.ToString() ?? string.Empty;
if (myMap.ContainsKey(key))
return ValidationResult.Success;
string[] formattedKeys = myMap.Keys.Select(key => $"'{key}'").ToArray();
string keysText = string.Join(" / ", formattedKeys);
string errorMessage = $"Invalid value. Valid ones are {keysText}";
return new ValidationResult(errorMessage);
}
}
Attributes are not designed for this purpose. But you can use action filters instead.
Let`s make your attribute as simple as it can be, we don't need any validation logic there.
[AttributeUsage(AttributeTargets.Property)]
public class CustomValidationAttribute : Attribute
{ }
For my example I created service that we are going to inject
public class SomeService
{
public bool IsValid(string str)
{
return str == "Valid";
}
}
and a class that we are going to validate
public class ClassToValidate
{
[CustomValidation]
public string ValidStr { get; set; } = "Valid";
[CustomValidation]
public string InvalidStr { get; set; } = "Invalid";
}
Now we can finally create action filter to validate our properties. In the snippet below, we hook into ASP.NET Core pipeline to execute code just before our controller action executes. Here I get action arguments and try to find CustomValidationAttribute on any property. If it is there, grab the value from the property, cast to type (I simply invoke .ToString()) and pass to your service. Based on value that is returned from service, we continue execution or add error to ModelState dictionary.
public class CustomValidationActionFilter : ActionFilterAttribute
{
private readonly SomeService someService;
public CustomValidationActionFilter(SomeService someService)
{
this.someService = someService;
}
public override void OnActionExecuting(ActionExecutingContext context)
{
var actionArguments = context.ActionArguments;
foreach (var actionArgument in actionArguments)
{
var propertiesWithAttributes = actionArgument.Value
.GetType()
.GetProperties()
.Where(x => x.GetCustomAttributes(true).Any(y => y.GetType() == typeof(CustomValidationAttribute)))
.ToList();
foreach (var property in propertiesWithAttributes)
{
var value = property.GetValue(actionArgument.Value).ToString();
if (someService.IsValid(value))
continue;
else
context.ModelState.AddModelError(property.Name, "ModelState is invalid!!!");
}
}
base.OnActionExecuting(context);
}
}
Don't forget to add your filter to the pipeline in Startup.cs!
services.AddMvc(x =>
{
x.Filters.Add(typeof(CustomValidationActionFilter));
});
Update:
If you strictly want to use dependency injection inside attribute, you could use service locator anti-pattern. For that we need to emulate DependencyResolver.Current from ASP.NET MVC
public class CustomValidationAttribute : ValidationAttribute
{
private IServiceProvider serviceProvider;
public CustomValidationAttribute()
{
serviceProvider = AppDependencyResolver.Current.GetService<IServiceProvider>();
}
public override bool IsValid(object value)
{
// scope is required for scoped services
using (var scope = serviceProvider.CreateScope())
{
var service = scope.ServiceProvider.GetService<SomeService>();
return base.IsValid(value);
}
}
}
public class AppDependencyResolver
{
private static AppDependencyResolver _resolver;
public static AppDependencyResolver Current
{
get
{
if (_resolver == null)
throw new Exception("AppDependencyResolver not initialized. You should initialize it in Startup class");
return _resolver;
}
}
public static void Init(IServiceProvider services)
{
_resolver = new AppDependencyResolver(services);
}
private readonly IServiceProvider _serviceProvider;
public object GetService(Type serviceType)
{
return _serviceProvider.GetService(serviceType);
}
public T GetService<T>()
{
return (T)_serviceProvider.GetService(typeof(T));
}
private AppDependencyResolver(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
}
It should be initialized in Startup.cs
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
AppDependencyResolver.Init(app.ApplicationServices);
// other code
}

Changing database at runtime with MVC WebApi 2

I want to change the connection to a database at runtime in a REST Api. I want to put a variable of the request and let the Api decide which connectionstring to use.
For example:
I put the variable "dbid" with the value "develop" in the request header and send it to the Api.
The Api sees the header and gets the correct connectionstring from the web.config.
I have three layers (data, business, api). The data contains EntityFramework to get and set data. Like this:
public class WebsiteContext : IocDbContext, IWebsites
{
public DbSet<Website> Websites { get; set; }
public IEnumerable<Website> GetAll()
{
return Websites.ToList();
}
}
(IoCDbContext.cs)
public class IocDbContext : DbContext, IDbContext
{
public IocDbContext() : base("develop")
{
}
public void ChangeDatabase(string connectionString)
{
Database.Connection.ConnectionString= connectionString;
}
}
In the business I have a class to retrieve data from the datalayer and do some logical stuff (not needed here, but still good for the story).
public class Websites : IWebsites
{
private readonly Data.Interfaces.IWebsites _websiteContext;
#region Constructor
public Websites(Data.Interfaces.IWebsites websiteContext)
{
_websiteContext = websiteContext;
}
#endregion
#region IWebsites implementation
public IEnumerable<Website> GetWebsites()
{
List<Data.Objects.Website> websiteDtos = _websiteContext.GetAll().ToList();
return websiteDtos.Select(web => web.ToModel()).ToList();
}
#endregion
}
public static class WebsiteMapper
{
public static Website ToModel(this Data.Objects.Website value)
{
if (value == null)
return null;
return new Website
{
Id = value.Id,
Name = value.Name
};
}
}
And, last but not least, the controller:
public class WebsiteController : ApiController
{
private readonly IWebsites _websites;
public WebsiteController(IWebsites websites)
{
_websites = websites;
}
public IEnumerable<Website> GetAll()
{
return _websites.GetWebsites().ToList();
}
}
My Unity configuration:
public static void RegisterComponents()
{
var container = new UnityContainer();
container.RegisterType<Business.Interfaces.IWebsites, Websites>();
container.RegisterType<IDbContext, IocDbContext>();
container.RegisterType<IWebsites, WebsiteContext>();
// e.g. container.RegisterType<ITestService, TestService>();
GlobalConfiguration.Configuration.DependencyResolver = new Unity.WebApi.UnityDependencyResolver(container);
DependencyResolver.SetResolver(new UnityDependencyResolver(container));
}
So as you can see the connection string with the name "develop" is used by default. This will return a website with the name "website". Now I would change the header variable "dbid" to "live". The api should see this and should get the connectionstring that corresponds with the name "live". This last part is something I am trying, but nothing works.
This I tried:
Adding session to webapi. This means I break the stateless idea of REST api: not done
Statics cannot work either, because everyone could get the same connectionstring, but its user specific
Google, but most of the examples don't work for me
Searching StackOverflow... See previous point.
This is driving me crazy! There should be a way to change the connectionstring given by a value in a request header, right?
I have the same scenario in a multi-tenant application I created where I use a different connection string for each tenant.
It doesn't matter the implementation you choose, but you have to determine how you are going to differentiate each request per connection string. In my application, I created a custom route value, and used it in the url to differentiate each request. The important thing is to create whatever this mechanism is, and it needs to be the 1st thing you register in your DI framework, on a per request basis.
For example (using Ninject):
private static void RegisterServicdes(IKernel kernel)
{
kernel.Bind<ISiteContext>().To<SiteContext>().InRequestScope();
kernel.Bind<IDbContextFactory>().To<DbContextFactory>().InRequestScope();
// register other services...
}
Rather than your implementation of your DbContext, I would change to be this, then always create your DbContext instance via a DbContextFactory.
public class IocDbContext : DbContext, IDbContext
{
public IocDbContext(string connectionStringType) : base(connectionStringType) { }
}
Then you need to create a DbContextFactory that you use when you create your DbContext, and take the above class as a dependency. Or you can take the dependency into your services, and pass it into the DbContextFactory instead.
public interface IDbContextFactory
{
TestModel CreateContext();
}
public class DbContextFactory : IDbContextFactory
{
private string _siteType;
public DbContextFactory(ISiteContext siteContext)
{
_siteType = siteContext.Tenant;
}
public TestModel CreateContext()
{
return new TestModel(FormatConnectionStringBySiteType(_siteType));
}
// or you can use this if you pass the IMultiTenantHelper dependency into your service
public static TestModel CreateContext(string siteName)
{
return new TestModel(FormatConnectionStringBySiteType(siteName));
}
private static string FormatConnectionStringBySiteType(string siteType)
{
// format from web.config
string newConnectionString = #"data source={0};initial catalog={1};integrated security=True;MultipleActiveResultSets=True;App=EntityFramework";
if (siteType.Equals("a"))
{
return String.Format(newConnectionString, #"(LocalDb)\MSSQLLocalDB", "DbOne");
}
else
{
return String.Format(newConnectionString, #"(LocalDb)\MSSQLLocalDB", "DbTwo");
}
}
}
Then you can use it like so when accessing your DbContext:
public class DbAccess
{
private IDbContextFactory _dbContextFactory;
public DbAccess(IDbContextFactory dbContextFactory)
{
_dbContextFactory = dbContextFactory;
}
public void DoWork()
{
using (IocDbContext db = _dbContextFactory.CreateContext())
{
// use EF here...
}
}
}
ISiteContext interface implementation (for using route).
public interface ISiteContext
{
string Tenant { get; }
}
public class SiteContext : ISiteContext
{
private const string _routeId = "tenantId";
private string _tenant;
public string Tenant { get { return _tenant; } }
public SiteContext()
{
_tenant = GetTenantViaRoute();
}
private string GetTenantViaRoute()
{
var routedata = HttpContext.Current.Request.RequestContext.RouteData;
// Default Routing
if (routedata.Values[_routeId] != null)
{
return routedata.Values[_routeId].ToString().ToLower();
}
// Attribute Routing
if (routedata.Values.ContainsKey("MS_SubRoutes"))
{
var msSubRoutes = routedata.Values["MS_SubRoutes"] as IEnumerable<IHttpRouteData>;
if (msSubRoutes != null && msSubRoutes.Any())
{
var subRoute = msSubRoutes.FirstOrDefault();
if (subRoute != null && subRoute.Values.ContainsKey(_routeId))
{
return (string)subRoute.Values
.Where(x => x.Key.Equals(_routeId))
.Select(x => x.Value)
.Single();
}
}
}
return string.Empty;
}
}
API action:
[Route("api/{tenantId}/Values/Get")]
[HttpGet]
public IEnumerable<string> Get()
{
_testService.DoDatabaseWork();
return new string[] { "value1", "value2" };
}
you need to create a factory class for Dynamic picking of connection string.
It is the responsibility of that class to give correct connectionString based on the certain Parameter.

instantiate ValidatorFactory with unity ioc

I am having some trouble trying to integrate FluentValidation with Unity.
I have a factory class
public class UnityValidatorFactory : FluentValidation.ValidatorFactoryBase
{
private readonly IUnityContainer _container;
public UnityValidatorFactory()
{
_container = null;
}
public UnityValidatorFactory(IUnityContainer container)
{
_container = container;
}
public override FluentValidation.IValidator CreateInstance(Type validatorType)
{
return _container.Resolve(validatorType) as FluentValidation.IValidator;
}
}
The problem is that it won't instantiate in the data class
public class Payment : IValidatableObject
{
private readonly IValidator _validator = new PaymentValidator();
public string paymentType { get; set; }
//etc
public Payment(IValidatorFactory validatorFactory)
{
**//ValidatorFactory is always null!**
_validator = validatorFactory.GetValidator(typeof(Payment));
}
}
This is the code I am trying to use to register the factory class with unity
container.RegisterType<IValidatorFactory, UnityValidatorFactory>(new ContainerControlledLifetimeManager());
However the factory is always null. Any idea what I am doing wrong?
My suggestion would be the next.
Create all your validators and implement the next interface, name it as: IModelValidator
public interface IModelValidator
{
}
//example of a validator class
public class MyModelValidator : AbstractValidator<MyModel>, IModelValidator
{
public MyModelValidator ()
{
CascadeMode = CascadeMode.StopOnFirstFailure;
RuleFor(x => x.yourField)
.NotNull()
.NotEmpty()
.WithMessage("You need to enter yourField");
}
}
//this is to register all your interfaces on the container
void RegisterValidators(IUnityContainer container)
{
var type = typeof(IValidator<>);
var validators = AssemblyScanner.FindValidatorsInAssemblyContaining<IModelValidator>();
validators.ForEach(validator => container.RegisterType(validator.InterfaceType, validator.ValidatorType));
}
//Integrating with MVC, this should be do in your Global.asax when you create your container
public static ConfigureFluentValidation(IUnityContainer container)
{
var fluentValidationModelValidatorProvider = new FluentValidationModelValidatorProvider(new UnityValidationFactory(container));
//disables the implicit required validator being added for both the DataAnnotationsModelProvider, as well as the FluentValidationModelValidatorProvider.
DataAnnotationsModelValidatorProvider.AddImplicitRequiredAttributeForValueTypes = false;
fluentValidationModelValidatorProvider.AddImplicitRequiredValidator = false;
//adds the new model validator provider to the list of model validator providers used by the MVC framework.
ModelValidatorProviders.Providers.Add(fluentValidationModelValidatorProvider);
}
Now in your controller method when you post the information to be validated you will see if you leave yourField empty that ModelState.IsValid has a false value

Categories

Resources