I have a model that uses remote validation. The model also acts as a parent class to a child class. How would i disable remote validation in the view for a particular field?
Model code
public user
{
[Remote("Validateemail","User",etc)]
public string Email {get; set;}
}
public edituser:user
{
public int userid {get; set;}
public edituser(int userid,string email)
{
userid=userid;
Email=email;
}
}
My aim is to remove the remove validation in the edituser class in the view.
im assuming that you just need to display the email field and all the data in it as it is in editView because by defauflt a user cannot edit his/her email..
to so this you can just change the code in the view from
Html.EditorFor() to Html.DisplayFor()
this is the perfect case for viewmodel. create a different viewmodel for edit (as a matter-effect view should not be linked directly to domain models in most of the cases)
or you can use IValidatableObject or fluentvalidation , or you can use DataAnnotationsModelValidatorProvider for manually attaching attribute condition based if it applies in your case.
Related
I'm an ASP.NET MVC beginner and currently I use EF with a database-first approach to do my work. Everything goes well and every model have its controller and views files. I face problem when I want to save multi model data on a view.
In my case:
I have ASP.NET MVC form for teacher details data, and another for teacher employment history (this two are auto generate from scaffolding database approach)
I want a view Create.cshtml where user inputs teacher details and employment history and this can be saved into their own tables. Therefore, I use tuple and follow what (Multiple models in a view) and (Two models in one view in ASP MVC 3) did.
As a result, I successfully create teacher details and employment history on a view (Just interface).
But I have no idea how to save the input into the different tables (2 tables: teacher, employmentHistory). Now, when I save the input, nothing happens.
I guess I need to do something on controllers parts, please give me some ideas on how to do it.
Thanks in advance
First, welcome to StackOverflow!
You are correct, you should do something on the controller part.
I would create a ViewModel for the view. By doing this you can project the data from the database in any way that is needed in the view. Also, with this approach you don't return the full entity, maybe there is some sensitive information there. And you can get the benefit of the model validations also. (For example, what if at some point you need to add another field from another entity?)
I would also create partial views for the information in the view model and pass that model to the partial view, this way enabling the re-use of the views, if needed.
When passing the data to the controller you can pass the ViewModel and then save the data in the database.
Since you didn't give no model info of your classes, I give below an example. Either way (view model or tuple example you followed), you should change the controller code similar to what I'm writing below.
public class TeacherViewModel
{
//teacher details
[Required]
public int TeacherId {get; set;}
[Required]
public string TeacherName {get; set;}
// {...}
//teacher employment info
//here you can have a list of information or the last entry in the history,
//or what's required to add a new entry
public string LastWorkPlace {get; set;}
public DateTime From {get; set;}
public To {get; set; }
//{...}
}
public class TeacherController: Controller
{
//{...}
[HttpPost]
public ActionResult SaveTeacherInfo(TeacherViewModel model){
if (ModelState.IsValid) {
var teacher = new TeacherEntity
{
TeacherId = model.TeacherId, //if update otherwise leave blank
TeacherName = model.TeacherName
//{...}
};
//your context name here
_dbContext.Teachers.Add(teacher);
var teacherEmploymentInfo = new TeacherEmploymentHistory
{
TeacherId = teacher.TeacherId,
LastWorkPlace = model.LastWorkPlace,
From = model.From,
To = model.To
//{...}
};
_dbContext.TeachersEmploymentInfo.Add(teacherEmploymentInfo);
_dbContext.SaveChanges();
//_dbContext.SaveChangesAsync(); if using async await
}
return View(model); //return any validation errors from the view model
// to the user to fix.
}
}
If you are using the ErrorHandler as a global filter no need for try..catch block if you are not planing to return custom errors (BusinessException or DatabaseException, for example) from the controller, if not, you should wrap the code in a try..catch block.
I have made a custom attribute which checks the password complexity, the issue is that it called when i first run the code, after that if i change the complexity it does not register that attribute even if the session is refreshed.
i am calling it on property named password
in UserModel.
[ComplexPassword()]
public String Password { get; set; }
The custom attribute is here.
public class ComplexPassword : RegularExpressionAttribute
{
public ComplexPassword()
: base(GetRegex())
{
T = Localizer.CaptionInstance;
}
private Localizer.CaptionDelegate T { get; set; }
Some Logic here...
}
the Password property is used in the changed password form which is using user model. i think view code is not necessary to show. Can anyone guide how to fire it at every at every call of Password property.
I believe the Data Annotations get cached on a model. So if you are doing something behind the scenes so that GetRegex() changes depending on something you do in the app, it might not be reflected in any new validation attempts on the model. You might need to create your own MetadataProvider that cusotmizes the behavior of DataAnnotationsModelMetadataProvider
I often find myself in the situation where I only want to present and edit some fields from my model. Let's say I have a model that represts an address, perhaps I just want the form to update the city and post code fields (bad example, but hopefully it explains the scenario).
I know of two methods:
1) Persist the unwanted fields in hidden input elements on the form, or...
2) Create a dedicated view model that just defines the fields I need.
I favour option #2, but I don't have a nice clean way of merging the data from the view model back into the 'real' model within the controller action. At the moment, I follow this approach...
1) Store the record I'd in a hidden field on the view model
2) When the page posts back, the controller retrieves the original record and I manually assign each field from the view model to the real model
3) Save the real model back to the data store.
This works, but it is quite a lot of work and very easy to miss an assignment/reassignment and I was wondering if anyone knew of another approach?
Use the System.ComponentModel.DataAnnotations.MetadataType.
Something like:
public class BaseClassOfProperties
{
public string Name { get; set; }
}
public interface INameViewableProperties
{
[Display(name = "Your Name")]
string Name { get; set; }
}
public interface INameHiddenProperties
{
//[scaffoldColumn(false)] this completely hides the fields
[UIHint("Hidden")] // i think...
string Name { get; set; }
}
[MetadataType(typeof(INameViewableProperties)]
public class NameViewAbleProperties : BaseClassOfProperties
{
}
[MetadataType(typeof(INameHiddenProperties)]
public class NameHiddenProperties : BaseClassOfProperties
{
}
One common recommended practice in asp.net mvc is that you should not send your business models to your views.. instead you should create viewmodels specific to each view.
When that is done and you call the ModelState.IsValid method in your controller you are effectively checking the validity of the viewmodel but not the business object.
What is the conventional approach to dealing with this?
public class Person
{
public int ID {get; set;};
[Required]
public string Name {get; set;}
[Required]
public string LastName {get; set;}
public virtual ICollection<Exam> Exams {get; set;}
}
public class PersonFormViewModel
{
public int ID {get; set;};
[Required]
public string Name {get; set;}
[Required]
public string LastName {get; set;}
}
This is exactly what I have right now but Im not sure if the [Required] attribute should appear on both models or just the ViewModel or just the Business Model.
Any tips on this issue are appreciatedd.
More links to support my claim that it is a common good practice to always use view models.
How to add validation to my POCO(template) classes
http://blogs.msdn.com/b/simonince/archive/2010/01/26/view-models-in-asp-net-mvc.aspx
My preference is to do input validation on the view models, and business validation on the domain models.
In other words, any data annotations such as required fields, length validation, regex, etc should be done on your view models, and added to the model state when error occurs.
And you'll probably have business/domain rules that rely on more than just a "form", so you should do that either in the domain models (execute the validation after they're mapped back), or with a service layer.
All our models have a method called "Validate", which we call in the services prior to persisting. They throw custom exceptions if they fail business validation, which gets caught by the controller and also added to the model state.
May not be everyone's cup of tea, but it's consistent.
Example of business validation, as requested:
Here's an example of a domain model we have, which represents a generic "Post" (question, photo, video, etc):
public abstract class Post
{
// .. fields, properties, domain logic, etc
public void Validate()
{
if (!this.GeospatialIdentity.IsValidForThisTypeOfPost())
throw new DomainException(this, BusinessException.PostNotValidForThisSpatial.);
}
}
You see there, I am checking against business rules, and throwing custom exceptions. DomainException is our base, and we have many derived implementations. We have an enum called BusinessException, which contains values for all our exceptions. We use extension methods on the enum to provide the resource-based error message.
This is not simply a field on the model I'm checking, e.g "All posts must have a subject", because although that is part of the domain, it's input validation first and foremost, and thus is handled via the data annotations on the view model.
Now, the controller:
[HttpPost]
public ActionResult Create(QuestionViewModel viewModel)
{
if (!ModelState.IsValid)
return View(viewModel);
try
{
// Map to ViewModel
var model = Mapper.Map<QuestionViewModel,Question>(viewModel);
// Save.
postService.Save(model); // generic Save method, constraint: "where TPost: Post, new()".
// Commit.
unitOfWork.Commit();
// P-R-G
return RedirectToAction("Index", new { id = model.PostId });
}
catch (Exception exc)
{
var typedExc = exc as DomainException;
if (typedExc != null)
{
// Internationalised, user-friendly domain exception, so we can show
ModelState.AddModelError("Error", typedExc.BusinessError.ToDescription());
}
else
{
// Could be anything, e.g database exception - so show generic msg.
ModelState.AddModelError("Error", "Sorry, an error occured saving the Post. Support has been notified. Please try again later.");
}
}
return View(viewModel);
}
So, by the time we get to the "Save" method on the service, the model has passed input validation. Then the Save method calls post.Validate(), invoking business rules.
If an exception is raised, the controller catches it and displays the message. If it gets pass the Save method and another error occurs (90% of the time, it's Entity Framework, for example), we show a generic error message.
As I said, not for everyone, but this works well for our team. We have a clear separation of presentation and domain validation, and a consistent flow of control from the raw HTTP POST, to the redirect after success.
The MetaData "buddy" class is exactly what this is for. The validation is created once but can be used on both the model and the viewmodel classes:
public class PersonMetaData
{
[Required]
public string Name {get; set;}
[Required]
public string LastName {get; set;}
}
[MetadataType(typeof(PersonMetaData))]
public class Person
{
public string Name {get; set;}
public string LastName {get; set;}
}
[MetadataType(typeof(PersonMetaData))]
public class PersonFormViewModel
{
public string Name {get; set;}
public string LastName {get; set;}
}
Great answer by RPM1984, and nice code sample.
My view is still that using Variant 2 from the start is a pragmatic balance between productivity and structure, but you always have to be willing to move to Variant 3 in some cases, so I advocate a mix of the two approaches where logical. Patterns & Practices however recommend always doing Variant 3 as it is the ideal separation of concerns etc, and it avoids you using two approaches in the same solution which some people don't like and many customers I work with pick Variant 3 and run with that for all models.
I think the key is what RPM1984 said - reusing your business entities inside your View Models is useful for the sake of reusing the validation, but do bear in mind that often your business logic needs to do different validation too (e.g. checking the record doesn't already exist). If you use Variant 3 it empowers you to focus your View Model validation purely on the needs of your views (at the expense of a little extra effort) but you will always need some kind of business logic validation too.
Given that you always pass viewmodels to your view and your domain models are not exposed to the end user through views then i don't see any need for validating domain model itself. For example if you receive PersonViewModel from view through form post you will transform it to Person Model (perhaps through automapper etc.) only if PersonViewModel is valid itself. So i believe that input validations should stay with the view models because they are the one that are bound to input.
Typically your ViewModel will contain a reference to your Model - a ViewModel is only necessary if you need to display additional information in your view that isn't available in your Model, there is no need to replicate the data already present.
Ok I've just ran into this and I was only supposed to be checking my emails however I've ended up watching this (and not far off subscribing to TekPub).
http://tekpub.com/production/starter
Now this app is a great starting point, but it raises one issue for me and the development process I've been shown to follow (rightly or wrongly). There is no conversion from the LinqToSql object when passing data to the view. Are there any negitives to this?
The main one I can see is with validation, does this cause issues when using MVC's built in validation as this is somthing we use extensivly. Because we are using the built in objects generated by LinqToSql how would one go about adding validation, like
[Required(ErrorMessage="Name is Required")]
public string Name {get;set;}
Interested to understand the benifits of this methodology and any negitives that, should we take it on, experiance through the development process.
Should this be taken as a guide and we should be using ViewModels? If so should we always use them even in simple cases? And how/where in the application logic does the Entity get converted to a ViewModel?
With entity objects, you could use buddy classes, whereby you create a second class which acts as a metadata provider for your entity. For instance, with a Customer entity generated by Linq-to-Sql, I could create a buddy class like so:
[MetadataType(typeof(CustomerMeta))]
partial class Customer {
}
public class CustomerMeta {
[DisplayName("Forename", Required(ErrorMessage = "Forename is required.")]
public string Forename { get; set;}
}
Entities are generated as partial classes so you can add your own code to them.
Alternatively, you could forego pushing your entity types to your views and create specific models based around the functionality required, for instance I would typically have a User entity, but when I need to create a User, I have something called a CreateUserSpec model:
public class CreateUserSpec
{
[DisplayName("Forename")]
public string Forename { get; set; }
}
Which has a subset of the properties of the User, only those required to create a User. This is the model I would pass to my view, and repopulate from the form data. For instance:
public class AccountController
{
public ActionResult Register() {
return View(new CreateUserSpec());
}
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Register(CreateUserSpec spec) {
if (!ModelState.IsValid) {
return View(spec);
}
var user = UserFactory.CreateUser(spec);
// Redirect to authorisation page?
}
}