My user model has an int property called active (its an int because I didn't create the model nor the database, its getting used as a boolean..). I'm creating the action /Users/Edit/ that will be editting the model, so I've got a form with a checkbox for the active property. When the submit button is pressed the model binder will put all the form data together into one single object and pass it as a parameter to /Users/Edit.
But when I try to save the changes to the database, I check ModelState.isValid and it returns false because the model binder won't change the boolean into an integer.
How can I solve this problem?
1) Edit Db to make it a bit
2) Change the property on your view model to a bool
3) Use DisplayFor in your Razor View
It will now automatically generate a checkbox and check it accordingly.
Let's assume worst case that you cannot change your Db to a bool. Then you would use a View Model to perform the data transformation for you.
Say your user model is such:
public class StaticModel {
public string description { get; set; }
public int IsActive { get; set; }
}
Then you create a view model (facade style):
public class StaticModelFacade {
StaticModel _value;
public StaticModelFacade(StaticModel value) {
_value = value;
}
public string description {
get {return _value.description;}
set {_value.description = value;}
}
public bool IsActive {
get { return _value.IsActive = 1; }
set { _value.IsActive = value ? 1 : 0 }
}
}
Your view now binds to a bool and all is wired up Ok.
Related
I have a model with enum property which based on 'int'.
I need to validate that this property is not empty. But NotEmpty forbids 0 value. And NotNull just doesn't work because enum property cannot be null.
I cannot make my property nullable.
How can I do such validation?
As long as the enum type is int you can do the following:
public class Status
{
public StatusType type { get; set; }
}
public enum StatusType
{
open = 1,
closed = 2
}
public class StatusValidator : AbstractValidator<Status>
{
public StatusValidator()
{
RuleFor(x => x.type).Must(x => x != 0);
}
}
If you can't avoid 0 you can define a workaround for the model as follow (source: Choosing the default value of an Enum type without having to change values):
[Note: you need to include using System.ComponentModel;]
public class Status
{
public StatusType type { get; set; }
}
[DefaultValue(_default)]
public enum StatusType
{
_default = -1,
test = 0,
open = 1,
closed = 2,
}
public static class Utilities
{
public static TEnum GetDefaultValue<TEnum>() where TEnum : struct
{
Type t = typeof(TEnum);
DefaultValueAttribute[] attributes = (DefaultValueAttribute[])t.GetCustomAttributes(typeof(DefaultValueAttribute), false);
if (attributes != null &&
attributes.Length > 0)
{
return (TEnum)attributes[0].Value;
}
else
{
return default(TEnum);
}
}
}
public class StatusValidator : AbstractValidator<Status>
{
public StatusValidator()
{
RuleFor(x => x.type).Must(x => x != Utilities.GetDefaultValue<StatusType>());
}
}
I suppose you want to validate the model in a mvc controller but you should be more clear about your utilization context.
I think the model should be as wide as possible in terms of types to fit any possible choice the user make at UI level, for example always using nullable types. When the model binding try to build the object it match property names to request keys/values and set the matching value into the property. When it dosen't find any match in the request it leaves the property to its default value(0 in case of int). In that case the only way you have to know if the user left the field empty or intentionally wrote a zero value in it is to check the model state. Well in the first case an error (field can not be null...etc etc) will be tracked in the model state and checking the model state you can be aware if the user set the value or not. Fluent validation comes into play after the model binding and it relies on the work of model binder itselfe and poor him can't really understand what zero really mean (empty/missing value or zero value).
I want to assign my url parameters to Model properties, passed as a parameter to the associated Action. For example;
Say, my url is http://www.example.com/Item/Index?color=red&size=50
My action inside the controller is like below:
public class ItemController : Controller
{
public ActionResult Index(MyModel myModel)
{
//
return View(myModel);
}
}
I want to configure the model or whatever necessary so that my model takes the color and size as field values. The following didn't work:
public class MyModel
{
[Display(Name = "color")]
public string Color{ get; set; }
[Display(Name = "size")]
public string Size{ get; set; }
}
What would be the correct way to solve the problem?
Thanks for any suggestion.
Update
Well, yes! The code above would work correctly, because Url parameter names are the same as model property names. I should explain my problem exactly as I encounter for the next time, sorry.
I must correct a part of my question to make it clear. The url should have been: http://www.example.com/Item/Index?c=red&s=50 to detect the problem.
If the url is like that, the code would not work. Because Url parameters don't have the same name as Model properties.
Updated model is below:
public class MyModel
{
[Display(Name = "c")]
public string Color{ get; set; }
[Display(Name = "s")]
public string Size{ get; set; }
}
Try adding [FromUri] in front of the parameter.
public class ItemController : Controller
{
public ActionResult Index([FromUri] MyModel myModel)
{
// do something
return View();
}
}
debugging the issue
Here are some suggestions in debugging the issue, as it should work out of the box.
try binding to primitive types
public class ItemController : Controller
{
public ActionResult Index(string color, string size)
{
// do something
return View();
}
}
Try reading out of the request object directly
var size = this.Request["size"];
If either of those work there is an issue with your model binding.
Update
If you want to have the query string parameters different to the model in MVC you'll need to have a custom model binder. Take a look at Asp.Net MVC 2 - Bind a model's property to a different named value and http://ole.michelsen.dk/blog/bind-a-model-property-to-a-different-named-query-string-field.html which extends the answer a little.
https://github.com/yusufuzun/so-view-model-bind-20869735 has an example with some html helpers that could be useful.
I was trying to get my CompanyProfile ID in my UserController
in the first step i get the right ID but in the [HttpPost] the ID value was change into the same value as the user ID
This is my controller
public ActionResult Edit (int id)
{
User user = _db.Users.Find(id);
if ( user == null)
{
return HttpNotFound();
}
ViewBag.CompanyProfile = new SelectList(_db.CompanyProfiles,"ID","NamaProfil", user.CompanyProfile.ID);
return View(user);
}
[HttpPost]
public ActionResult Edit(User user)
{
if (ModelState.IsValid)
{
_db.Entry(user).State = EntityState.Modified;
_db.SaveChanges();
return RedirectToAction ("Index");
}
return View(user);
}
this is the Model
public int ID {get;set;}
public virtual CompanyProfile CompanyProfile {get;set;}
and this is the dropdown in the edit view
<div class= "controls">
#Html.DropDownList("CompanyProfile", null, "--Choose Company--", new {#class="span6 m-wrap"})
</div>
every answer may help,thanks :D
forget to tell this,this is the error message
There is no ViewData item of type 'IEnumerable' that has the key 'CompanyProfile'.
You have a number of issues here. The model you are passing in to your view has a property named CompanyProfile of reference type CompanyProfile. This type cannot be bound to IEnumerable of any kind - those two models simply don't match. Your model property needs to be of type int to match with the selected value you are passing in - user.CompanyProfile.ID.
2nd issue is that you should never name your model property the same as any ViewBag value. Again you have a property named CompanyProfile and you have a ViewBag.CompanyProfile. Those two will be overwriting each other during the model binding...
I suggest you do the following: In your controller bind the select list to a ViewBag without defining the selected value:
ViewBag.CompanyProfileList = new SelectList(_db.CompanyProfiles,"ID","NamaProfil");
Then your model should have the selected company profile id property:
public int CompanyProfileId { get; set; }
And then bind it in your view like this:
#Html.DropDownListFor(m => m.CompanyProfileId, (SelectList)ViewBag.CompanyProfileList, "--Choose Company--", new { #class = "span6 m-wrap" })
How can I make JSON.NET / MVC 4 WebAPI reject integer values for which the enum has no member? Eg:
If I have this model:
public enum Colour { Red = 1 };
public class Model
{
public Colour Colour { get; set; }
}
Model Post(Model model)
{
// model.Colour could be 99, 34234234, 0 etc, etc
}
If I post { Color: 9999 }, I end up with a model where model.Color = 999 and I want to return a Bad Request status code instead.
It turns out the EnumDataTypeAttribute, which comes with the out-of-the-box ValidationAttributes in the System.ComponentModel.DataAnnotations namespace, does an Enum.Defined check.
Once I applied this attribute to my view model, out-of-range integer values failed validation:
public enum Color {Red = 1, Blue = 2}
public class Car
{
[EnumDataType(typeof(Color))]
public Color Color { get; set; }
}
Note: values that can be parsed into integers that are defined on the enum will still pass validation due to the default behavior of enum model binding. This means, for example, true will be parsed as 1, which would be valid for this enum. I assume characters that can be mapped to integers will also work.
If you only want one flavor of enum parsing to work, be it string or integer, consider using that specific type on your view model, and then write a custom ValidationAttribute that takes in the enum type, validating that the string or integer on your view model matches a value in the enum.
One option is to write a validator:
public class ValidEnumValueAttribute : ValidationAttribute
{
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
Type enumType = value.GetType();
bool valid = Enum.IsDefined(enumType, value);
if (!valid)
{
return new ValidationResult(String.Format("{0} is not a valid value for type {1}", value, enumType.Name));
}
return ValidationResult.Success;
}
}
Use as:
public enum Color {Red = 1, Blue = 2}
public class Car
{
[ValidEnumValue]
public Color Color { get; set; }
}
In the controller, ModelState.IsValid would be false.
You can also throw a ValidationException, if you really want to fail the request, but I'm not quite sure that is how they should be used.
We have some old tables with legacy schemas that we find it hard to work with.
Is it possible to use NHibernate to transform some of this data into a nicer model?
For example we might have an integer status column that we want to break down into several properties.
Legacy status: 0 - Active, 1 - Inactive, 2 - TemporarilyInactive, 3 - etc
We'd want to have something like:
bool IsActive { get; set; }
Status Status { get; set; }
(where Status is an enum)
I know that we can use a protected field that can take the status and then define the getters for the extra properties to return the appropriate value based on the backing field, but I'm pretty sure that this will disable the ability to query based on these properties.
Through this however, we wouldn't be able to do queries such as .Query(p => p.IsActive) and get that translated to SQL such as where status = 0, right?
Is the only way through custom IUserTypes? If so, is there any helper framework that makes working with IUserType easier in order to achieve this?
How do other people handle this?
You can create your own "enumeration" class.
public class Status
{
//The numeric (legacy) code
public int Code{ get;private set; }
//The human readable name
public string Name{ get; private set; }
//If this status is an active status
public bool IsActive { get; private set; }
private Status(int aCode, string aName, bool anIsActive)
{
Code = aCode;
Name = aName;
IsActive = anIsActive;
}
public static Status ACTIVE = new Status(0, "Active");
public static Status INACTIVE = new Status(1, "Inactive");
//Other status here...
private static Status[] STATUSES = {Active,Inactive,...};
//Returns a status based on the passed in code
public static Status GetByCode(int aCode)
{
return STATUSES.FirstOrDefault(aStatus => aStatus.Code == aCode);
}
}
Then you can have NHibernate set a private variable with the legacy value and have a getter/setter that converts between the enumeration and the legacy value.
private int theLegacyStatus; //This is the value that NHibernate sets
public Status
{
get
{
return Status.GetStatusByCode(theLegacyStatus);
}
set
{
theLegacyStatus = value.Code;
}
}
You can then use this enumeration in NHibernate queries: .Query(p => p.Status.Code)