I've got a ViewModel for adding a user with properties: Email, Password, ConfirmPassword with Required attribute on all properties. When editing a user I want the Password and ConfirmPassword properties not to be required.
Is there a way to disable validation for certain properties in different controller actions, or is it just best to create a seperate EditViewModel?
I like to break it down and make a base model with all the common data and inhierit for each view:
class UserBaseModel
{
int ID { get; set; }
[Required]
string Name { get; set; }
[Required]
string Email { get; set; }
// etc...
}
class UserNewModel : UserBaseModel
{
[Required]
string Password { get; set; }
[Required]
string ConfirmPassword { get; set; }
}
class UserEditModel : UserBaseModel
{
string Password { get; set; }
string ConfirmPassword { get; set; }
}
Interested to know if there is a better way as well although this way seems very clean an flexible.
You could write a custom attribute that can test a condition and either allow an empty field or not allow it.
The below is a simple demo i put together for the guys here. You'll need to modify to suit your purposes/
using System.ComponentModel.DataAnnotations;
namespace CustomAttributes
{
[System.AttributeUsage(System.AttributeTargets.Property)]
public class MinimumLength : ValidationAttribute
{
public int Length { get; set; }
public MinimumLength()
{
}
public override bool IsValid(object obj)
{
string value = (string)obj;
if (string.IsNullOrEmpty(value)) return false;
if (value.Length < this.Length)
return false;
else
return true;
}
}
}
Model;
using CustomAttributes;
namespace Models
{
public class Application
{
[MinimumLength(Length=20)]
public string name { get; set; }
}
}
Controller
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Index(Application b)
{
ViewData["Message"] = "Welcome to ASP.NET MVC!";
if (ModelState.IsValid)
{
return RedirectToAction("MyOtherAction");
}
return View(b);
}
enter code here
Related
I have generic model for contact
public class Contact
{
public string Title { get; set; }
public string FirstName { get; set; }
[Required(ErrorMessage = "Please enter LastName")]
public string LastName { get; set; }
[Required(ErrorMessage = "Please enter Email")]
public string Email { get; set; }
public string Phone { get; set; }
}
Now I want to use my contact class in two models but apply the validation only on second?
public class Step1Model{
public Contact Contact{get;set;}
}
public class Step2Model{
[Requried]
public Contact Contact{get;set;}
}
How do I make it work?
I see two options here:
1 - Code to an interface which will require you to create a ContactRequired class and a ContactOptional class based upon the ContactInterface. I believe this will allow you to then have a single StepModel where you would set the StepModel.Contact property to either a new ContactRequired() or a new ContactOption(). Then when the validaiton runs for the StepModel, it will be have based upon the type of class you set for the StepModel.Contact property.
public interface ContactInterface
{
string Title { get; set; }
string FirstName { get; set; }
string LastName { get; set; }
string Email { get; set; }
string Phone { get; set; }
}
public class ContactOptional : ContactInterface
{
public string Title { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
public string Phone { get; set; }
}
public class ContactRequired : ContactInterface
{
public string Title { get; set; }
public string FirstName { get; set; }
[Required(ErrorMessage = "Please enter LastName")]
public string LastName { get; set; }
[Required(ErrorMessage = "Please enter Email")]
public string Email { get; set; }
public string Phone { get; set; }
}
public class StepModel
{
public ContactInterface Contact { get; set; }
}
Usage:
StepModel smTest = new StepModel();
ContactRequired crContact = new ContactRequired();
ContactOptional coContact = new ContactOptional();
List<ValidationResult> lErrors = new List<ValidationResult>();
smTest.Contact = coContact;
//Validate Option
if (Validator.TryValidateObject(smTest, new ValidationContext(smTest, serviceProvider: null, items: null), lErrors, true))
{
//Code should reach this as the model should be valid;
}
smTest.Contact = crContact;
//Validate Required
if (Validator.TryValidateObject(smTest, new ValidationContext(smTest, serviceProvider: null, items: null), lErrors, true))
{
//Code should not reach this as the model should be invalid;
}
2 - Create a custom required attribute which will look at another property of the Contact model (such as bool UseValidation) to determine if the required validation should even take place or if it should simply return true as the default. I am not initially providing code for this option as you would need a custom attribute for every type of validation attribute in your class. Also, I think option 1 is the better one unless you have a specific reason against it.
I have decided not to have many view models.
Here is my implementation
https://gist.github.com/cpoDesign/bc9c5980a89cfe7b0caf
I have a type defined as
public class Autocomplete
{
public Guid Id { get; set; }
public string Label { get; set; }
}
I then have a model with this type as
public class MPEngagementActivity : IActivity
{
[UIHint("Hidden")]
public Guid Id { get; set; }
[UIHint("Hidden")]
//TODO: GET THIS FROM THE LOGGED IN USER
public Guid CreatedBy { get; set; }
[UIHint("Hidden")]
public int ActivityType { get; set; }
[Required(ErrorMessage = "Please select an Organisation")]
[Display(Name="Constituency")]
public Autocomplete Organisation { get; set; }
[UIHint("ReadOnly")]
[Display(Name = "MP Office Default Contact")]
public String DefaultContact { get; set; }
[Display(Name = "MP Contact")]
public Autocomplete MainContact { get; set; }
}
As you can see one of the properties is marked as required, but when testing the model is coming back as valid even though the property was not set in the form, the id is coming back as 0s and the label empty.
How can I get mvc to properly validate this?
public class Autocomplete
{
public Guid Id { get; set; }
public string Label { get; set; }
// This should do the trick with the standard Required attribute
public static implicit operator string(Autocomplete ac)
{
return ac.Label;
}
// Optionally, if you want to use a custom required instead, this may be more correct
public override string ToString()
{
return Label;
}
}
Then you simply put the [Required] attribute on the Autocomplete property.
Set the [Required] attribute on the class's properties
public class Autocomplete
{
[Requried]
public Guid Id { get; set; }
[Required]
public string Label { get; set; }
}
I have the following ASP.NET MVC filter attribute:
public void OnActionExecuting(ActionExecutingContext context) {
ControllerBase controller = context.Controller;
}
And on the view I have a form with
#Html.TextBox("Captha");
And my model is:
public class SignUpModel {
public String Email { get; set; }
public String Password { get; set; }
public String Captcha { get; set; }
}
How can I, in my filter attribute, do the following:
Get value inserted in the text box;
Add an error to model state if there is no value or a specific condition is false?
Do I need the captcha property in my model?
Thank You,
Miguel
You don't need an ActionFilter to do this. Use CompareAttribute in your model to validate the Captcha property. Add another property to your model, and call it SessionValue, then use the CompareAttribute to compare the value entered for the Captcha property with the SessionValue property:
public class SignUpModel {
public string Email { get; set; }
public string Password { get; set; }
[Compare("SessionValue")]
public string Captcha { get; set; }
public string SessionValue { get; set; }
}
Then, in your Controller action set the value of the SessionValue property to the value stored in the Session:
var model = new SignUpModel();
model.SessionValue = Session["MyValue"];
return View(model);
And, in your View, you'll have:
#Html.HiddenFor(model => model.SessionValue)
#Html.TextBoxFor(model => model.Captcha)
#Html.ValidationMessageFor(model => model.Captcha)
UPDATE:
If you don't want to have SessionValue as a hidden input in your View, you can create a custom validation attribute like this:
using System.ComponentModel.DataAnnotations;
using System.Web;
public class MyCustomValidationAttribute : ValidationAttribute
{
public override bool IsValid(object value)
{
if (value == null)
return true;
string compareValue = HttpContext.Current.Session["MyValue"];
return (string)value.Equals(compareValue);
}
}
And, use it in your Model like this:
public class SignUpModel {
public string Email { get; set; }
public string Password { get; set; }
[Required]
[MyCustomValidation]
public string Captcha { get; set; }
}
I want to create large user table (advance User Profile) and save user's data in my database context. So, I don't want to use 2 DbContexts in my project. When users register to site, they data (UserName, Password etc.) stores my own User table. My classes are like this:
public class ModelBase
{
public int Id { get; set; }
public DateTime CreateDate { get; set; }
public DateTime LastUpdateDate { get; set; }
}
public class User : ModelBase
{
public string UserName { get; set; }
public string Password{ get; set; }
public string FullName { get; set; }
public string Email { get; set; }
public DateTime BirthDate { get; set; }
public string Specialty { get; set; }
}
public class News : ModelBase
{
public int UserId { get; set; }
public string Title { get; set; }
...
}
....
Context is so:
public class MyDBContext : DbContext
{
public MyDBContext()
{
Database.SetInitializer<MyDBContext>(new MyDBContextInitializer());
}
public DbSet<User> UserSet { get; set; }
public DbSet<News> NewsSet { get; set; }
public DbSet<Project> ProjectSet { get; set; }
public DbSet<Section> SectionSet { get; set; }
....
}
class MyDBContextInitializer : DropCreateDatabaseIfModelChanges<MyDBContext>
{
protected override void Seed(MyDBContext context)
{
base.Seed(context);
}
}
I replaced DbContext name with mine and changed connection name in default SimpleMembershipInitializer class like this:
....
Database.SetInitializer<MyDBContext>(null);
try
{
using (var context = new MyDBContext())
{
if (!context.Database.Exists())
{
// Create the SimpleMembership database without Entity Framework migration schema
((IObjectContextAdapter)context).ObjectContext.CreateDatabase();
}
}
WebSecurity.InitializeDatabaseConnection("MyDBContextConnection", "User", "Id", "UserName", autoCreateTables: true);
....
Finally, I changed RegisterModel and WebSecurity.CreateUserAndAccount() suitable my User class. But, it does not work.
How can I use my own User table for register to site?
You can have Asp.net Membership and your complex classes connected together.
with this approach you will save so much time because asp.net membership is much more robust(you don't need to think about Role and User management) and sure you can make use of existing open source project like this and add it to your project with minimum effort of time.
Then your class will have structure like :
public class CustomUserDetail : ModelBase
{
public string UserName { get; set; } // what you really need is this to be unique for each user in you data base
// public string Password{ get; set; } handled by asp.net Membership
public string FullName { get; set; }
// public string Email { get; set; } handled by asp.net Membership
public DateTime BirthDate { get; set; }
public string Specialty { get; set; }
}
Then you can can add extension method to IPrincipal like :
public static CustomUserDetail CustomUserDetail (this IPrincipal principal)
{
var repository = new YourUserDetailRepository();
return repository.GetCurrentUserDetail();
}
and finnaly in your code easily use
<p> #User.CustomUserDetail.FullName </p>
I have the following class that's used by my MVC3 application. I would like
to simplify the updating of the class so that when a new class object is
created then the Created and CreatedBy fields get set automatically.
I'd also like to make it so that the Modified and ModifiedBy fields get
updated automatically.
Is there a way that I could do this?
The class is used in MVCnamespace Storage.Models
{
public class Topic : TableServiceEntity
{
[DisplayName("Partition Key")]
public override string PartitionKey { get; set; }
[DisplayName("Row Key")]
public override string RowKey { get; set; }
[DisplayName("Description")]
public string Description { get; set; }
public DateTime Created { get; set; }
public DateTime Modified { get; set; }
public String CreatedBy { get; set; }
public string ModifiedBy { get; set; }
}
}
Set the defaults in the constructor for the class
public class Topic
{
public Topic()
{
this.Created = DateTime.Now;
this.CreatedBy = UserName;
}
[DisplayName("Partition Key")]
public override string PartitionKey { get; set; }
[DisplayName("Row Key")]
public override string RowKey { get; set; }
[DisplayName("Description")]
public string Description { get; set; }
public DateTime Created { get; set; }
public DateTime Modified { get; set; }
public String CreatedBy { get; set; }
public string ModifiedBy { get; set; }
}
jonathan,
Jason's answer above with the logic contained within the constructors is a perfectly valid and clean way of doing this and I wouldn't argue with that (and have done it myself for more 'static' properties). However, given that there could be a timelapse between the creation of the object and the actual save, then you may also want to consider putting this logic into your controller (or service layer).
this would look roughly like this:
public ActionResult Create(MyCreateViewModel viewModel)
{
if (ModelState.IsValid)
{
viewModel.Entity.Created = DateTime.UtcNow;
_myService.Insert(viewModel.Entity);
_myService.SaveChanges();
return this.RedirectToAction(x => x.Index());
} else {
PopulateViewModel(viewModel);
return View(viewModel);
}
}
likewise, you may have a LastEdit datetime that you want to track. use the Edit action similarly:
public ActionResult Edit(MyEditViewModel viewModel)
{
if (ModelState.IsValid)
{
viewModel.Entity.LastEditDate= DateTime.UtcNow;
_myService.AttachAndUpdate(viewModel.Entity);
_myService.SaveChanges();
return this.RedirectToAction(x => x.Index());
} else {
PopulateViewModel(viewModel);
return View(viewModel);
}
}
just another approach to ensure that datetime related properties are truly reflected.
In this solution i think you may have to make big change in your Repository/Service layer
define an Interface like :
public interface IHistoryLog
{
DateTime Created { get; set; }
DateTime Modified { get; set; }
string CreatedBy { get; set; }
string ModifiedBy { get; set; }
}
then:
public class Topic:IHistoryLog
{
// Implement interface..
}
then create a generic service class:
public abstract class CRUDService<TModel>
{
protected CRUDService(DataContext dataContext)
{
// data context to do generic CRUD stuff
}
public virtual Save(TModel model)
{
if(model is IHistoryLog)
{
// assign Createdby and Created
}
}
public virtual Update(TModel model)
{
if(model is IHistoryLog)
{
// assign ModifiedBy and Modified
}
}
}