MVC 4 - Use a different model in partial view - asp.net

Please bear with my noobness, I'm super new to the MVC pattern.
What I'm trying to do
I am building a profile information page for registered users on my site. This page would list data about the user, such as date of birth, telephone number, subscription status, etc.. You get the idea. I would also like to have a form to let users change their password, email address, personal information on the same page.
My problem
The user's data comes from my controller via a passed model variable:
public ActionResult Profil()
{
var model = db.Users.First(e => e.UserName == WebSecurity.CurrentUserName);
return View(model);
}
The output looks like this in my view:
<label>Phone number: </label>
#if (Model.PhoneNumber != null)
{
#Model.PhoneNumber
}
else
{
<span class="red">You haven't set up your phone number yet. </span>
}
The form in which the user could change his info would use another model, ProfileModel. So basiccaly I need to use two models in my view, one for outputting information and one for posting data. I thought that using a partial view I can achieve this, but I get this error:
The model item passed into the dictionary is of type
'Applicense.Models.User', but this dictionary requires a model item of
type 'Applicense.Models.ProfileModel'.
Here's what my call to the partial view looks like:
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
#Html.ValidationSummary()
#Html.Partial("_ModifyProfileInfo")
}
Here's the partial view:
#model Applicense.Models.ProfileModel
<ul>
<li>
#Html.LabelFor(m => m.Email)
#Html.EditorFor(m => m.Email)
</li>
<li>
#Html.LabelFor(m => m.ConfirmEmail)
#Html.EditorFor(m => m.ConfirmEmail)
</li>
<input type="submit" value="Update e-mail" />
</ul>
And finally here's my ProfileModel:
public class ProfileModel
{
[Required]
[DataType(DataType.EmailAddress)]
[Display(Name = "New e-mail address")]
public string Email { get; set; }
[DataType(DataType.EmailAddress)]
[Display(Name = "Confirm new e-mail address")]
[Compare("Email", ErrorMessage = "The e-mail and it's confirmation field do not match.")]
public string ConfirmEmail { get; set; }
}
Am I missing something? What's the proper way to do this?
Edit:
I remade my code reflecting Nikola Mitev's answer, but now I have another problem. Here's the error I get:
Object reference not set to an instance of an object. (#Model.UserObject.LastName)
This only occurs when I'm posting the changed e-mail address values. Here's my ViewModel (ProfileModel.cs):
public class ProfileModel
{
public User UserObject { get; set; }
[Required]
[DataType(DataType.EmailAddress)]
[Display(Name = "Új e-mail cím")]
public string Email { get; set; }
[DataType(DataType.EmailAddress)]
[Display(Name = "Új e-mail cím megerősítése")]
[Compare("Email", ErrorMessage = "A két e-mail cím nem egyezik.")]
public string ConfirmEmail { get; set; }
[DataType(DataType.EmailAddress)]
[Display(Name= "E-mail cím")]
public string ReferEmail { get; set; }
}
Controller:
public ActionResult Profil()
{
var User = db.Users.First(e => e.UserName == WebSecurity.CurrentUserName);
var ProfileViewModel = new ProfileModel
{
UserObject = User
};
return View(ProfileViewModel);
}
And finally here's my user.cs model class:
[Table("UserProfile")]
public class User
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int UserId { get; set; }
[Column("UserName")]
public string UserName { get; set; }
[Column("Email")]
[Required]
public string Email { get; set; }
[Column("FirstName")]
public string FirstName { get; set; }
[Column("LastName")]
public string LastName { get; set; }
[Column("PhoneNumber")]
public string PhoneNumber { get; set; }
... You get the idea of the rest...
I'm thinking it's happening because the model is trying to put data in each required columns into the database.
Edit2:
The httppost method of my Profil action:
[HttpPost]
[Authorize]
[ValidateAntiForgeryToken]
public ActionResult Profil(ProfileModel model)
{
if (ModelState.IsValid)
{
//insert into database
return Content("everything's good");
}
else
{
//outputs form errors
return View(model);
}
}

The best way to handle this situation is to use and pass viewModel to your Profile controller, viewModel is wrapper class for multiple objects that you want to pass to your view.
public class ProfileUserViewModel
{
public ProfileModel ProfileModelObject {get; set;}
public UserModel UserModelObject {get; set;}
}
Your controller should look like:
public ActionResult Profil()
{
var profileModel = db.Users.First(e => e.UserName == WebSecurity.CurrentUserName);
var userModel = //fetch from db.
var pmViewModel = new ProfileUserViewModel
{
ProfileModelObject = profileModel,
UserModelObject = userModel
};
return View(pmViewModel);
}
And finally your view :
#model Applicense.Models.ProfileUserViewModel
<label>Phone number: </label>
#if (Model.ProfileModelObject.PhoneNumber != null)
{
#Model.PhoneNumber
}
else
{
<span class="red">You haven't set up your phone number yet. </span>
}

There is an overload of #Html.Partial which allows you to send ViewData as defined in your controller - this is the method I generally use for partial views.
In your controller define ViewData["mypartialdata"] as ViewDataDictionary. Then in your view
#Html.Partial("_ModifyProfileInfo",ViewData["mypartialdata"])

In your [HttpPost] profil function, if modelstate.isvalid is false, you return your edit view, but you need to define your pmViewModel again , other wise your partial view will not have an object to display. Try using the following and let us know what happens
[HttpPost]
[Authorize]
[ValidateAntiForgeryToken]
public ActionResult Profil(ProfileModel model)
{
if (ModelState.IsValid)
{
//insert into database
return Content("everything's good");
}
else
{
//outputs form errors
var pmViewModel = new ProfileUserViewModel
{
ProfileModelObject = profileModel,
UserModelObject = userModel
};
return View(model);
}
}

While I know this question has been asked longtime ago however some people might still face a similar problem. One easy solution I use to pass or have more than one view model on a page is to use a ViewBag to hold the second object and refer to it in the view. See example bellow.
In your controller do this:
Obj2 personalDets = new Obj2();
DbContext ctx = new DbContext();
var details = ctx.GetPersonalInformation;
foreach(var item in details) {
personalDets.Password = item.Password;
personalDets .EmailAddress = item.EmailAddress;
}
ViewBag.PersonalInformation = personalDets;
Then in your view those properties become readily available for you

Related

Unusual behavior DropDownList MVC 5

I managed to populate DropDownList with value from a Database in ASP.NET MVC 5. My goal is to assing one of the dropDownList's value to a specific model, and send it back to the Database. So, if i leave the default value in the dropdownlist, the data in SQL server is null, which is Okay, but if I choose an option, I get an error :
Exception thrown: 'System.InvalidOperationException' in System.Web.Mvc.dll ("There is no ViewData item of type 'IEnumerable' that has the key 'Status'."). I tried everything so far and i am opened for suggestions. Thank you !!!
In Controller :
ViewBag.Status = new SelectList(db.Status, "Id", "Name");
in View
#Html.DropDownList("Status","Select status...")
In Controller so far..
public ActionResult Index()
{
return View();
}
[HttpGet]
public ActionResult Apply(ViewModelVM vm,int x=0)
{
myDb db = new myDb();
ViewBag.SocialStatus = new SelectList(db.SocialStatuses, "Id", "StatusDescription");
return View();
}
[HttpPost]
public ActionResult Apply(ViewModelVM vm)
{
if (ModelState.IsValid)
{
using (myDb db = new myDb())
{
var personalinfo = new PersonalInformation()
{
FirstName = vm.PersonalInformation.FirstName,
LastName = vm.PersonalInformation.LastName,
Birthdate = vm.PersonalInformation.Birthdate,
SocialStatus = vm.SocialStatus
};
ViewBag.SocialStatus = new SelectList(db.SocialStatuses, "Id", "StatusDescription");
db.PersonalInformations.Add(personalinfo);
db.SaveChanges();
}
return View("Success");
}
return View();
}
The model:
public partial class Status
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public SocialStatus()
{
PersonalInformations = new HashSet<PersonalInformation>();
}
public int Id { get; set; }
[StringLength(20)]
public string StatusDescription { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<PersonalInformation> PersonalInformations { get; set; }
}
}
The ViewModel:
public class ViewModelVM
{
...
public Status SocialStatus { set; get; }
...
}
Firstly your using a view model so include a property in your view model for the SelectList
public IEnumerable<SelectListItem> StatusList { get; set; }
Next remove the parameter for the model from the GET method (and since you don't appear to be using the value of x, that should be removed also)
[HttpGet]
public ActionResult Apply(ViewModelVM vm,int x=0)
{
myDb db = new myDb();
ViewModelVM model = new ViewModelVM()
{
StatusList = new SelectList(db.SocialStatuses, "Id", "StatusDescription");
};
return View(model); // return the model to the view
}
Next, your dropdown is binding to a property named Status but your view model does not contain a property named status (its SocialStatus) and SocialStatus is a complex object and you cannot bind a <select> to a complex object (a <select> only posts back a single value (or array or values in the case of <select multiple>).
In addition, because your view model contains a property which is a complex object with validation attributes on its properties, ModelState will always be invalid because you do not post back a value for StatusDescription. As a result you always return the view in the POST method, and because you have not reassigned ViewBag.Status = ...., it is null, hence the error.
Remove property public Status SocialStatus { set; get; } and include
[Display(Name = "Social Status")]
[Required(ErrorMessage = "Please select a status")]
public int SocialStatus { get; set; }
an then in the view, strongly bind to your model using
#Html.LabelFor(m => m.SocialStatus)
#Html.DropDownListFor(m => m.SocialStatus, Model.StatusList, "-Please select-")
#Html.ValidationMessageFor(m => m.SocialStatus)
Then, in the POST method, if ModelState is invalid, populate the select list again before returning the view
if(!ModelState.IsValid)
{
model.StatusList = new SelectList(db.SocialStatuses, "Id", "StatusDescription");
return View(model);
}
// save and redirect
Finally, review What is ViewModel in MVC?.

#Html.DropDownListFor not posting back to controller

I am using #Html.DropDownListFor for the first time. Code is below.
Model:
class Student
{
[Required]
[Display(Name = "Name")]
public string Name { get; set; }
[Required]
[Display(Name = "Roll Number")]
public string RollNumber { get; set; }
[Required]
[Display(Name = "ClassId")]
public int ClassId { get; set; }
}
class Class
{
[Required]
[Display(Name = "ClassId")]
public string RollNumber { get; set; }
[Required]
[Display(Name = "ClassName")]
public string RollNumber { get; set; }
}
Controller:
public ActionResult Create()
{
Student student = new BusinessEntities.Student();
List<Class> classes = GetAllClasses();
ViewBag.ClassId = new SelectList(classes, "ClassId", "ClassName");
return View(student);
}
[HttpPost]
public ActionResult Create(BusinessEntities.Student student)
{
if (ModelState.IsValid)
{
//Integer has 0 by default. But in our case if it contains 0,
//means no class selected by user
if(student.ClassId==0)
{
ModelState.AddModelError("ClassId", "Select Class to Enroll in");
return View(student);
}
}
}
Student Create View:
<form method="post">
Select Class :
#Html.DropDownListFor(Model=>Model.ClassId,ViewBag.ClassId as IEnumerable<SelectListItem>, "ClassId","ClassName")
#Html.ValidationMessageFor(Model => Model.ClassId)
<input type="submit" value="Submit" />
</form>
Error Message:
The ViewData item that has the key 'ClassId' is of type 'System.Collections.Generic.List`1[[BusinessEntities.Class, BusinessEntities, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]' but must be of type 'IEnumerable'.
I want ClassId of Student be binded and populated automatically when posted back to Controller. Please help me to get rid of it.
Thanks.
You need to give the SelectList a different name that the property your binding to (say)
ViewBag.ClassList = new SelectList(classes, "ClassId", "ClassName");`
and then
#Html.DropDownListFor(m => m.ClassId, ViewBag.ClassList as IEnumerable<SelectListItem>)
and then ensure if you return the view (for example if ModelState is invalid), that you repopulate the SelectList (as you have done in the GET method). Currently when you return the view, it is null resulting in an error, because if the second parameter is null the fallback is that the helper expects the first parameter to be IEnumerable<SelectListItem> (but its not - its typeof int)
Side notes: Do not use Model => Model.XXX (capital M) and your current use of DropDownistFor() as 2 parameters which make no sense. "ClassId" will add a label option <option value="">ClassId</option> and the last one ,"ClassName" will not do anything.
Edit
In addition, your
if(student.ClassId==0)
{
ModelState.AddModelError("ClassId", "Select Class to Enroll in");
return View(student);
}
is a bit pointless. student.ClassId will never be 0 unless one of the items in your GetAllClasses() has ClassId = 0. You should be using
[Required(ErrorMessage = "Select Class to Enroll in")] // add error message here
public int ClassId { get; set; }
and in the view
#Html.DropDownListFor(m => m.ClassId, ViewBag.ClassList as IEnumerable<SelectListItem>, "--please select--")
which will create the first option with a value of null. If this option were selected, then the DefaultModelBinder will attempt to set the value of ClassId = null which fails (because typeof int cannot be null) and a ModelState error is added and ModelState becomes invalid.
The in the POST method
[HttpPost]
public ActionResult Create(BusinessEntities.Student student)
{
if (!ModelSTate.IsValid)
{
ViewBag.ClassList = // repopulate select list
return View(student);
}
// Save and redirect
}

get IEnumerable<T> in mvc post method argument

I have one model called ProductSupplier
I am passing #model IEnumerable to my View
and showing it from view
Now when i submit the form i m not getting list of IEnumerable in my http post method. I want to know the selected supplier from user.
Below is my model
public sealed class ProductSupplier
{
public int CountryId { get; set; }
public int UserId { get; set; }
public bool IsProductSupplier { get; set; }
public string CountryName { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
}
This is my HttpGet method
public ActionResult ManageSupplier(int id)
{
var supplier = App.UsersRepo.GetSupplierForProduct(id);
return View(supplier);
}
And I am binding it via following way (U can suggest me best way I am new bee to MVC)
#model IEnumerable<ProductSupplier>
#using (Html.BeginForm("ManageSupplier", "Products", FormMethod.Post, new { role = "form") })
{ #Html.AntiForgeryToken()
foreach (var item in Model)
{
<div class="checkbox">
<label>
#Html.CheckBoxFor(x => item.IsProductSupplier, new { id = item.Email }) #item.FirstName #item.LastName (#item.Email)
</label>
</div>
}
}
And finally my HttpPost method
[HttpPost]
public ActionResult ManageSupplier(IEnumerable<ProductSupplier> obj)
{ // I m getting obj null in my argument
//I want to Get selected id from obj and want to pass in selectedSupplier
var returnVal = App.ProductRepo.AssigneSupplierForProduct(productId, selectedSupplier);
return Json(new { success = true }, JsonRequestBehavior.DenyGet);
}
can anyone suggest me where i m making mistake.
I am new to MVC any kind of suggestion highly appreciated.
Thank you in advance.
Firstable u cant do it like this.One way to do that is something like this.Here is the basic step how u do that.
1-assign for all checkbox ,checkbox change event with the unique id.
(take a look at here)
2-Cretae a jquery object and store the data when ever the checkbox clicked ,via versa
var ListProductSuppliers ={ {ProductSupplier_info_here },{ProductSupplier_info_here } };
3-later via ajax request,serilize this object(ListProductSuppliers ) and send to your method
4-on server side deserilize this to the IEnumerable<ProductSupplier>
5 later do it whatever u want with those selected suppliars

MVC Model State Validation fails on Listbox

I have a simple model which uses a multi select listbox for a many-many EF relationship.
On my Create action, I'm getting the error
The parameter conversion from type 'System.String' to type 'MyProject.Models.Location' failed because no type converter can convert between these types.
I have 2 models, an Article and a Location:
Article.cs
namespace MyProject.Models
{
public class Article
{
public Article()
{
Locations = new List<Location>();
}
[Key]
public int ArticleID { get; set; }
[Required(ErrorMessage = "Article Title is required.")]
[MaxLength(200, ErrorMessage = "Article Title cannot be longer than 200 characters.")]
public string Title { get; set; }
public virtual ICollection<Location> Locations { get; set; }
}
Location.cs:
namespace MyProject.Models
{
public class Location
{
[Key]
public int LocationID { get; set; }
[Required(ErrorMessage = "Location Name is required.")]
[MaxLength(100, ErrorMessage = "Location Name cannot be longer than 100 characters.")]
public string Name { get; set; }
public virtual ICollection<Article> Articles { get; set; }
}
}
I have a ViewModel:
namespace MyProject.ViewModels
{
public class ArticleFormViewModel
{
public Article article { get; set; }
public virtual List<Location> Locations { get; set; }
public ArticleFormViewModel(Article _article, List<Location> _locations)
{
article = _article;
Locations = _locations;
}
}
}
create.cshtml:
#model MyProject.ViewModels.ArticleFormViewModel
<h2>Create</h2>
#using (Html.BeginForm()) {
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<fieldset>
<legend>Article</legend>
<div class="editor-label">
#Html.LabelFor(model => model.article.Title)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.article.Title)
#Html.ValidationMessageFor(model => model.article.Title)
</div>
<h3>Locations</h3>
#Html.ListBoxFor(m=>m.article.Locations,new MultiSelectList(Model.Locations,"LocationID","Name"))
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
Finally my controller actions:
// GET: /Article/Create
public ActionResult Create()
{
var article = new Article();
var AllLocations = from l in db.Locations
select l;
ArticleFormViewModel viewModel = new ArticleFormViewModel(article, AllLocations.ToList());
return View(viewModel);
}
//
// POST: /Article/Create
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(Article article)
{
var errors = ModelState.Values.SelectMany(v => v.Errors);
if (ModelState.IsValid)
{
var locations = Request.Form["article.Locations"];
if (locations != null)
{
var locationIDs = locations.Split(',');
foreach (var locationID in locationIDs)
{
int id = int.Parse(locationID);
Location location = db.Locations.Where(l => l.LocationID == id).First();
article.Locations.Add(location);
}
}
db.Articles.Add(article);
db.SaveChanges();
return RedirectToAction("Index");
}
var AllLocations = from l in db.Locations
select l;
ArticleFormViewModel viewModel = new ArticleFormViewModel(article, AllLocations.ToList());
return View(viewModel);
}
This all works relatively well, my Locations listbox is populated properly:
If I do not select a Location then my model is saved properly. If I select one or more locations then my Model.IsValid check fails with the exception
The parameter conversion from type 'System.String' to type 'MyProject.Models.Location' failed because no type converter can convert between these types.
However if I remove the ModelState.IsValid check then despite the error my values are all correctly saved into the database - just that I lose validation for things such as the model title.
Hope someone can help!
Unless you create a type converter, you cannot directly bind the results of your list box directly to a complex object like that. The reason lies in the fact that MVC can only deal with posted HTTP values, which in this case are an array of strings that contain the selected ID's. Those strings do not directly map to your Locations object (ie the number 1 cannot be directly converted to a Locations object with an ID of 1).
Your best bet is to have a list of location ID's in your View Model of type string or int to accept the posted values, then in your post method create the Location objects and fill them with the correct ID's.
FYI, the reason your code works is because you are bypassing the model binding and going directly to the Request.Form collection. You will notice that the bound Article object will not have any Location objects.
EDIT:
I don't even see how your code would work even without this problem. Your ArticleFormViewModel does not have a parameterless constructor, so that will fail in model binding (unless you have a custom model binder).
In any event, what you want to do is this (note, you will have to populate SelectedLocationIDs if you want them to be selected when the view is rendered):
public class ArticleFormViewModel
{
...
List<int> SelectedLocationIDs { get; set; }
...
}
Then, in your view you have:
#Html.ListBoxFor(m=>m.SelectedLocationIDs,
new MultiSelectList(Model.Locations,"LocationID","Name"))
In your Post method, instead of the code that calls Request.Form, you have something like this:
foreach(var locationID in article.SelectedLocationIDs) {
... // look up your locations and add them to the model
}

ASP.NET MVC how to achieve to use the same model with different error message

I am having this issue at the moment, I had address model (use required attribute to decorate) which can be used more than once on the same page, one is billing address and the other one is shipping address. when validation failed, I'd like to have suffix in front of my generic error message indicate which address is required e.g. "{0} - address line 1 required", either billing or shipping
Here is my model
public class AddressBaseModel
{
[Display(Name="Address line 1")]
[Required(ErrorMessageResourceType = typeof(ModelValidation), ErrorMessageResourceName = "AddrLine1Required")]
public string AddressLine1 { get; set; }
[Display(Name="Address line 2")]
[Required(ErrorMessageResourceType = typeof(ModelValidation), ErrorMessageResourceName = "AddrLine2Required")]
public string AddressLine2 { get; set; }
[Display(Name="Address line 3")]
public string AddressLine3 { get; set; }
[Display(Name="Address line 4")]
public string AddressLine4 { get; set; }
}
}
Here is the code segment I used in my page
<fieldset class="space-bottom">
<legend>Please enter your home address</legend>
<div id="home_fields">
#Html.EditorFor(m => m.HomeAddress)
</div>
</fieldset>
<fieldset class="space-bottom">
<legend>Please enter your delivery address</legend>
<div id="delivery_fields">
#Html.EditorFor(m => m.DeliveryAddress)
</div>
</fieldset>
Thanks
Personally I use the FluentValidation.NET library instead of Data Annotations as it makes things so much easier and provides a lot more power. Here's an example of how to achieve your goal using this ilbrary.
Create a new ASP.NET MVC 3 project using the default Visual Studio template
Install the FluentValidation.MVC3 NuGet package.
Add the following line to Application_Start:
ModelValidatorProviders.Providers.Add(
new FluentValidationModelValidatorProvider(
new AttributedValidatorFactory()
)
);
Define the following models:
public class AddressBaseModel
{
public string AddressLine1 { get; set; }
}
[Validator(typeof(MyViewModelValidator))]
public class MyViewModel
{
public AddressBaseModel HomeAddress { get; set; }
public AddressBaseModel DeliveryAddress { get; set; }
}
And the following Validators:
public class AddressBaseModelValidator : AbstractValidator<AddressBaseModel>
{
private readonly string _addressType;
public AddressBaseModelValidator(string addressType)
{
_addressType = addressType;
RuleFor(x => x.AddressLine1)
.NotEmpty()
.WithMessage(string.Format("{0} - address line 1 required", addressType));
}
}
public class MyViewModelValidator : AbstractValidator<MyViewModel>
{
public MyViewModelValidator()
{
RuleFor(x => x.HomeAddress)
.SetValidator(new AddressBaseModelValidator("billing"));
RuleFor(x => x.DeliveryAddress)
.SetValidator(new AddressBaseModelValidator("shipping"));
}
}
Modify the HomeController:
public class HomeController : Controller
{
public ActionResult Index()
{
var model = new MyViewModel
{
HomeAddress = new AddressBaseModel(),
DeliveryAddress = new AddressBaseModel()
};
return View(model);
}
[HttpPost]
public ActionResult Index(MyViewModel model)
{
return View(model);
}
}
And the corresponding Index.cshtml view:
#model MyViewModel
#using (Html.BeginForm())
{
<fieldset class="space-bottom">
<legend>Please enter your home address</legend>
<div id="home_fields">
#Html.EditorFor(m => m.HomeAddress)
</div>
</fieldset>
<fieldset class="space-bottom">
<legend>Please enter your delivery address</legend>
<div id="delivery_fields">
#Html.EditorFor(m => m.DeliveryAddress)
</div>
</fieldset>
<input type="submit" value="Register" />
}
You could create a custom attribute that does the dynamic formatting for you. You would just tag your address fields with the Address attribute like this:
[Address]
public string AddressLine1 { get; set; }
You would need to add a property in the AddressBaseModel where you tell the system what type of address this is (you would set this to "Billing" or "Shipping" when you instantiate the view model right before you pass the view model to the View in the controller get action):
public string AddressType { get; set; }
A custom attribute like this should work (I haven't tested it, I wrote it just now). This automatically gets the address type you specified when you create the model instance and formats it with the display name of the address field).
public class AddressAttribute : ValidationAttribute
{
private const string DefaultErrorMessage = "{0} - {1} required";
public AddressAttribute()
: base(DefaultErrorMessage) { }
protected override ValidationResult IsValid(object value,
ValidationContext validationContext)
{
if (value != null)
{
if (!base.IsValid(value))
{
// get the property called "AddressType" from the model so we know if it's Billing or Shipping
var addressType = validationContext.ObjectInstance.GetType()
.GetProperty("AddressType")
.GetValue(validationContext.ObjectInstance, null);
// use the display name of the address field in the error message
return new ValidationResult(
string.Format(DefaultErrorMessage, addressType, validationContext.DisplayName));
}
}
return ValidationResult.Success;
}
}
This should work:
[Required(ErrorMessage = "The Address 2 is required.")]

Resources