I'm developing a little site using ASP.NET MVC, MySQL and NHibernate.
I have a Contact class:
[ModelBinder(typeof(CondicaoBinder))]
public class Contact {
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual int Age { get; set; }
}
And a Model Binder:
public class ContactBinder:IModelBinder {
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) {
Contact contact = new Contact ();
HttpRequestBase form = controllerContext.HttpContext.Request;
contact.Id = Int16.Parse(form["Id"]);
contact.Name = form["Name"];
contact.Age = Int16.Parse(form["Age"]);
return contact;
}
}
Also, I have a view with a form to update my database, using this action:
public ActionResult Edit([ModelBinder(typeof(ContactBinder))] Contact contact) {
contactRepo.Update(contact);
return RedirectToAction("Index", "Contacts");
}
Until here, everything is working fine. But I have to implement a form validation, before update my contact.
My question is: Where should I implement this validation? In ActionResult method or in Model Binder? Or anywhere else?
Thank you very much.
Have a look at XVAL by Steve Sanderson.
Your business objects are where your business logic should be applied.
Kindness
Dan
XVal
I second Steve Sanderson, his book is amazing.
I've really liked the nerd dinner approach written by Rob Conery, Scott Hanselman, Phil Haack, Scott Guthrie. Basically you have a method in each entity that validates against buisness logic. That method returns a list of RuleViolations that contain the field / error msg. You also expose a bool value for convience.
You get the free chapter here: Nerd Dinner Chapter
I think this case it is better follow Microsoft recommendation that is Validation with Service Layer
Related
MVC 5 comes with its own built in authentication which is great and exactly what I need. I don't want to re-invent the wheel, so I will use that. I have also seen lots of posts and information on how to extend that if I need more information for a user.
The question I have is that I need to have another Model that links to the user that is logged in. Imagine a forum where there are logged in users who have 'Posts'. I need a 'Post' class with a relationship to the user that ASP.NET has done all the work to create.
I tried doing something like:
public virtual ApplicationUser CreatedBy { get; set; }
However that didn't work, and I don't know enough to figure out how I get it to work. Any tutorials or examples online focus on the authentication element of MVC on its own, or the entity framework side where you make your own dbContext and go off to do everything in there. How can I link these two up?
To add a reference to your applicationuser from another class you can try something like this:
public class Post{
public int Id { get; set;
public string Name { get; set; }
public virtual ApplicationUser CreatedBy { get; set; }
}
And in your controller create action (or where you are creating your new Post):
(Added code lines for usermanager etc for clarity)
var post = new Post { Name = "My new post" }
var userManager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(new ApplicationDbContext()));
var currenApplicationUser = userManager.FindById(User.Identity.GetUserId());
var currentUser = db.Users.Find(currenApplicationUser.Id);
post.CreatedBy = currentUser;
db.Posts.Add(post);
db.SaveChanges();
I'm starting to use AutoMapper and some doubts arose.
Where is the correct way to map a dto into a domain model?
I'm doing this:
DTO:
public class PersonInsert
{
[Required]
public string Name { get; set; }
public string LastName { get; set; }
}
Action:
[HttpPost]
public ActionResult Insert(PersonInsert personInsert)
{
if (ModelState.IsValid)
{
new PersonService().Insert(personInsert);
return RedirectToAction("Insert");
}
return View("Insert");
}
Service:
public class PersonService
{
public int Insert(PersonInsert personInsert)
{
var person = Mapper.Map<PersonInsert, Person>(personInsert);
return new PersonRepository().Insert(person);
}
}
Repository:
public class PersonRepository
{
internal int Insert(Person person)
{
_db.Person.Add(person);
_db.SaveChanges();
return person.Id;
}
}
So, is this correct? should my service knows about domain? or should I make the bind in repository only? is correct to use [Required] in DTO?
I would almost never create an entity from a DTO - I explain why below. I would use a request object to allow a factory method to build the entity:
Request:
public class InsertPersonRequest
{
[Required]
public string Name { get; set; }
public string LastName { get; set; }
}
Action:
[HttpPost]
public ActionResult Insert(InsertPersonViewModel viewModel)
{
if (ModelState.IsValid)
{
InsertPersonRequest request = InsertPersonViewModelMapper.CreateRequestFrom(viewModel);
new PersonService().Insert(request );
return RedirectToAction("Insert");
}
return View("Insert");
}
Service:
public class PersonService
{
public int Insert(InsertPersonRequest request)
{
var person = Person.Create(request.name, request.LastName);
return new PersonRepository().Insert(person);
}
}
Repository stays the same.
This way all logic for creating the Person are located in the Factory method of the person, and so business logic is encapsulated in the domain - derived fields, default fields etc.
The problem with what you are doing is that the DTO has to be created in the UI, then all fields are mapped to the entity - this is a sure fire way for business logic to seep into the service layer, UI, or anywhere it is not supposed to be.
PLease read that again - This is a very serious mistake I see made time and time again.
I would however, use AutoMapper in the service layer to return a DTO:
Service:
public class PersonService
{
public PersonDto GetById(intid)
{
var person = new PersonRepository().GetById(id);
var personDto = Mapper.Map<Person, PersonDto>(person);
return personDto
}
}
Is this correct?
I personally don't see anything wrong with having your service do the mapping
Is it correct to use [Required] in DTO
No, DTOs should have no business logic whatsoever. They should be used purely for transmitting data across different tiers/layers of your application.
DataAnnotations are typically used on ViewModels for client/server side validation, therefore, I would add another separation into your model and introduce a ViewModel for your Insert action e.g.
public class PersonViewModel
{
[Required]
public string Name { get; set; }
public string LastName { get; set; }
}
public class PersonDto
{
public string Name { get; set; }
public string LastName { get; set; }
}
Action:
[HttpPost]
public ActionResult Insert(PersonViewModel personViewModel)
{
if (ModelState.IsValid)
{
var personDto = Mapper.Map<PersonViewModel, PersonDto>(personViewModel);
new PersonService().Insert(personDto);
...
}
...
}
}
Service:
public class PersonService
{
public int Insert(PersonDto personDto)
{
var person = Mapper.Map<PersonDto, Person>(personDto);
return new PersonRepository().Insert(person);
}
}
It may seem overkill in this scenario (considering the only difference is the [Required] attribute). However, in a typical MVC application you would want to ensure a clean separation between your ViewModels and your business models.
I would say that your PersonService could be seen as part of the domain layer (or Application layer directly above the domain) of your architecture and the controller and DTO is in a layer above that. That means you shouldn't have a reference to the DTO in your PersonService signatures and instead use the domain Person class here. So the Mapping code should go into the Controller. This ensures that your domain logic is not affected by changes to the webservice contract which could really be just one way to use your PersonService.
I would also introduce an interface for your repository which is injected into your PersonService because the PersonService again shouldn't need to know about concrete data access implementations.
As for the [Required] attribute, I don't see a problem with having this on the DTO because it just states the data contract of your webservice method. Anybody calling your webservice should adhere to this data contract. Of course this requirement will typically also be reflected somewhere in your domain code, maybe by throwing an exception etc.
In ASP.NET MVC the typical use of DTO is being part of something called viewmodel. Viewmodel is a class that will combine one to several DTOs into one class tailored for view presentation and posting values back to server.
What you doing is correct, no issues with that, but data annotations should reside on view models, rather than DTOs. Unless you call your DTO a view model, then its fine.
Please read the following posting about model (Domain Model) vs ViewModel in ASP.NET MVC world:
ASP.NET MVC Model vs ViewModel
Confused with Model vs ViewModel
Hope this helps
I think it is fine to have annotations on the DTOs, such as [Required], MaxLength, Range etc.
Your DTO can come in from any (possibly untrusted) source (Not just your website, but from another endpoint, WCF service, etc). All requests will be funneled to your Service/Business Layers, so you will need to validate the input before performing your business logic (simple guard checks). Having the annotations on the DTO simply describe the needed input to perform the task at hand. Passing an object with annotations is not peforming validation.
However, I believe you should be validating the DTO information is correct in the service/business layer (and annotations are a nice way to check this).
Just my thoughts on the situation :)
I have the need to manually instansiate some controllers and therefore have this code:
var controller = Activator.CreateInstance(typeof(AccountController),
repository) as AccountController;
In the AccountController I have a method similar to this:
[AllowAnonymous]
[HttpPost]
public ApiJsonResult LogOn(LogOnAccountDto model)
{
ValidateModel(model);
if (ModelState.IsValid)
{
//...
}
}
I want my ModelState.IsValid to work, so therefore I call ValidateModel and pass it the model.
This fails, apparently because the controlContext isn't set.
I get this error:
Value cannot be null. Parameter name: controllerContext Description:
An unhandled exception occurred during the execution of the current
web request. Please review the stack trace for more information about
the error and where it originated in the code.
Exception Details: System.ArgumentNullException: Value cannot be null.
Parameter name: controllerContext
So, how can I manually instansiate a IController in code - so that "everything" works?
Thanks in advance.
So, why do I need this?
I'm playing around with some architecture and game logic ideas for an "online strategy game".
I have an ASP.NET MVC 4 (Preview) application, which is my web version of the game. The idea is that the game should also be played on devices like Windows Phone, iPhone etc via NATIVE apps.
Therefore I need some API for my game (some kind of REST service which communicate via http/json).
As this API will be the public interface for the game, all the game logic will of course be located in side this API.
Therefore I want to use this API from both the "web version" and the "mobile version" of the game.
I have implemented this API as an Area inside ASP.NET MVC 4 (Preview). My first though were to actually do httpwebrequest from my "web version" to the API so I were using the API EXACTLY as the "mobile version" would.
But then I thought, that it might be better to actually just instansiate the controllers manually to avoid all the json/web-calling overhead I would get from calling the API the "right way".
So that's why I'm here now, I want to instansiate my controllers manually in code, because I want to use the exact logic in them.
Makes sense?
If you have a better idea please let me know - I'm doing this this for the learning of it, not produce a real product - at least thats not the goal right now - right now I'm just trying to learn some new stuff :)
You don't need to call ValidateModel directly.
At least I never needed to call it directly in any of my code and I haven't seen any examples which would call it either.
You can use the attributes from System.ComponentModel.DataAnnotations to control how your model is validated.
Let me give you an example, copy-pasted from some working code.
The model class:
(Basically just a DTO, nothing special.)
public class ArticleModel
{
public ArticleModel()
{
CategoryIds = new List<int>();
}
public int Id { get; set; }
[Required(ErrorMessage = "Field mandatory!")]
public string Title { get; set; }
[Required(ErrorMessage = "Field mandatory!")]
public string Text { get; set; }
public string Summary { get; set; }
public bool RefreshDate { get; set; }
public List<int> CategoryIds { get; set; }
}
The controller action:
(Instantiates an object for the ORM and saves it to the database.)
[HttpPost]
[ValidateInput(false)]
[SiteAuthorize(SiteAuthorization.SiteOwner)]
public ActionResult EditArticle(ArticleModel model)
{
var article = Repository.Retrieve<Article>().SingleOrDefault(x => x.Id == model.Id && x.Site == ColorfulUtility.CurrentSite);
if (article == null)
return RedirectToAction("ArticleList");
if (ModelState.IsValid)
{
if (model.RefreshDate)
article.Date = DateTime.Now;
article.Title = model.Title.SimpleTextToSafeHtml();
article.Text = model.Text.RichTextToSafeHtml();
article.Summary = model.Summary.RichTextToSafeHtml();
foreach (var category in ColorfulUtility.CurrentSite.ArticleCategories)
{
if (!article.Categories.Contains(category) && model.CategoryIds.Contains(category.Id))
{
article.Categories.Add(category);
}
else if (article.Categories.Contains(category) && !model.CategoryIds.Contains(category.Id))
{
article.Categories.Remove(category);
}
}
Repository.Flush();
return RedirectToAction("ArticleList");
}
return View("CreateArticle", model);
}
I'm not sure if this behavior is expected or not, but it seems that custom model binding doesn't work when the binding is assigned to an interface type. Has anyone experimented with this?
public interface ISomeModel {}
public class SomeModel : ISomeModel {}
public class MvcApplication : HttpApplication {
protected void Application_Start(object sender, EventArgs e) {
ModelBinders.Binders[typeof(ISomeModel)] = new MyCustomModelBinder();
}
}
With the above code when I bind to a model of type SomeModel, MyCustomModelBinder is never hit; however, if I change the above code and substitute typeof(ISomeModel) for typeof(SomeModel) and post the exact same form MyCustomModelBinder is called as expected. Does that seem right?
Edit
I found myself back in this predicament over a year after I originally asked this question, and now I have a solution that works. Thank you Matt Hidinger!
http://www.matthidinger.com/archive/2011/08/16/An-inheritance-aware-ModelBinderProvider-in-MVC-3.aspx
I was experimenting with this issue and I came up with a solution of sorts. I made a class called InterfaceModelBinder:
public class InterfaceModelBinder : DefaultModelBinder
{
public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
ModelBindingContext context = new ModelBindingContext(bindingContext);
var item = Activator.CreateInstance(
Type.GetType(controllerContext.RequestContext.HttpContext.Request.Form["AssemblyQualifiedName"]));
Func<object> modelAccessor = () => item;
context.ModelMetadata = new ModelMetadata(new DataAnnotationsModelMetadataProvider(),
bindingContext.ModelMetadata.ContainerType, modelAccessor, item.GetType(), bindingContext.ModelName);
return base.BindModel(controllerContext, context);
}
}
Which I registered in my Application_Start as so:
ModelBinders.Binders.Add(typeof(IFormSubmission), new InterfaceModelBinder.Models.InterfaceModelBinder());
The interface and a concrete implementation look like this:
public interface IFormSubmission
{
}
public class ContactForm : IFormSubmission
{
public string Name
{
get;
set;
}
public string Email
{
get;
set;
}
public string Comments
{
get;
set;
}
}
The only downside to this whole approach (as you might have gathered already) is that I need to get the AssemblyQualifiedName from somewhere, and in this example it is being stored as a hidden field on the client side, like so:
<%=Html.HiddenFor(m => m.GetType().AssemblyQualifiedName) %>
I'm not certain though that the downsides of exposing the Type name to the client are worth losing the benefits of this approach. An Action like this can handle all my form submissions:
[HttpPost]
public ActionResult Process(IFormSubmission form)
{
if (ModelState.IsValid)
{
FormManager manager = new FormManager();
manager.Process(form);
}
//do whatever you want
}
Any thoughts on this approach?
Suddenly, an MVC3 solution appears:
http://www.matthidinger.com/archive/2011/08/16/An-inheritance-aware-ModelBinderProvider-in-MVC-3.aspx
I'm not sure if its directly related but yes there are things that you need to think about when using model binding and interfaces... I ran into similar problems with the default model binder, but it may not be directly related depending on how you are doing things...
Have a look at the following:
ASP.net MVC v2 - Debugging Model Binding Issues - BUG?
ASP.net MVC v2 - Debugging Model Binding Issues - BUG?
I'm using asp.net mvc and I'm trying to create a new Employee, in my form I use the Html.DropDown(...) to display a list of Departments to select from.
Ideally I would like MVC to just figure out which Department was selected (Id property is the value in dropdown), fetch it and set it in the incoming Employee object. instead I get a null value and I have to fetch the Department myself using the Request.Form[...].
I saw an example here: http://blog.rodj.org/archive/2008/03/31/activerecord-the-asp.net-mvc-framework.aspx but that doesn't seem to work with asp.net mvc beta
This is basic CRUD with a well-proven ORM.... need it really be so hard?
ASP.NET MVC does not know how to translate the DepartmentId form value to a Department.Load(DepartmentId) call. To do this you need to implement a binder for your model.
[ActiveRecord("Employees")]
[ModelBinder(EmployeeBinder]
public class Employee : ActiveRecordBase<Employee>
{
[PrimaryKey]
public int EmployeeId
{
get;
set;
}
[BelongsTo(NotNull = true)]
public Department Department
{
get;
set;
}
// ...
}
The EmployeeBinder is responsible for turning route/form data into an object.
public class EmployeeBinder : IModelBinder
{
#region IModelBinder Members
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
// ...
if (controllerContext.HttpContext.Request.Form.AllKeys.Contains("DepartmentId"))
{
// The Department Id was passed, call find
employee.Department = Department.Find(Convert.ToInt32(controllerContext.HttpContext.Request.Form["DepartmentId"]));
}
// ...
}
#endregion
}
With this in place anytime an Employee is used as a parameter for an action the binder will be called.
public ActionResult Create(Employee employee)
{
// Do stuff with your bound and loaded employee object!
}
See This blog post for further information
I ported ARFetch to ASP.NET MVC a couple of weeks ago... maybe that could help you.