From what I can tell, ASP.Net Core performs model state validation before calling the relevant controller action method. This means that code in the action method isn't given an opportunity to add data to the model before it is validated.
What is the ASP.Net Core way of giving a view model access to additional, non-user-submitted data prior to validation?
Example
What I'm trying to do (doesn't work).
The view model's Validate method expects data to be in ValidOptions. However, since validation occurs before the controller can set this property, validation causes the view model to throw an ArgumentNullException.
// From the Controller
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Process([Bind("SelectedId")]ViewModels.Import details)
{
// data needed for validation
details.ValidOptions = await service.ImportTypes.ToListAsync();
if (ModelState.ValidationState != ModelValidationState.Valid) {
// ...
}
}
// From ViewModels.Import
public IEnumerable<Option> ValidOptions { get; set; }
public int SelectdId {get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
// throws ArgumentNullException because ValidOptions hasn't been set when this is executed
var option = ValidOptions.Single(t => t.Id == SelectdId);
//...
}
Probably many ways to skin a cat here. But the easiest for you is probably custom model binders. It's a way to "supplement" or change the binding of your model before it hits the controller. I will say that some see it as extremely bad practice to call an external service/repository at the point of model binding, but it does work and can come in handy.
You need to implement a class that inherits from IModelBinder.
public class MyViewModelBinder : IModelBinder
{
public Task BindModelAsync(ModelBindingContext bindingContext)
{
//Bind here. Including calling external services if you want.
}
}
Then you need to implement a provider, this essentially says "when" to bind.
public class MyViewModelBinderProvider : IModelBinderProvider
{
public IModelBinder GetBinder(ModelBinderProviderContext context)
{
if (context.Metadata.ModelType == typeof(MyViewModel))
return new MyViewModelBinder();
return null;
}
}
In your configure method of your startup.cs, you need to add the provider to the ModelBinderProviders list.
public void ConfigureServices(IServiceCollection services)
{
// Add framework services.
services.AddMvc(config =>
config.ModelBinderProviders.Add(new MyViewModelBinderProvider())
);
}
Further Documentation :
http://dotnetcoretutorials.com/2016/12/28/custom-model-binders-asp-net-core/
http://intellitect.com/custom-model-binding-in-asp-net-core-1-0/
I don't think the official documentation has an article on custom model binders yet unfortunately.
Related
Using .NET Framework MVC, one would register a custom model binder like so:
ModelBinders.Binders.Add(typeof(MyModel), new MyModelBinder());
Then any controller action that had a parameter of type MyModel would automatically use MyModelBinder to bind its value.
public ActionResult Test(MyModel o){
// dunski!
...
}
It seems like in .NET Core MVC, one must specify the use of MyModelBinder each time rather than registering it once only - or am I mistaken?
public IActionResult Test([ModelBinder(BinderType = typeof(MyModelBinder))] MyModel o){
...
}
Decorating the type that is being model-bound with the [ModelBinder] attribute will cause any parameter of the same type (in your controller actions) to be automatically bound using the model binder specified in the [ModelBinder] attribute.
Example:
[ModelBinder(BinderType = typeof(MyModelBinder))]
public class MyModel{
public string Name{ get; set; }
public string Age{ get; set; }
}
public class MyModelBinder : IModelBinder{
public Task BindModelAsync(ModelBindingContext bindingContext)
{
// whatever model binding you need to do
bindingContext.Result = ModelBindingResult.Success(new MyModel());
return Task.CompletedTask;
}
}
More on this at official documentation
I am converting from the old ways of ASP.NET Web Forms to ASP.NET MVC. I have a project that I am working on that has about 40-50 tables in the database. I have decided to use Entity Framework as my data access layer. I have also decided to put a repository layer and unit of work abstraction over EF so that I am not tied to it and so that I can do unit testing. Finally, I want to make my controllers "thin" so I am looking at implementing a business "service" layer for my business logic.
The thing I am struggling with is how do I propagate Business Logic Errors from my service layer to my Presentation UI layer so that an appropriate error can be shown? Please note that I am trying to look for a solution that is NOT MVC specific as this service/business logic layer will likely be used in other things besides an MVC app (console app's, web services, etc.)
On to some code...
Lets say I have a POCO / data / domain model like so:
public class Category
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public bool IsActive { get; set; }
// other properties (navigation, etc)...
}
An Entity Framework fluent configuration/mapping class like so:
public class CategoryMap : EntityTypeConfiguration<Category>
{
public CategoryMap()
{
this.HasKey(c => c.Id);
this.Property(c => c.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity); // auto increment identity in our DB schema
this.Property(c=> c.Name)
.IsRequired() // defined as NOT NULL in DB schema so we put a constraint here
.HasMaxLength(150); // defined as varchar(150) in DB schema so we put a constraint here
this.Property(c=> c.Description)
.IsRequired(); // defined as NOT NULL in DB schema so we put a constraint here
// fluent config for related entities (navigation properties) would go here...
}
}
A unit of work encapsulating multiple repositories like so:
public class UnitOfWork : IUnitOfWork
{
private readonly MyDbContext context;
private CategoryRepository catRepo;
public UnitOfWork()
{
this.context = new MyDbContext();
}
public ICategoryRepository Categories
{
get { return this.catRepo?? (this.catRepo= new CategoryRepository (this.context)); }
}
}
A service / business logic layer like so:
public class CategoryService : ICategoryService
{
private readonly IUnitOfWork unitOfWork;
public CategoryService(IUnitOfWork uow) // injected by IoC
{
this.unitOfWork = uow;
}
public Category CreateNewCategory(Category category)
{
if (category == null)
{
throw new ArgumentNullException("category cannot be null");
}
// Simple business logic here to make sure another category with this name does not already exist.
int count = this.unitOfWork.Categories.Count(cat => cat.Name == category.Name);
if (count > 0)
{
// *** This is the error I want the user to see in the UI ***
throw new Exception("Sorry - a category with that name already exists!");
}
}
}
And a controller like this:
public ManageCategoriesController : Controller
{
ICategoryService catSvc;
public ManageCategoriesController(ICategoryService svc) // injected by IoC
{
this.catSvc = svc;
}
[HttpPost]
public ActionResult(CategoryCreateModel createModel) // my View Models / Create Models have Data Annotations on them
{
if (ModelState.IsValid)
{
// use of AutoMapper to map from View Model to domain model...
Category cat = Mapper.Map<CategoryCreateModel , Category>(createModel);
this.catSvc.CreateNewCategory(cat); // ***need to get potential errors from Service and display on form.***
return this.RedirectToAction("Index");
}
}
}
First of all, can anybody tell me if I am on the right track with using View Models? I feel like I almost have three View Models (Create, Edit, View/List) per domain model.
Secondly, my EF configuration/mapping class takes care of the database constraints. Some of these constraints (e.g. Max length) are also data annotations in the View Models and can easily be displayed on the UI. But where can I show my custom business logic errors?
First, your overall approach to MVC looks good to me :-)
Second, you most likely want to use DataAnnotation on your view models for model validation. Have a look this blog post for a good intro on using it in ASP.MVC.
In case of custom validation not suitable for data annotation you can do the following in your controller:
try
{
// the following exception could be thown by some nested validation logic
// e.g. while processing a post request
throw new ValidationException("the error description");
}
catch (ValidationException exception)
{
ModelState.AddModelError("", exception.Message);
}
This is a pretty old question, but for future readers I'd like to add something.
If you're actually using a N-Tier pattern, entity validation should be in your Service layer. Not in your MVC Controller.
The right way to do it is to do basic model validations in your model class, using ValidationAttributes, but re-validate your entities in your service layer.
Add a handling of custom exceptions in your controller to catch any validation error raised from the service layer, and display error messages.
If your service layer is just there to call your repositories, you're doing something wrong ;)
I am creating an ASP.NET MVC3 restful web service to allow reports to be uploaded from a set of servers. When a new report is created, I want the client app to do a PUT to
http://MyApp/Servers/[ServerName]/Reports/[ReportTime]
passing the content of the report as XML in the body of the request.
My question is: how do I access the content of the report in my controller? I would imagine that it is available somewhere in the HttpContext.Request object but I am reluctant to access that from my controller as it is not possible(?) to unit test that. Is it possible to tweak the routing to allow the content to be passed as one or more parameters into the controller method? The outcome needs to be RESTful, i.e. it has to PUT or POST to a URL like the one above.
Currently my routing is:
routes.MapRoute(
"SaveReport",
"Servers/{serverName}/Reports/{reportTime",
new { controller = "Reports", action = "Put" },
new { httpMethod = new HttpMethodConstraint("PUT") });
Is there any way to modify this to pass content from the HTTP request body into the controller method?
The controller method is currently:
public class ReportsController : Controller
{
[HttpPut]
public ActionResult Put(string serverName, string reportTime)
{
// Code here to decode and save the report
}
}
The object I am trying to PUT to the URL is:
public class Report
{
public int SuccessCount { get; set; }
public int FailureOneCount { get; set; }
public int FailureTwoCount { get; set; }
// Other stuff
}
This question looks similar but doesn't have any answer.
Thanks in advance
Seems like you just need to use the standard ASP.NET MVC model binding capability with the slight wrinkle that you would doing an HTTP PUT instead of the more common HTTP POST. This article series has some good samples to see how model binding is used.
Your controller code would then look like:
public class ReportsController : Controller
{
[HttpPut]
public ActionResult Put(Report report, string serverName, string reportTime)
{
if (ModelState.IsValid)
{
// Do biz logic and return appropriate view
}
else
{
// Return invalid request handling "view"
}
}
}
EDIT: ====================>>>
Jon added this code to his comment as part of the fix so I added it to the answer for others:
Create a custom ModelBinder:
public class ReportModelBinder : IModelBinder
{
public object BindModel(
ControllerContext controllerContext,
ModelBindingContext bindingContext)
{
var xs = new XmlSerializer(typeof(Report));
return (Report)xs.Deserialize(
controllerContext.HttpContext.Request.InputStream);
}
}
Modify the Global.asax.cs to register this model binder against the Report type:
ModelBinders.Binders[typeof(Report)] = new Models.ReportModelBinder();
We have quite a few validation methods that need to access repositories / database to do their work. So far we have been using the service locator pattern (albeit sparingly) to accomplish this in custom ValidationAttributes:
public override bool IsValid(object value)
{
// use custom service locator in our app's infrastructure
var repos = DependencyInjector.Current.GetService<IXyzRepository>();
...
}
I know this is :( upon as an anti-pattern, and we would like to use a more correct approach. We use unity, and I read this post that says to use a build-up method. However the link in the accepted answer says that the documentation is outdated (retired content).
The solution does not need to use a validation attribute, I suppose it could use IValidatableObject, however the problem remains: how to inject the dependency into the model. Do we need a custom model binder to do this?
Another solution would be to perform the validation in the controller, where dependency injection is easy. To me this feels cluttered though. I would like the model to be validated by the time it gets to the action method.
Also we sometimes use [RemoteAttribute] to perform some of these validations on the client. Currently those methods construct a viewmodel and delegate validation to the model by using the static Validator.TryValidateObject method.
How have you accomplished validation that requires an injected dependency to do its work, without using the SL anti-pattern?
How have you accomplished validation that requires an injected
dependency to do its work, without using the SL anti-pattern?
I use FluentValidation.NET to perform validation in my applications. It allows me to inject dependencies into my validators. It has a really nice integration with ASP.NET MVC. It also supports automatic client side validation for the standard rules just the same way as data annotations using jquery unobtrusive validate:
NotNull/NotEmpty
Matches (regex)
InclusiveBetween (range)
CreditCard
Email
EqualTo (cross-property equality comparison)
Length
I have never used data annotations to perform validation. They are absolutely useless when you need to handle some more complex validation scenarios where you need to validate dependent properties and even use some service. I put complex in italics in the previous sentence because, I don't think that validating that one of the 2 properties is required is a really complex validation scenario and yet, just checkout the amount of infrastructure crap you have to write in order to implement it using data annotations. Looking at this code you no longer know what you are validating.
Inject your validation into your Model.
Validation Attributes can become awkward to work with when your validation stories become more complex. Yuck!
I like to use Entity Framework with Code First. I have full control of my model at that point. I also use FluentValidation like #Darin Dimitrov and I really like its ease of use and simple syntax.
Here’s how you put it together.
I assume you have assembly with your interfaces or contracts.
This will be the base interface for your models…
using System.ComponentModel;
using FluentValidation.Results;
public interface IAbstractBase : IDataErrorInfo
{
bool IsValid { get; }
ValidationResult SelfValidate();
}
and its counterpart in your business layer looks like this…
using System;
using System.Linq;
using FluentValidation.Results;
using Contracts;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
public abstract class AbstractBase : IAbstractBase
{
#region IDataErrorInfo
public abstract ValidationResult SelfValidate();
[NotMapped]
public bool IsValid
{
get
{
return SelfValidate().IsValid;
}
}
[NotMapped]
public string Error
{
get
{
var results = SelfValidate().Errors.Select(s => string.Format("● {0}{1}", s.ErrorMessage, Environment.NewLine)).ToArray();
return string.Join("", results);
}
}
[NotMapped]
public IList<ValidationFailure> Errors
{
get
{
var results = SelfValidate().Errors;
return results;
}
}
[NotMapped]
public string this[string columnName]
{
get
{
var validationResults = SelfValidate();
if (validationResults == null) return string.Empty;
var columnResults = validationResults.Errors.FirstOrDefault(x => string.Compare(x.PropertyName, columnName, true) == 0);
return columnResults != null ? columnResults.ErrorMessage : string.Empty;
}
}
#endregion
}
This is your base class for your models. Make sure you implement the abstract method in your models. It should look like this.
public class MyModel : AbstractBase, IMyModel
{
private AbstractValidator<IMyModelValidator> _myModelValidator;
public MyModel():this(new MyModelValidator()){};
public MyModel(AbstractValidator<IMyModelValidator> myModelValidator){
_myModelValidator = myModelValidator;
};
public int MyModelId { get; set; }
public string Name { get; set; }
public DateTime CreatedDate { get; set; }
public override ValidationResult SelfValidate()
{
return _myModelValidator.Validate(this);
}
}
Your validator class will look something like this.
public class MyModelValidator : AbstractValidator<IMyModelValidator>
{
private IMyModelProvider _myModelProvider;
public MyModelValidator(IMyModelProvider myModelProvider){ _myModelProvider = myModelProvider;};
private void SetRules()
{
RuleFor(x => x.Name).NotEmpty().WithMessage("Please specify a project name.");
RuleFor(x => x.Name.Length).LessThanOrEqualTo(100).WithMessage("The project name must be less than or equal to 100 characters.");
}
public override ValidationResult Validate(IMyModel instance)
{
SetRules();
return base.Validate(instance);
}
}
Pass your validation results from your Model to your view in your Controller using the following call in your controller.
TryValidateModel(your model here);
After you call this in your controller call your model.IsValid property.
Make sure you register everything and you should be good to go. I assume you can fill in the missing pieces.
The big picture looks like this:
When a view model is created you can populate the options (e.g. used in a dropdown list) into a setter property of the view model.
The problem is that when that view model is later passed as a parameter (by the framework!) into an action method, those property values has not become automagically
repopulated, so if you need to redisplay the form because of validation errors, you need to repopulate those options again.
One potential solution, which I am asking for specifically in this question, is how to make the MVC framework instantiate the view model with constructor injection, which would provide the view model constructor with an implementation of some kind of data access object (e.g. a repository) that can be used for retrieving the options when they are requested by the view (e.g. in the helper method "DropDownListFor") ?
I think the solution might have something to do with implementations of IModelBinderProvider or IModelBinder but after having experimented with these things from example code snippets here and there on the net, I am still looking for a completely working example, with downloadable executable code without any missing piece of how putting all things together.
If you are looking for some alternative discussion about how to populate a select list, e.g. with "Dependecy Lookup" instead of "Dependecy Injection" you may want to check out the following discussion:
Best way to populate SelectList for ViewModel on GET/POST
Best way to populate SelectList for ViewModel on GET/POST
Some days ago I wrote the following follow-up-question in that thread about the "Dependecy Injection" I am now looking for in this thread:
https://stackoverflow.com/a/8674525/310457
(which provides a code example about the problem I am looking for a solution of)
But instead of hoping that someone will find that old thread with a less specific title, I have created this new question with a more specific subject about what I am looking for.
And I will also provide a link from that thread into this new question for anyone that want to follow-up regarding this specific solution I am looking for.
I'm assuming you want to have your ViewModels automatically injected with something via their Constructor - for example some kind of configuration object that the View will use to determine what to show. I'm also assuming that this approach is causing a "No parameterless constructor defined for this object" error when MVC tries to automatically create and bind a model instance, from the arguments of your Controller Action. Let's also then assume that we will use a DI framework to inject the SiteConfig object into our Controllers automatically at runtime.
This means that the only problem we have to solve is how to get the injected object from our Controller into its Actions' ViewModels when they are automatically bound.
So let's define a base model for others to inherit from.
BaseViewModel
public class BaseViewModel
{
public ISiteConfig SiteConfig { get; set; }
public BaseViewModel(ISiteConfig siteConfig)
{
this.SiteConfig = siteConfig;
}
}
And now let's create a model that inherits from it.
IndexViewModel
public class IndexViewModel : BaseViewModel
{
public string SomeIndexProperty { get; set; }
public IndexViewModel (ISiteConfig siteConfig) : base(siteConfig) {}
}
And now let's define a Base Controller that our Controllers will inherit from.
BaseController
public abstract class BaseController : Controller
{
protected BaseController(ISiteConfig siteConfig)
{
_siteConfig = siteConfig;
}
private readonly ISiteConfig _siteConfig;
public ISiteConfig SiteConfig
{
get
{
return _siteConfig;
}
}
}
Now we define our actual controller.
HomeController
public HomeController: BaseController
{
public HomeController(ISiteConfig siteConfig): base(siteConfig) {}
}
Assuming we're using Ninject for DI, Ninject would be configured to automatically create the Controller and pass a concrete ISiteConfig object into its Constructor at runtime.
Now we add our Action to the Controller.
Index Action
public ActionResult Index(IndexViewModel model)
{
return View(model);
}
And so this is the point where without doing anything else, MVC will explode with a "Parameterless Constructor" error if you try to call the Index Action, because MVC can't find a ViewModel constructor that takes no arguments.
And so, the answer. We need to override the default ModelBinder.
BaseViewModelBinder
public class BaseViewModelBinder : DefaultModelBinder
{
protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType)
{
if (modelType == typeof(BaseViewModel) || modelType.IsSubclassOf(typeof(BaseViewModel)))
{
var baseControl = controllerContext.Controller as BaseController;
if (baseControl == null)
{
throw new Exception("The Controller must derive from BaseController");
}
var instance = Activator.CreateInstance(modelType, baseControl.SiteConfig);
bindingContext.ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => instance, modelType);
return instance;
}
else
{
return base.CreateModel(controllerContext, bindingContext, modelType);
}
}
}
And we need to set this as the default model binder in global.asax.cs :
protected void Application_Start()
{
...
ModelBinders.Binders.DefaultBinder = new BaseViewModelBinder();
}
That's all. As you can see, when you view the Index Action now, MVC will use our custom model binder. It will realise that the IndexViewModel derives from BaseViewModel, and so will attempt to spin up an IndexViewModel instance using the ISiteConfig it can find in the Action's Controller (because the Controller derives from BaseController).