I am quite new to MVC. I am using an interface as a property for my model.
I noticed that my Data Annotation Attributes were being ignored. I also got an error while submitting the form:
Cannot create an instance of an interface.
I soon figured out that I will have to use a custom ModelBinder
I am having hard time figuring what needs to be done inside the CreateModel method of the ModelBinder
I have the following RegistrationModel:
public class RegistrationModel
{
#region Properties (8)
public string Email { get; set; }
public string FirstName { get; set; }
public Gender Gender { get; set; }
public string LastName { get; set; }
public string Password { get; set; }
public string PasswordConfirmation { get; set; }
public IPlace Place { get; set; }
public string Username { get; set; }
#endregion Properties
}
Here is the IPlace interface and implementation:
public interface IPlace
{
#region Data Members (7)
string City { get; set; }
string Country { get; set; }
string ExternalId { get; set; }
Guid Id { get; set; }
string Name { get; set; }
string Neighborhood { get; set; }
string State { get; set; }
#endregion Data Members
}
public class Place : IPlace
{
#region Implementation of IPlace
public Guid Id { get; set; }
[StringLength(100, ErrorMessage = "City is too long.")]
public string City { get; set; }
[StringLength(100, ErrorMessage = "Country is too long.")]
public string Country { get; set; }
[StringLength(255, ErrorMessage = "External ID is too long.")]
public string ExternalId { get; set; }
[Required(ErrorMessage = "A name is required.")]
[StringLength(450, ErrorMessage = "Name is too long.")]
[DisplayName("Location")]
public string Name { get; set; }
[StringLength(100, ErrorMessage = "Neighborhood is too long.")]
public string Neighborhood { get; set; }
[StringLength(100, ErrorMessage = "State is too long.")]
public string State { get; set; }
#endregion
}
You should try to avoid using interfaces and abstract types in your view models. So in your case if the controller action taking this view model can never have any other implementation of IPlace than Place, than simply replace the interface.
If for some reason you needed it, as you have already found out, you will have to write a custom model binder in which you specify which implementation you want to create. Here's an example.
Related
I know this question might be asked so many times, I tried a lot to find the answer and ultimately I am throwing it here if some one can help me.
I am doing a project in ASP.NET Core MVC with a database-first approach. I have a registration form where I need to populate Country and City dropdowns to fill with data coming in through WeCareConext class.
In my controller I am doing this:
[HttpGet]
public IActionResult Register()
{
CompanyEditViewModel model = new CompanyEditViewModel();
var fromDatabaseEF = new SelectList(_context.Country.ToList(), "CountryId", "NameEn");
ViewData["CompanyRegister"] = fromDatabaseEF;
return View(model);
}
My Country model:
public partial class Country
{
public Country()
{
City = new HashSet<City>();
Currency = new HashSet<Currency>();
HolidayType = new HashSet<HolidayType>();
Holidays = new HashSet<Holidays>();
Province = new HashSet<Province>();
StaffInfo = new HashSet<StaffInfo>();
}
public int CountryId { get; set; }
public string NameAr { get; set; }
public string NameEn { get; set; }
public string NameFr { get; set; }
public string IsoCode2 { get; set; }
public string IsoCode3 { get; set; }
public string RegionAr { get; set; }
public string Region { get; set; }
public byte? PostcodeRequired { get; set; }
public bool? IsActive { get; set; }
public byte? IsAvailable { get; set; }
public string MobileMask { get; set; }
public byte? IsDeleted { get; set; }
public int? BaseCharges { get; set; }
public int? MobileNumberLength { get; set; }
public string CountryCode { get; set; }
public byte[] IconImage { get; set; }
public string PickerListAr { get; set; }
public string PickerListEn { get; set; }
public string PickerListFr { get; set; }
public virtual ICollection<City> City { get; set; }
public virtual ICollection<Currency> Currency { get; set; }
public virtual ICollection<HolidayType> HolidayType { get; set; }
public virtual ICollection<Holidays> Holidays { get; set; }
public virtual ICollection<Province> Province { get; set; }
public virtual ICollection<StaffInfo> StaffInfo { get; set; }
}
My CompanyEditViewModel class:
public class CompanyEditViewModel
{
[Display(Name = "Company Number")]
public string CompanyId { get; set; }
[Required]
[Display(Name = "Company Name")]
[StringLength(75)]
public string Name { get; set; }
[Required]
[Display(Name = "Country")]
public string CountryId { get; set; }
public IEnumerable<SelectListItem> Countries { get; set; }
[Required]
[Display(Name = "City")]
public string SelectedCityCode { get; set; }
public IEnumerable<SelectListItem> Cities { get; set; }
}
Unfortunately, this returns an error:
System.ArgumentException: 'Format of the initialization string does not conform to specification starting at index 0.'
I am trying different approaches but not solving my issue. Can anyone suggest some code which can help me and I can also select country and then populate city dropdown according to the CountryID without page reload ? Thanks in advance.
I have two partial views named as _centerDetails.cshtml and _centerRights.cshtml .
I am passing data from centerdetails when I click on submit and want to show this when another partial view is switched, but without posting it to the server.
This is my model class which I have created to handling my data via
controller:
namespace ADWP_AdminWebPortal.Models
{
public class CountryList
{
[Required(ErrorMessage = "Select a Country.")]
public int CountryId { get; set; }
[Required]
public string Country { get; set; }
[Required(ErrorMessage = "Select a State.")]
public int StateId { get; set; }
[Required]
public string State { get; set; }
[Required(ErrorMessage = "Select a City.")]
public int CityId { get; set; }
[Required]
public string City { get; set; }
}
public class CustomerDetails: CountryList
{
public int ClientId { get; set; }
[Required (ErrorMessage="Eneter the First name")]
[DataType(DataType.Text)]
public string FirstName { get; set; }
[Required(ErrorMessage = "Eneter the Middle name")]
[DataType(DataType.Text)]
public string MiddleName { get; set;}
public string NatureOfOccupation { get; set; }
public string AgentId { get; set; }
[Required(ErrorMessage ="Select a Client Type.")]
public string ClientType { get; set; }
public int TariffId { get; set; }
public string TariffName { get; set; }
public int ServiceId { get; set; }
public string ServiceName { get; set; }
public string OrderId { get; set; }
public int PaymentMethodId { get; set; }
public string PaymentMethodName { get; set; }
}
}
Here you can see the mess I have created. My problem is how to handle multiple models when you are working with multiple models in same controller?
Here I did not created all the data in a single class because I wanted it to reuse as per my need.
How can I handling data in a model? This is the main problem that is
coming to me while I am creating the partial views in my project.
You may use CTE and ROW_NUMBER function together to delete duplicate rows from your table.
With CTE AS (
SELECT VERIFICATIONTYPE,
NAME,
COST,
RN = ROW_NUMBER() OVER (PARTITION BY VERIFICATIONTYPE, NAME, COST ORDER BY VERFICATIONTYPE)
FROM DETAILS)
DELETE FROM CTE WHERE RN > 1
END
After a bit of a struggle I got the result.
public class CountryList
{
[Required(ErrorMessage = "Select a Country.")]
public int CountryId { get; set; }
[Required]
public string Country { get; set; }
}
// Added Class
public class State
{
[Required(ErrorMessage = "Select a State.")]
public int StateId { get; set; }
[Required]
public string State { get; set; }
}
// Added Class
public class City
{
[Required(ErrorMessage = "Select a City.")]
public int CityId { get; set; }
[Required]
public string City { get; set; }
}
public class CustomerDetails
{
public int ClientId { get; set; }
[Required (ErrorMessage="Enter the first name")]
[DataType(DataType.Text)]
public string FirstName { get; set; }
[Required(ErrorMessage = "Enter the middle name")]
[DataType(DataType.Text)]
public string MiddleName { get; set; }
public int TariffId { get; set; }
public string TariffName { get; set; }
public int ServiceId { get; set; }
public string ServiceName { get; set; }
public string OrderId { get; set; }
public int PaymentMethodId { get; set; }
public string PaymentMethodName { get; set; }
public List<CountryList> { get; set; } //added list
public List<State> { get; set; } //added list
public List<City> { get; set; } //added list
}
Yeah, it was so easy.
I have this contact Info model.
namespace School.Models
{
public partial class ContactInfo
{
public ContactInfo()
{
this.Branches = new List<Branch>();
this.People = new List<Person>();
}
public long ID { get; set; }
[Required]
[DataType( System.ComponentModel.DataAnnotations.DataType.PhoneNumber)]
[StringLength(12, MinimumLength=9)]
public string PhoneNumber { get; set; }
[Required]
[DataType(System.ComponentModel.DataAnnotations.DataType.PhoneNumber)]
public string CellNumber { get; set; }
[Required(ErrorMessage="Please enter a valid email like name#domain.com")]
[DataType(System.ComponentModel.DataAnnotations.DataType.EmailAddress, ErrorMessage="xyz#domain.com")]
public string Email { get; set; }
public virtual ICollection<Branch> Branches { get; set; }
public virtual ICollection<Person> People { get; set; }
}
}
I have to set all these fields required , But when i do i get an error that i can't do so. Its working if i remove any one of the required tag but starts giving the error when i make all of them required.
Is there any way that i can set all these fields "Phone Number", "Cell Number", "Email" REQUIRED.
Thanks in advance.
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 page I am writing to send emails to customers. One of the ways to select customers to email is to select products they have. Each product has different detail information and you will also be able to choose specifics about the product to narrow down who you are emailing.
Because this is going to be complex I need to do the processing to come up with the customer email list on the controller side but it would be a nightmare to try to pull all the data form the controls to send to it manually.
I would like to use an AJAX call that on the controller side would get the model tied to the view, query the database, and send back the list of emails so on the view I can pop up outlook for them with the list of email addresses already populated. Because I need to go back to the view with data I don't think I can do this with a form post which is how I normally get model into into the controller.
Does someone know how I could accomplish this?
Here are some of the clases I have to try to help people understand my layout
public class ProductType
{
[HiddenInput(DisplayValue = false)]
public int ID { get; set; }
[Required(ErrorMessage = "Please enter a product type description")]
public string Description { get; set; }
public virtual ICollection<ProductTypeDetail> ProductDetails { get; set; }
}
public class ProductTypeDetail
{
[HiddenInput(DisplayValue = false)]
public int ID { get; set; }
[HiddenInput(DisplayValue = false)]
public int? ProductTypeID { get; set; }
public ProductType ProductType { get; set; }
[Required(ErrorMessage = "Please enter a description")]
public string Description { get; set; }
[Required(ErrorMessage = "Please enter a valid type")]
public string Type { get; set; }
public virtual ICollection<ProductTypeDetailValidValue> ValidValues { get; set; }
}
The above 2 classes are for product types which someone could enter anything for and as many as they want. The product details are detail information you might need to know about your products. For example you could have a product type of Vehicle Registration System and put a product detail item in that for a specific import process that pertains to the product they you need to know if they use or not.
public Customer()
{
SiteVisits = new List<SiteVisit>();
Payments = new List<Payment>();
Contacts = new List<CustomerEmail>();
}
[HiddenInput(DisplayValue = false)]
public int ID { get; set; }
[Display(Name = "Name")]
[Required(ErrorMessage = "Please enter a customer name")]
public string CustomerName { get; set; }
[Display(Name = "Line 1")]
public string Address1 { get; set; }
[Display(Name = "Line 2")]
public string Address2 { get; set; }
[Display(Name = "Line 3")]
public string Address3 { get; set; }
[Display(Name = "Line 4")]
public string Address4 { get; set; }
public string City { get; set; }
public string State { get; set; }
public string Zip { get; set; }
[HiddenInput(DisplayValue = false)]
[Required(ErrorMessage = "Please enter a customer type")]
public int CustomerTypeID { get; set; }
[Display(Name = "Type")]
public virtual CustomerType CustomerType { get; set; }
[HiddenInput(DisplayValue = false)]
[Required(ErrorMessage = "Please enter a customer status")]
public int CustomerStatusID { get; set; }
[Display(Name = "Status")]
public virtual CustomerStatus CustomerStatus { get; set; }
[DataType(DataType.MultilineText)]
public string Comments { get; set; }
[Required(ErrorMessage = "Please enter an expiration year")]
public long ExpirationYear { get; set; }
[Required(ErrorMessage = "Please enter an expiration month")]
public long ExpirationMonth { get; set; }
[Required(ErrorMessage = "Please enter a control name")]
public string ControlName { get; set; }
public Boolean Networked { get; set; }
public long Population { get; set; }
[Display(Name = "First Fiscal Month")]
public long FirstMonth { get; set; }
[Display(Name = "FTP User Name")]
public string FTPUserName { get; set; }
[Display(Name = "FTP Password")]
public string FTPPassword { get; set; }
[Display(Name = "Customer ID")]
public string CustomerUpdateID { get; set; }
[DataType(DataType.Date)]
[Display(Name = "Customer Since")]
public DateTime? StartDate { get; set; }
public virtual ICollection<CustomerPhoneNumber> PhoneNumbers { get; set; }
public virtual ICollection<CustomerProduct> Products { get; set; }
public virtual ICollection<CustomerEmail> Contacts { get; set; }
public virtual ICollection<SiteVisit> SiteVisits { get; set; }
public virtual ICollection<Payment> Payments { get; set; }
}
public class CustomerProduct
{
[HiddenInput(DisplayValue = false)]
public int ID { get; set; }
[HiddenInput(DisplayValue = false)]
public int? ProductTypeID { get; set; }
public virtual ProductType ProductType { get; set; }
[HiddenInput(DisplayValue = false)]
public int CustomerID { get; set; }
public virtual Customer Customer { get; set; }
[HiddenInput(DisplayValue = false)]
public int? VersionID { get; set; }
public virtual ProductVersion Version { get; set; }
[HiddenInput(DisplayValue = false)]
public int? StatusID { get; set; }
public virtual ProductStatus Status { get; set; }
public virtual ICollection<CustomerProductDetail> ProductDetails { get; set; }
}
public class CustomerProductDetail
{
[HiddenInput(DisplayValue = false)]
public int ID { get; set; }
[HiddenInput(DisplayValue = false)]
public int CustomerProductID { get; set; }
public virtual CustomerProduct CustomerProduct { get; set; }
[HiddenInput(DisplayValue = false)]
public int ProductTypeDetailID { get; set; }
public virtual ProductTypeDetail ProductTypeDetail { get; set; }
//[Required(ErrorMessage = "Please enter a value")]
public string Value { get; set; }
}
So above I have the customer class. Each customer can be set up with any number of the product types you have set up and you can select values for the product details of that product type for this particular customer. The customers also contain contacts. that is the class that has the email addresses.
So I need to show a screen that displays all the product types you have set up and lets you select values for detail items on products you select then I need to query and find customers that match this
All I needed to do was serialize the form and pass it as data in my ajax call. If on the Controller side that is getting called I have an argument that is of the same type as the model my view is strongly typed to the model binder is smart enough to fill in my object automatically