Working in MVC3, c#
I am wondering about the 'hierarchy' of a model, class, db table and a partial class.
In the db I have a UserAccount table structured as:
USERACCOUNT: UserId, firstname, Lastname, Login, Password, email
In my project I have a model called UserModel. I am using it so i can decorate the properties with dataannotations. it is set up as the metadata type for the useraccount class, like so:
[MetadataType(typeof(UserModel))]
public partial class useraccount
{
}
public class UserModel
{
public int UserId { get; set; }
[Display(Name="First Name")]
[StringLength(20)]
[Required]
public string FirstName { get; set; }
[Display(Name = "Last Name")]
[StringLength(30)]
[Required]
public string LastName { get; set; }
[Display(Name = "Email Address")]
[StringLength(20)]
[Required]
public string email { get; set; }
[Remote("IsUserNameAvailable", "Validation")]
[Display(Name = "Choose a Login Name")]
[StringLength(40)]
[Required]
public string login { get; set; }
[Display(Name = "Choose a Password")]
[StringLength(64)]
[Required]
public string password { get; set; }
[Display(Name = "Enter Password Again")]
[StringLength(64)]
[Required]
public string confirmPassword { get; set; }
}
Notice in the database table there is a 'Password' column, but no 'confirmPassword' however in the UserModel, there is.
My thinking is the UserAccount class, using the UserModel class for metadata, should now contain a definition for 'confirmPassword'.
Here is the problem I am having. On the Register view, i am using the UserModel as the model, so at the top of the page I have:
#model OurAgreements.Models.UserModel
the problem comes in the controller when I try to save. This code:
public ActionResult Register(UserModel model)
{
var repo = new Repository();
if (ModelState.IsValid)
{
using (var db = new ouragreementEntities())
{
db.useraccount.Add(model);
db.SaveChanges();
}
return View();
}
gives the error, cant convert UserModel to useraccount. I can understand this, I am trying to put data into a table that doesn't match all the columns.
So then I figured I would change the model on the view to:
#model OurAgreements.Models.useraccount
because per my thinking, the useraccount class should be using the UserModel, but doing that gives an error, 'useraccount has no definition for 'confirmPassword'
So I am a bit stuck. i know I can switch back to using the UserModel as the model, then inthe controller I can build a new useraccount instance, and fill it with the data from the model, then save the instance to the database.
So I guess my question then, is why does useraccount not contain a definition for confirmPassword?
First things first. Change the name of your classes. Use UserAccountMetadata / UserAccount. This makes things a lot easier to read and understand.
Also, use the compare attribute on the confirm password field so that you can be sure they match. The datatype attribute should be present on both password fields.
[DataType(DataType.Password)]
[Display(Name = "Confirm Password")]
[Compare("Password", ErrorMessage = "Password and confirm password must be same!")]
public string confirmPassword { get; set; }
The model your view uses should be UserAccount.
Before you pass your view model (UserAccount) to the method that saves it in the database, you need to map it to the object that the method takes, which looks like is your domain model object. You can do that manually but that is cumbersome. Use a mapping tool, such as AutoMapper to do that automatically for you. You can tell AutoMapper which fields in the UserAccount map to which fields in you domain model.
You were on the right path before by creating a View Model that contains your DataAnnotations and additional properties that are not in your database. This ViewModel and its related DataAnnotations help you to perform your business validation logic as well as allow you to map one or more tables worth of data to a single class for your view.
So, as to what I think your overall question is, why is there not a confirm password in the database? Why should there be? Having two copies of the same data in the database does not make sense in the realm of storing data.
The confirm password is a business logic / user input check to ensure that they did not 'fat finger' the password and lock themselves out of their account. Once you have confirmed that the password they entered is the one that they intended (password == confirmPassword), you hash the password and place it into the database.
Related
I had the idea to use Data Annotations in order to validate ModelState. This works wonderfully. The problem I am having is that the [Required] Data Annotation is being enforced on [Key] fields on post. Our data layer takes care of setting Id's and we don't want anyone consuming the service to have to worry about Id's. Is there a way around this in WebApi2?
I have looked at this question, and removing the Id field from ModelState in the POST method before checking for valid ModelState would work. The issue with that is that we use a filter for ModelState.
EDIT:
After doing some more research, what I am essentially wanting to do is what the [Bind] attribute does in MVC. After some research, it does not look like this is a feature that has yet been implemented in WebApi. If anyone has any ideas, feel free to post them.
What you can do is to replace your entity with a data transfer object, which is identical to your original entity without the ID field. For example,
The original entity may look like this
public class User
{
[Required]
public Guid UserId { get; set; }
public string Firstname { get; set; }
public string Lastname { get; set; }
public string Username { get; set; }
public string Email { get; set; }
}
and the DTO may look like this
public class UserDto
{
public string Firstname { get; set; }
public string Lastname { get; set; }
public string Username { get; set; }
public string Email { get; set; }
}
Hope this helps.
I have one model and one controller for multiple views.
The model have some required fields but for a specific view i need to ignore the validation for 2 specific fields.
There is any method to ignore the validation for those 2 fields?
I am using asp.net MVC3.
Model code example:
[Required(ErrorMessage = "Campul strada este obligatoriu")]
public string Strada { get; set; }
[DisplayName("Numar strada")]
[Required(ErrorMessage = "Campul strada numar este obligatoriu")]
public string NrStrada { get; set; }
For 9/10 views that is ok but for 1 view i don't want to be requiered.
When such an issue occurs then I normally create different view models. Each with it's own validation logic. There's nothing wrong with doing it this way.
Here are examples, not relating to your code, you can adjust your code accordingly.
For example with create customer I would have a create customer view model, and for edit customer I would have a edit customer view model. Each has different sets of validation. Create customer only requirs a first name and a last name. Edit customer needs a first name, last name, and employee number of who updated the customer record. Employee number updater is not required when adding a new customer.
Here is a possible create customer view model:
public class CreateCustomerViewModel
{
[Required(ErrorMessage = "Required")]
public string FirstName { get; set; }
[Required(ErrorMessage = "Required")]
public string LastName { get; set; }
}
Here is a possible edit customer view model:
public class EditCustomerViewModel
{
[Required(ErrorMessage = "Required")]
public string FirstName { get; set; }
[Required(ErrorMessage = "Required")]
public string LastName { get; set; }
[Required(ErrorMessage = "Required")]
public string UpdatedByEmployeeNumber { get; set; }
}
This is just a basic example.
Public class UserMetdata
{
[Required]
[Display(Name="User ID")]
public int UserID { get; set; }
[Display(Name="User Name")]
public string UserName { get; set; }
}
I dont want to UserName to be shown in View. Its similar like creating not required Annotation. One solution is by deleting UserName form Class but i dont want that.
How can it be done using Data Annotation.
You could use ScaffoldColumnAttribute for that property
[ScaffoldColumn(false)]
public string UserName { get; set; }
This will work only when you let framework dynamically generate your views by calling #Html.DisplayForModel() or like, and you DO NOT have defined display template for that model at Views/Shared/DisplayTemplates or Views/ControllerName/DisplayTemplates. Otherwise, you should edit that display template and remove corresponding line from it
I’m using ASP.NET MVC3. I have a model that has one property that I don’t want to store in the database. Is there an attribute that I can put on the property to achieve this? Thanks.
public class Person
{
[Key]
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
[NotMapped]
public string FullName { get; set; }
}
The attribute are in the namespace System.ComponentModel.DataAnnotations
Just to add more options... this is why I prefer to keep my domain model separate from my view model. My view model often has additional fields necessary for rendering the view which does not belong in the domain model. The design I typically use is described pretty well here.
I have a model class that has a couple of required fields:
public class UserMetadata
{
[Required(ErrorMessage = "Please enter a name.")]
public string Name { get; set; }
[Required(ErrorMessage = "Please enter a password.")]
public string Password { get; set; }
}
On the create view, if I don't give a name and/or password, then the validation summary errors appears. All nice and good. For the edit view, I'm only displaying the 'Name' field - I don't to show the 'Password' field.
When I save my changes on the edit page, the validation summary error appears saying that I must enter a password.
How can I control the validation of the password field, so that for the edit view, it should not bother with it? Or, am I approaching this the wrong way? I still want the 'Name' field validation to work on the edit view.
EDIT:
For my MVC project, I'm using Entity Framework. Thus, I have a 'UserMetadata' class defined so that I can attached things like '[Required]' onto certain fields on the 'User' class (which is in the EDMX file).
I should also explain that I'm using a view model eg 'UserEditViewModel' which has a property 'User' attached to it. So on my post:
[HttpPost]
public ActionResult Edit(UserEditViewModel inputViewModel)
{
if(ModelState.IsValid) { inputViewModel.User blah.... }
}
Think I rushed a bit when typing this question. Any other missing information you think is important, then please give me a shout.
Cheers.
Jas.
I ended up doing this in my action method:
ModelState.Remove("User.Password");
Now my code runs fine, only raising validation errors on the "Name" field, which is what I wanted..
ModelState.Remove("User.Password") did not work for me in MVC 3.
However, the following worked.
Option 1:
ModelState.Remove("Password")
Option 2:
ModelState.Where(m => m.Key == "Password").FirstOrDefault().Value.Errors.Clear()
Assuming you're using your UserMetadata class as a view model, you should be using a different view model per page (view).
e.g.
public class UserMetaDataCreate
{
[Required(ErrorMessage = "Please enter a name.")]
public string Name { get; set; }
[Required(ErrorMessage = "Please enter a password.")]
public string Password { get; set; }
}
and UserMetaDataEdit
public class UserMetaDataEdit
{
[Required(ErrorMessage = "Please enter a name.")]
public string Name { get; set; }
}
Basically, if the edit view doesn't need password, it shouldn't be in the model anyway.
In your controller,
public ActionResult Create()
{
return View(new UserMetaDataCreate());
}
// and subsequent post actions
[HttpPost]
public ActionResult Edit(UserMetaDataEdit vm)
{
if(ModelState.IsValid)
{
// do something
}
else
return View(vm);
}
Of course, you could go about some inheritance as your models become more complex e.g.
public class UserMetaData
{
[Required(ErrorMessage = "Please enter a name.")]
public string Name { get; set; }
}
And subclass your view models
public class UserMetaDataEdit
{
[Required(ErrorMessage = "Please enter a password.")]
public string Password { get; set; }
}
public class UserMetaDataCreate
{
}
But, I'm not sure that makes sense contextually since UserMetaData does semantically include a password.