I have made a remote validation in my project, to avoid duplicate entries in DB. My model class is like this
public class Supplier
{
public int SupplierId { get; set; }
public string SupplierName { get; set; }
[Required, DisplayName("Supplier Code")]
[Remote("ViCodeExists", "Supplier", "Vi Code is already exists.", AdditionalFields = "SupplierId")]
public string SupplierCode { get; set; }
}
And inside my SupplierController I have the function like this
public JsonResult ViCodeExists(string SupplierCode, int SupplierId = 0)
{
var user = _db.Suppliers.Where(x => x.SupplierCode == SupplierCode.Trim() && x.SupplierId != SupplierId);
return !user.Any() ?
Json(true, JsonRequestBehavior.AllowGet) :
Json(string.Format("{0} is already exists.", SupplierCode),
JsonRequestBehavior.AllowGet);
}
In my create View
#Html.TextBoxFor(model => model.SupplierCode)
#Html.ValidationMessageFor(model => model.SupplierCode)
Everything looks okay to me, but this validation does not works. I have tried adding breakpoint inside controller, But it never get hit. Can any one point out What I am doing wrong here?
Note: I have same type of validation in some other controllers in the
same project and they all work well. Issue is with this one only.
You using the overload of RemoteAttribute that accepts 3 string parameters where the 3rd parameter is the area name (not an error message).
Change the attribute to
[Remote("ViCodeExists", "Supplier", ErrorMessage = "Vi Code is already exists.", AdditionalFields = "SupplierId")]
public string SupplierCode { get; set; }
Note your overriding the error message in the methods return statement anyway, so you can probably omit it and just use
[Remote("ViCodeExists", "Supplier", AdditionalFields = "SupplierId")]
public string SupplierCode { get; set; }
Related
I am getting an error in my mvc application. I am trying to display record from my sql table using entity framework. here is the code which i am trying. But I dnt know why this code giving me error
This is my model
[Table("tbInsertMobile")]
public class tbInsertMobile
{
[Key]
public int MobileID { get; set; }
public string MobileName { get; set; }
public string MobileIMEno { get; set; }
public string mobileprice { get; set; }
public string mobileManufacured { get; set; }
}
my another class in model
public class EmployeeContext:DbContext
{
public DbSet<tbInsertMobile> usermobiles { get; set; }
}
This the code of my contoller
public ActionResult Index(int id)
{
EmployeeContext employeeContext = new EmployeeContext();
tbInsertMobile employee = employeeContext.usermobiles.Single(x => x.MobileID == id);
return View(employee);
}
and now the error which i am getting is this
The 'MobileID' property on 'tbInsertMobile' could not be set to a 'System.Int64' value. You must set this property to a non-null value of type 'System.Int32
Experts Tell me where I am getting this error
Thanks
The answer is there already in the comment.
It's trying to assign a long to an int. Change MobileID to be long. – CodeCaster
I'm working on an MVC application and i'm trying to implement some validation. I've strucuture the site to use EF for storage and a set of view models with automapper.
I want to add some validation which i'm sure would work if i added it to the View Models however i'm assuming it would be better to put validation in with the EF model so if in the future i create another interface the same validation would also apply.
First of is this the correct approach and second how do i get MVC to actually test the validation before saving the object. Currently it just skips my EF validation.
The address model is auto generated so i created this partial class to add the validation:
public partial class Address : IValidatableObject
{
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (!string.IsNullOrWhiteSpace(this.AddressLine1) &&
!string.IsNullOrWhiteSpace(this.AddressLine2) &&
!string.IsNullOrWhiteSpace(this.AddressLine3) &&
!string.IsNullOrWhiteSpace(this.Town) &&
!string.IsNullOrWhiteSpace(this.City) &&
!string.IsNullOrWhiteSpace(this.County) &&
!string.IsNullOrWhiteSpace(this.Postcode))
yield return new ValidationResult("Address cannot be blank.");
}
}
This is my view model class with the display names changed
public class AddressVM
{
public int? ID { get; set; }
[Display(Name = "Address line 1")]
public string AddressLine1 { get; set; }
[Display(Name = "Address line 2")]
public string AddressLine2 { get; set; }
[Display(Name = "Address line 3")]
public string AddressLine3 { get; set; }
[Display(Name = "Town")]
public string Town { get; set; }
[Display(Name = "City")]
public string City { get; set; }
[Display(Name = "County")]
public string County { get; set; }
[Display(Name = "Postcode")]
public string PostCode { get; set; }
}
This is my controller
public ActionResult AddAddress(AddressVM vm)
{
IncidentAddress theAddress = Mapper.Map<AddressVM, Address>(vm);
if (ModelState.IsValid)
{
UOW.Addresses.Add(theAddress);
UOW.Save();
}
return PartialView("AddressVM-edit", vm);
}
if (ModelState.IsValid)
This will always be true for your object, as it will look for validity of your model, which is AddressVM (you receive that from view so this is your model) and does not have any validators. ModelState does not know that you have mapped this object to some other which implements validation. You need to run validation on your other object manually and add validation errors to ModelState.
If you want to have this separated, you can implement IValidatableObject on AddressVM, and internally perform validation by creating a instance of Address, mapping it from AddressVM (this) and returning result of it's Validate method. You also can expose the same constructed Address object as a property and use it to perform entity operation.
Example of AddressVM:
public class AddressVM : IValidatableObject
{
public int? ID { get; set; }
[Display(Name = "Address line 1")]
public string AddressLine1 { get; set; }
[Display(Name = "Address line 2")]
public string AddressLine2 { get; set; }
[Display(Name = "Address line 3")]
public string AddressLine3 { get; set; }
[Display(Name = "Town")]
public string Town { get; set; }
[Display(Name = "City")]
public string City { get; set; }
[Display(Name = "County")]
public string County { get; set; }
[Display(Name = "Postcode")]
public string PostCode { get; set; }
//// I added this and interface in class definition:
public IncidentAddress GetIncidentAddress()
{
return Mapper.Map<AddressVM, Address>(this);
}
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
return this.GetIncidentAddress().Validate(validationContext);
}
}
This way your logic stays in your business object, and your viewmodel uses it without having copy of it or some other dependency.
The Address class and AddressVm are not bound to each other in your case - AutoMapper does not do validation stuff, it just copies values. So you do not get ModelState populated and validations performed.
There're two workarounds i'm thinking of
Define the validations on AddressVm. If ModelState.IsValid, then map AddressVm to Address and save.
You do not need AddressVm at all. Change Action signature to expect Address parameter. That way, ModelState.IsValid will be automatically populated by validation system (Not the best solution).
Ideally, ViewModels should be defined for specific scenarios. In your case, I would define AddAddressModel, use it only for adding addresses and define only the properties needed to create address. Then, define validations on AddAddressModel and use mapper to map ViewModel to Address instance (So, I prefer first solution, plus defining specific model).
If you need reusable validator classes, you could check out FluentValidation. It has good support of asp.net-mvc too.
Hi I have an MVC app that I used to insert update invoices:
public class Invoice : IEntity, IValidatableObject
{
public virtual int InvoiceId { get; set; }
[Required(ErrorMessage = "Invoice Number is a required field.")]
[Column(TypeName = "varchar")]
[StringLength(20)]
[Display(Name = "Invoice Number:")]
public virtual string InvoiceNumber { get; set; }
[Required(ErrorMessage = "Organisation is a required field.")]
[Display(Name = "Organisation:")]
public int OrganisationId { get; set; }
...
}
The problem is I have a requirement "The combination of organisation and invoice number must be unique.
So this has been set up by the DBA in the database. So if I try to do this it will return an exception.
Is there a way instead of displaying the exception to catch it and add a custom error message to the validation summary?
You could catch the exception and add the error to the model state so that your client code handles all errors the same way. Something like:
ModelState.AddModelError("InvoiceNumber", "The combination of organisation and invoice number must be unique.");
you can achieve this by putting remote attribute on both properties and in AdditionalField parameter you pass the name of other property like
[Remote("IsUnique","home",AdditionalFields = "OrganisationID",ErrorMessage = "abc")]
virtual string InvoiceNumber { get; set; }
[Required(ErrorMessage = "Organisation is a required field.")]
[Display(Name = "Organisation:")]
[Remote("IsUnique","home",AdditionalFields = "InvoiceNumber",ErrorMessage = "abc")]
public int OrganisationId { get; set; }
and you can write InUnique method in home controller (for instance) like
public JsonResult IsUnique(string InvoiceNumber, int? OrganisationID)
{
if(InvoiceNumber == null || !Organisation.HasValue)
{
return Json({valid = true});//null check is not job of this attribute
}
else
{
bool result = CheckDbForUniqueness(InvoiceNumber, OrganisationID.Value);
return Json({valid = result});
}
}
This method will be invoked when you change value of either inputs on the form and take the other value as parameter. if either value is null it would return true and null checking will be handled by Required attributes.
Without getting fancy...you could catch it in the controller, then display the message in the view via a member of the model or ViewBag/ViewData.
You can also write a custom validator that checks this for you (even with some client-side validation as well). I wrote one that checked a database for whether a username existed or not, a similar check could be done for this, without relying on exceptions (although you still would want to handle it I wouldn't recommend using exceptions for a normal course of action).
What is the right way to use (Try)UpdateModel?
When I run this:
TryUpdateModel returns true,
ViewData has no errors,
but my Proxy is not updated.
Action Method
public void Save(string TypeName, int Id, FormCollection idontknow) {
var types = Assembly.GetExecutingAssembly().GetTypes();
var ObjectType=(from t in types where t.Name == TypeName select t).First();
var Proxy = context.Set(ObjectType).Find(Id); // EF 4.1
if (TryUpdateModel(Proxy, TypeName)) {
var x = ViewData.GetModelStateErrors(); // no errors
}
}
Posted Data
TypeName=Thing&Id=1&Thing.Id=1&Thing.Name=hello&Thing.OptionID=2
Thing Class
public class Thing : Base {
public virtual Nullable<int> OptionID { get; set; }
public virtual Option Option { get; set; }
public virtual ICollection<ListItem> ListItems { get; set; }
}
public class Base {
public virtual int Id { get; set; }
public virtual string Name { get; set; }
[NotMapped]
public virtual int? EntityState { get; set; }
}
EDIT: I also tried passing the form collection explicitly
TryUpdateModel(Proxy, TypeName, idontknow)
EDIT #2: (in response to NickLarsen)
Restarted VS and server, no change.
Values are actually in the FormCollection.
Mock data works! I know I must be messing up something here.
Using debugger to check values.
I stripped all the EF stuff and tried to get just that query string to populate the model with the values... and it worked just fine.
//controller class
public ActionResult Save(string TypeName, int Id, FormCollection idontknow)
{
var Proxy = new Thing
{
Id = 33,
OptionID = 2234,
Name = "tony",
};
if (TryUpdateModel(Proxy, TypeName))
{
ViewBag.Message = "WInner";
}
return RedirectToAction("Index");
}
//end controller class
public class Thing : Base
{
public virtual Nullable<int> OptionID { get; set; }
}
public class Base
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
}
Honestly I can't figure think of what in your code would keep it from working, but I would suggest going through the list 1 by one and testing after each step...
Save your progress and restart VS and your development server
Check that the values are actually in the form data, maybe something is getting in the way there.
Mock up some trash data like I did. (checking if the problem has something to do with EF)
How are you identifying that Proxy isn't being updated? In the debugger, on the page, etc?
Edit your question with the answer to all of the above questions.
Perhaps I'm missing something here, but it seems that anything in the object model tree 3 or more levels down, is ignored when using TryUpdateModel.
For example (simplified):
public virtual ActionResult SomeAction(int id, FormCollection form)
{
IValueProvider vpFrom = form.ToValueProvider();
/*
At this stage, vpForm contains:
1)PropertyA
2) PropertyB.SubPropertyA
3) PropertyB.SubPropertyB.SubSubPropertyA
*/
TryUpdateModel(someObjectModel, null, null, null, vpFrom);
//The first two properties are applied, number (3) seems to be ignored
Am I missing something here? If this is just the way it is, has anyone come up with a workaround?
A quick project created with the following model.
public class TestModel {
public TestModelA A { get; set; }
public string Name { get; set; }
}
public class TestModelA {
public TestModelB B { get; set; }
public string Name { get; set; }
}
public class TestModelB {
public TestModelC C { get; set; }
public string Name { get; set; }
}
public class TestModelC {
public TestModelD D { get; set; }
public string Name { get; set; }
}
public class TestModelD {
public TestModelE E { get; set; }
public string Name { get; set; }
}
public class TestModelE {
public string Name { get; set; }
}
Here's my edit - which is essentially the same as yours
[HttpPost]
public ActionResult Edit(FormCollection form) {
IValueProvider vpFrom = form.ToValueProvider();
Models.TestModel t = new Models.TestModel();
TryUpdateModel(t, null, null, null, vpFrom);
return View(t);
}
This all works exactly as expected with all the models created properly. The only problem that I can see happening is that you possibly aren't passing the same property names back from the form. (by not using <%: Html.TextBoxFor(model => model.A.B.C.CName)%> for example)
The models require parameterless constructors. But I'm sure you would have gotten an error about that - unless you're consuming the error.
So without more information about your project it will be hard to help as a basic setup produces expected results.
I believe the problem is in one of your model classes. Check, please, if PropertyB.SubPropertyB.SubSubPropertyA is really a property but not a field. A property should have get and set accessors.
Here's my checklist:
Make sure you're getting the value back in the form request. Request["A.B.C.Name"] and etc.
All the required fields are on the form.
I had deleteOnNull issue with Linq to SQL: How to set DeleteOnNull from designer for future ref if you're using L2SQL.