ASP.NET Core database configuration with dependency injection issue - c#

I ran in to a problem 6 days ago and i still can't figure it out.
I'm setting up a DBContext using DI:
public void ConfigureServices(IServiceCollection services)
{
// Add framework services.
services.AddDbContext<ApplicationContext>(options =>
options.UseSqlite(Configuration.GetConnectionString("DefaultConnection")));
services.AddMvc();
// Add application services.
services.AddTransient<IEmailSender, AuthMessageSender>();
services.AddTransient<ISmsSender, AuthMessageSender>();
}
The database file path is configurered in the appsettings.json
{
"ConnectionStrings": {
"DefaultConnection": "Data Source=xxxx.db"
},
"Logging": {
"IncludeScopes": false,
"LogLevel": {
"Default": "Debug",
"System": "Information",
"Microsoft": "Information"
}
}
}
And I'm creating a DBContext like this (https://learn.microsoft.com/en-us/ef/core/miscellaneous/configuring-dbcontext)
public class ApplicationContext : DbContext
{
public DbSet<Network> xxxx { get; set; }
public DbSet<NetworkEntry> xxxx { get; set; }
public ApplicationContext(DbContextOptions<ApplicationContext> options)
: base(options)
{ }
}
My controller code:
public JsonResult Index(ApplicationContext db)
{
return Json(db.Networks);
}
It compiles and runs. If I use the other way of configuring the DB, it works 100% problem free:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlite("Filename=./blog.db");
}
The problem I'm having:
System.InvalidOperationException: Could not create a model binder for model object of type 'xxx.Data.ApplicationContext'.
at Microsoft.AspNetCore.Mvc.ModelBinding.ModelBinderFactory.CreateBinder(ModelBinderFactoryContext context)
at Microsoft.AspNetCore.Mvc.Internal.DefaultControllerArgumentBinder.<BindModelAsync>d__8.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNetCore.Mvc.Internal.DefaultControllerArgumentBinder.<BindArgumentsCoreAsync>d__6.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.<InvokeNextResourceFilter>d__22.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.Rethrow(ResourceExecutedContext context)
at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.<InvokeAsync>d__20.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNetCore.Builder.RouterMiddleware.<Invoke>d__4.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.MigrationsEndPointMiddleware.<Invoke>d__5.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.DatabaseErrorPageMiddleware.<Invoke>d__6.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.DatabaseErrorPageMiddleware.<Invoke>d__6.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.<Invoke>d__7.MoveNext()
Any clues?

I have just solved a similar problem. The problem for me was that I'd misunderstood the dependency injection. The context doesn't get passed to the Controller method, but to the Controller's constructor.
public class NetworksController : Controller
{
private readonly ChoirAdminContext db;
public NetworksController(ApplicationContext context)
{
db = context;
}
public JsonResult Index()
{
return Json(db.Networks);
}
}

Related

Passing collection in querystring does not work with ODataQueryOptions

I have following existing GET endpoint
[HttpGet]
public async Task<IHttpActionResult> Get(ODataQueryOptions<Section> oDataOptions) {
//code here
return response;
}
I need to add a collection in querystring like <basequery>?$filter=id eq 1&includeCategory=A&includeCategory=B
so this would be bound as a collection in modified get endpoint as follows -
[HttpGet]
public async Task<IHttpActionResult> Get([FromUri (Name = "includeCategory"] ) List<Category> categories, ODataQueryOptions<Section> oDataOptions) {
//code here
return response;
}
Public enum Category {
A,
B
}
If I make postman request to this, I am getting An item with the same key has already been added.
System.ArgumentException: An item with the same key has already been added.
at System.ThrowHelper.ThrowArgumentException(ExceptionResource resource)
at System.Collections.Generic.Dictionary`2.Insert(TKey key, TValue value, Boolean add)
at System.Linq.Enumerable.ToDictionary[TSource,TKey,TElement](IEnumerable`1 source, Func`2 keySelector, Func`2 elementSelector, IEqualityComparer`1 comparer)
at System.Web.OData.Query.ODataQueryOptions..ctor(ODataQueryContext context, HttpRequestMessage request)
at System.Web.OData.Query.ODataQueryOptions`1..ctor(ODataQueryContext context, HttpRequestMessage request)
at System.Web.OData.ODataQueryParameterBindingAttribute.ODataQueryParameterBinding.CreateODataQueryOptions[T](ODataQueryContext context, HttpRequestMessage request)
at System.Web.OData.ODataQueryParameterBindingAttribute.ODataQueryParameterBinding.ExecuteBindingAsync(ModelMetadataProvider metadataProvider, HttpActionContext actionContext, CancellationToken cancellationToken)
at System.Web.Http.Controllers.HttpActionBinding.<ExecuteBindingAsyncCore>d__0.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Web.Http.Controllers.ActionFilterResult.<ExecuteAsync>d__2.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Web.Http.Filters.AuthorizationFilterAttribute.<ExecuteAuthorizationFilterAsyncCore>d__2.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Web.Http.Dispatcher.HttpControllerDispatcher.<SendAsync>d__1.MoveNext()
If my querystring has this <basequery>?$filter=id eq 1&includeCategory=A, this is working fine with ODataQueryOptions.
If my querystring has this <basequery>?$filter=id eq 1&includeCategory[0]=A&includeCategory[0]=B, this is working fine as well with ODataQueryOptions .
Same works for following with url <basequery>/1?includeCategory=A&includeCategory=B and endpoint with no $filter
[Route("{id}")
[HttpGet]
public async Task<IHttpActionResult> Get([FromUri (Name = "includeCategory"] ) List<Category> categories, long id) {
//code here
return response;
}
There is nothing in Section class that might collide with name Category.
That's why I am suspecting it's something to do with $filer, please suggest.

Validating Model Properties but allow null values

I am trying to get a model property validation to work but allowing a null value in a string property.
The Property i'm trying to validate is a:
public string PhoneNumber { get; set;}
And i am validating it like this:
[Phone(ErrorMessage = "Invalid telephone number.")]
public string PhoneNumber { get; set;}
The validation works great in my case except for when a value is not sent in for Phone number, to the api with an object.
is there any thing like a: [AllowNullValue] attribute or how do i get null values pass the "Phone" attribute?
UPDATE (Anton Gorbunov's post):
{
"Message": "An error has occurred.",
"ExceptionMessage": "The field is not a valid phone number.",
"ExceptionType": "System.ComponentModel.DataAnnotations.ValidationException",
"StackTrace": " at System.ComponentModel.DataAnnotations.ValidationAttribute.Validate(Object value, String name)\r\n at RABE_BCV_API.Controllers.APIController.UpsertMember(MemberModel memberObject) in C:\\Users\\John\\Documents\\Visual Studio 2015\\Projects\\RABE_BCV_API\\RABE_BCV_API\\Controllers\\APIController.cs:line 29\r\n at lambda_method(Closure , Object , Object[] )\r\n at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ActionExecutor.<>c__DisplayClass10.<GetExecutor>b__9(Object instance, Object[] methodParameters)\r\n at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ActionExecutor.Execute(Object instance, Object[] arguments)\r\n at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ExecuteAsync(HttpControllerContext controllerContext, IDictionary`2 arguments, CancellationToken cancellationToken)\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at System.Web.Http.Controllers.ApiControllerActionInvoker.<InvokeActionAsyncCore>d__0.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at System.Web.Http.Controllers.ActionFilterResult.<ExecuteAsync>d__2.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at System.Web.Http.Dispatcher.HttpControllerDispatcher.<SendAsync>d__1.MoveNext()"
}
It looks strange, because is the first instruction of IsValid Metod in PhoneAttribute is:
public override bool IsValid(object value) {
if (value == null) {
return true;
}
...
}
Maybe you use [Required] or [BindRequired] attributes?

Registrations not resolving

The auto-resolving of the .Net Core framework is not finding my registrations inside DryIoC.
This using the new .Net Core 2 framework, DryIoC 2.10.7 and DryIoc.Microsoft.DependencyInjection 1.2.2. I cannot update to DryIoC 2.11.7 because the DryIoc.Microsoft.DependencyInjection does not pick up the latest version. I did attempt to re-install the latter after the 2.11.7 install but that did not work (ambiguous references).
Here is my startup code:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc()
.AddControllersAsServices();
var container = new Container(rules => rules.With(propertiesAndFields: PropertiesAndFields.Auto))
.WithDependencyInjectionAdapter(services);
container.ConfigureServiceProvider<CompositionRoot>();
}
And here are my registration code:
public CompositionRoot(IRegistrator registrator, IContainer container)
{
//System Clock
registrator.Register<IClock, ConcreteClock>(Reuse.Singleton, null, null, IfAlreadyRegistered.Keep);
container.RegisterInstance<ISystemConfiguration>(new Configuration(container.Resolve<IClock>()), Reuse.Singleton, IfAlreadyRegistered.Keep);
//Logging
container.RegisterInstance<ILogging>(CreateLogger(container.Resolve<ISystemConfiguration>()), Reuse.Singleton, IfAlreadyRegistered.Keep);
}
Finally the controller:
public class AdminController : Controller
{
private readonly ILogging _log;
private readonly IClock _clock;
public AdminController(ILogging log,
IClock clock)
{
_log = log;
_clock = clock;
}
}
Here is the exception:
System.InvalidOperationException: Unable to resolve service for type
'SharedAssets.Interfaces.ILogging' while attempting to activate
'DataDictionaryService.Controllers.AdminController'. at
Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.CreateArgumentCallSites(Type
serviceType, Type implementationType, ISet1 callSiteChain,
ParameterInfo[] parameters, Boolean throwIfCallSiteNotFound) at
Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.CreateConstructorCallSite(Type
serviceType, Type implementationType, ISet1 callSiteChain) at
Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.TryCreateExact(ServiceDescriptor
descriptor, Type serviceType, ISet1 callSiteChain) at
Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.TryCreateExact(Type
serviceType, ISet1 callSiteChain) at
Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.CreateCallSite(Type
serviceType, ISet1 callSiteChain) at
Microsoft.Extensions.DependencyInjection.ServiceProvider.CreateServiceAccessor(Type
serviceType, ServiceProvider serviceProvider) at
System.Collections.Concurrent.ConcurrentDictionaryExtensions.GetOrAdd[TKey,TValue,TArg](ConcurrentDictionary2
dictionary, TKey key, Func`3 valueFactory, TArg arg) at
Microsoft.Extensions.DependencyInjection.ServiceProvider.GetService(Type
serviceType) at
Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(IServiceProvider
provider, Type serviceType) at
Microsoft.AspNetCore.Mvc.Controllers.ServiceBasedControllerActivator.Create(ControllerContext
actionContext) at
Microsoft.AspNetCore.Mvc.Controllers.ControllerFactoryProvider.<>c__DisplayClass5_0.g__CreateController0(ControllerContext
controllerContext) at
Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.Next(State&
next, Scope& scope, Object& state, Boolean& isCompleted) at
Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.d__14.MoveNext()
--- End of stack trace from previous location where exception was thrown --- at
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task
task) at
Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.d__22.MoveNext()
--- End of stack trace from previous location where exception was thrown --- at
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at
Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.Rethrow(ResourceExecutedContext
context) at
Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.Next(State& next,
Scope& scope, Object& state, Boolean& isCompleted) at
Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.d__17.MoveNext()
--- End of stack trace from previous location where exception was thrown --- at
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task
task) at
Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.d__15.MoveNext()
--- End of stack trace from previous location where exception was thrown --- at
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task
task) at
Microsoft.AspNetCore.Builder.RouterMiddleware.d__4.MoveNext()
--- End of stack trace from previous location where exception was thrown --- at
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task
task) at
Microsoft.AspNetCore.Cors.Infrastructure.CorsMiddleware.d__7.MoveNext()
--- End of stack trace from previous location where exception was thrown --- at
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task
task) at
Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.d__7.MoveNext()
Thanks!
When using custom DI frameworks you need to update the ConfigureServices to return your custom IServiceProvider.
public IServiceProvider ConfigureServices(IServiceCollection services) {
services.AddMvc()
.AddControllersAsServices();
var container = new Container(rules => rules.With(propertiesAndFields: PropertiesAndFields.Auto))
.WithDependencyInjectionAdapter(services);
var provider = container.ConfigureServiceProvider<CompositionRoot>();
return provider
}
At runtime, your provider will be used to resolve types and inject dependencies.
Reference:
Introduction to Dependency Injection in ASP.NET Core: Replacing the default services container

Structuremap - setup dependency which has dependency

I'm using structure map as my IOC with web api and I have an injected dependency in my controller and it's concrete type also has a dependecy.
Controller
[RoutePrefix("api/products")]
public class ProductsController : BaseApiController
{
//private readonly ProductRepository _manageProducts;
private readonly IProductFactory _productFactory;
private readonly IGenericRepository _genericRepository;
public ProductsController(IProductFactory productFactory, IGenericRepository genericRepository)
{
_productFactory = productFactory;
_genericRepository = genericRepository;
//_manageProducts = new ProductRepository();
}
[Authorize]
[Route("addProduct")]
public IHttpActionResult AddNewProduct(ProductViewModels.AddProductViewModel product)
{
if (User.IsInRole("Admin"))
{
_productFactory.CreateProduct(product);
return Ok("Product Successfully Added");
}
return BadRequest("Your must have Administrator rights to perform the operation.");
}
}
Factory
public class ProductFactory : IProductFactory
{
private readonly IGenericRepository _genericRepository;
public ProductFactory(IGenericRepository genericRepository)
{
_genericRepository = genericRepository;
}
/// <summary>
/// Creates the product.
/// </summary>
/// <returns>The product.</returns>
/// <param name="viewModel">New product.</param>
public Product CreateProduct(ProductViewModels.AddProductViewModel viewModel)
{
var productToBeAdded = new Product
{
Title = viewModel.Title,
ISBN = viewModel.ISBN,
};
return productToBeAdded;
}
}
When I try to call product controller addproducts I get this runtime error for null reference exception:
{
"Message": "An error has occurred.",
"ExceptionMessage": "Object reference not set to an instance of an object.",
"ExceptionType": "System.NullReferenceException",
"StackTrace": " at ICEBookshop.API.Factories.ProductFactory.CreateProduct(AddProductViewModel viewModel) in C:\\Users\\GOWDY_N\\Source\\Repos\\ICEBookshop.API\\ICEBookshop.API\\P00603ClientApi\\Factories\\ProductFactory.cs:line 29\r\n at ICEBookshop.API.Controllers.ProductsController.AddNewProduct(AddProductViewModel product) in C:\\Users\\GOWDY_N\\Source\\Repos\\ICEBookshop.API\\ICEBookshop.API\\P00603ClientApi\\Controllers\\ProductsController.cs:line 95\r\n at lambda_method(Closure , Object , Object[] )\r\n at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ActionExecutor.<>c__DisplayClass10.<GetExecutor>b__9(Object instance, Object[] methodParameters)\r\n at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ExecuteAsync(HttpControllerContext controllerContext, IDictionary`2 arguments, CancellationToken cancellationToken)\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at System.Web.Http.Controllers.ApiControllerActionInvoker.<InvokeActionAsyncCore>d__0.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at System.Web.Http.Controllers.ActionFilterResult.<ExecuteAsync>d__2.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at System.Web.Http.Filters.AuthorizationFilterAttribute.<ExecuteAuthorizationFilterAsyncCore>d__2.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at System.Web.Http.Dispatcher.HttpControllerDispatcher.<SendAsync>d__1.MoveNext()"
}
This is what I've done with structuremap
public class DefaultRegistry : Registry
{
#region Constructors and Destructors
public DefaultRegistry()
{
Scan(
scan =>
{
scan.TheCallingAssembly();
scan.WithDefaultConventions();
});
For<IGenericRepository>().Use<GenericRepository<ApplicationDbContext>>();
For<IProductFactory>()
.Use<ProductFactory>()
.Ctor<IGenericRepository>()
.Is<GenericRepository<ApplicationDbContext>>().Named("DefaultInstanceKey");
#endregion
}
}
I thought this would fix it so it knows how to resolve my factory:
For<IProductFactory>()
.Use<ProductFactory>()
.Ctor<IGenericRepository>()
.Is<GenericRepository<ApplicationDbContext>>().Named("DefaultInstanceKey");
But it doesn't work neither. Does anyone know how to fix this?
Just register the two interfaces and their implementations. The framework will resolve the dependencies when resolving the target.
For<IGenericRepository>().Use<GenericRepository<ApplicationDbContext>>();
For<IProductFactory>().Use<ProductFactory>();

Using ActionFilterAttribute for model validation

I am trying to use ActionFilterAttribute to validate the model when a call is made to my Web API. I have added the following
public class ValidateModelStateAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(HttpActionContext actionContext)
{
if (!actionContext.ModelState.IsValid)
{
actionContext.Response = actionContext.Request.CreateErrorResponse(
HttpStatusCode.BadRequest, actionContext.ModelState);
}
}
}
I have added standard validators to my model properties and it all works fine. However when I add a custom class level validator it fails with the following error.
A validation attribute of type XXXValidatorAttribute cannot be used to
validate values."
StackTrace: at
Microsoft.Practices.EnterpriseLibrary.Validation.Validators.BaseValidationAttribute.IsValid(Object
value)\r\n at
System.ComponentModel.DataAnnotations.ValidationAttribute.IsValid(Object
value, ValidationContext validationContext)\r\n at
System.ComponentModel.DataAnnotations.ValidationAttribute.GetValidationResult(Object
value, ValidationContext validationContext)\r\n at
System.Web.Http.Validation.Validators.DataAnnotationsModelValidator.Validate(ModelMetadata
metadata, Object container)\r\n at
System.Web.Http.Validation.DefaultBodyModelValidator.ShallowValidate(ModelMetadata
metadata, ValidationContext validationContext, Object container,
IEnumerable'1 validators)\r\n at
System.Web.Http.Validation.DefaultBodyModelValidator.ValidateNodeAndChildren(ModelMetadata
metadata, ValidationContext validationContext, Object container,
IEnumerable'1 validators)\r\n at
System.Web.Http.Validation.DefaultBodyModelValidator.Validate(Object
model, Type type, ModelMetadataProvider metadataProvider,
HttpActionContext actionContext, String keyPrefix)\r\n at
System.Web.Http.ModelBinding.FormatterParameterBinding.d__0.MoveNext()\r\n---
End of stack trace from previous location where exception was thrown
---\r\n at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task
task)\r\n at
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task
task)\r\n at
System.Runtime.CompilerServices.TaskAwaiter.GetResult()\r\n at
System.Web.Http.Controllers.HttpActionBinding.d__0.MoveNext()\r\n---
End of stack trace from previous location where exception was thrown
---\r\n at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task
task)\r\n at
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task
task)\r\n at
System.Runtime.CompilerServices.TaskAwaiter.GetResult()\r\n at
System.Web.Http.Controllers.ActionFilterResult.d__2.MoveNext()\r\n---
End of stack trace from previous location where exception was thrown
---\r\n at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task
task)\r\n at
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task
task)\r\n at
System.Runtime.CompilerServices.TaskAwaiter'1.GetResult()\r\n at
System.Web.Http.Controllers.AuthenticationFilterResult.d__0.MoveNext()\r\n---
End of stack trace from previous location where exception was thrown
---\r\n at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task
task)\r\n at
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task
task)\r\n at
System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()\r\n at
System.Web.Http.Dispatcher.HttpControllerDispatcher.d__1.MoveNext()
This validator works fine when I invoke it via a unit test using a validation factory. I am not sure why it fails during ModelState validation? What is the difference between the two?
I have the model defined as
I have a model defined as
[CashDetailValidator()]
public sealed class CashDetails : DetailsBase
{
// Reference Number
[ValidatorComposition(CompositionType.Or)]
[NotNullValidator(Negated=true)]
[StringLengthValidator(1, RangeBoundaryType.Inclusive, 16, RangeBoundaryType.Exclusive, MessageTemplate = "Reference number should be between 1 to 16 characters in length.")]
public string RelatedReference { get; set; } ...........................
}}
The composition validator also gives the same error.

Categories

Resources