I have a class like this
public class MyClass
{
public MyClass()
{
Enumlist = new List<MyEnum>();
}
public virtual List<MyEnum> Enumlist { get; set; }
}
and the enum is
public enum MyEnum
{
Enum1=1,
Enum2=2,
Enum3=3
}
but in my view i keep having this eror
"The value 'System.Collections.Generic.List`1[MyEnum]' is not valid for Enumlist"
I did not specify any validation attribute for the property EnumList, so i don't why the automatic error.
Please, can someone help with this?
In Asp.Net MVC 2 it is a default behaviour of DataBinding. If you have a Date field in your model it will automatically add validation error when the binding fails for the date. Same is true for enum.
I do not know if a list of an enum type is illegal in ASP.Net MVC but i solved this problem by using enum flag attribute and bit operations on the enum to combine the enum values.
This article (http://www.codeproject.com/Articles/37921/Enums-Flags-and-Csharp-Oh-my-bad-pun.aspx) on codeproject could help anyone having this same problem.
Happy coding.
Related
I am using Hibernate Validator - 5.2.2 (JSR 303).
I am doing a cross field validation, so I need to create a custom validator.
However I have no idea how to do the custom conditional nested validation.
example:
#ValidChildrenIfEnabled
public class MainDto {
public boolean isEnabled;
public List<Child> children;
}
public class Child {
#NotBlank
public String name;
#Range(min = 1, max = 3)
public int age;
}
If I don't need conditional validation, I would put
#Valid on top of "children".
#Valid
public List<Child> children;
Note: I know how to create a custom validator, I just don't know how to create a custom validator that do nested validation that take advantage of existing built-in validator. Thanks!
EDIT:
My payload actually has one more payload, let's say SuperDto.
public class SuperDto {
#Valid
public List<MainDto> mainDtos;
}
And I do validation like this:
validator.validate(superDto);
Interesting use case. Unfortunately, I don't think you can do what you want to do as you can't trigger a validation from isValid().
Supposing that you have other constraints you want to validate in every cases, I think the best workaround is probably to use groups. And to use different groups depending of if isEnabled is true or not.
Anyway, you would have to special case how this bean is validated.
Details:
ASP.Net MVC 5
.Net 4.5
MVVM
Entity Framework 6 Code First
I have ViewModels with complex properties on them. I have read about how I can use the BindAttribute to include or exclude properties for model bidning and got basic properties working with this.
However one thing I cannot find is how to control the binding of child properties within a collection property. For example I have the following
[Bind(Include = "Model.Id, Model.Positions.StartDate")]
public class ProjectViewModel
{
public Project Model {get;set;}
public ProjectViewModel(Project project)
: base(project)
{
Model = project;
}
public ProjectViewModel()
{
}
}
A Project has a list of Positions:
public class Project : BaseEntity
{
.
.
.
public virtual IList<Position> Positions
{
get;
set;
}
}
And a Position has a start and end date:
public class Position : BaseEntity
{
[SensibleDateTime]
public DateTime StartDate { get; set; }
[SensibleDateTime]
public DateTime EndDate { get; set; }
[Required]
[ForeignKey("Project")]
public Int64 ProjectID { get; set; }
public virtual Project Project { get; set; }
}
I have a screen using the Project View Model with a grid of positions where the user is allowed to change the start and end date of those positions. I do not want to allow them to let them amend any other properties of the Position including by manipulating the request / post to the server.
I am writing my own custom model binder which inherits from the DefaultModelBinder which gets the original entity out the database. I just want to bind permitted bound fields on top to get these new values and all the original values will already exist.
I am aware I could get the original entity out the database inside the controller instead and map the bound start and end date onto the entity manually. I want to avoid this if possible and make the controller code as simple as possible as this will be a very common task. Additionally it seems like using standard MVC mechanisms would be the preferred approach.
I have tried the following bind statement on the Project View Model:
[Bind(Include = "Model.Positions.StartDate")]
None of the new values are bound including the start date of the positions. If I write a simple include for a property directly on the Project View Model or Model it works.
How do you write a Bind attribute statement that refers to a property within an entity collection?
I can't say for sure as I never use the Bind attribute, but the dot syntax should work. However, I'm reasonably sure that Bind focuses on the posted name, not the actual model property. In other words, you would most likely need to do something like "Positions[0].StartDate,Positions[1].StartDate,...".
In general, it's far better to just use view models to only include the properties you want to be editable.
The Big Picture:
I have found what seems like a limitation of Razor and I am having trouble coming up with a good way around it.
The Players:
Let's say I have a model like this:
public abstract class BaseFooModel<T>
where T : BaseBarType
{
public abstract string Title { get; } // ACCESSED BY VIEW
public abstract Table<T> BuildTable();
protected Table<T> _Table;
public Table<T> Table // ACCESSED BY VIEW
{
get
{
if (_Table == null)
{
_Table = BuildTable();
}
return _Table;
}
}
}
And a subclass like this:
public class MyFooModel : BaseFooModel<MyBarType>
{
// ...
}
public class MyBarType : BaseBarType
{
// ...
}
I want to be able to pass MyFooModel into a razor view that is defined like this:
// FooView.cshtml
#model BaseFooModel<BaseBarType>
But, that doesn't work. I get a run-time error saying that FooView expects BaseFooModel<BaseBarType> but gets MyFooModel. Recall that MyFooModel in herits from BaseFooModel<MyBarType> and MyBarType inherits from BaseBarType.
What I have tried:
I tried this out in non-razor land to see if the same is true, which it is. I had to use a template param in the View to get it to work. Here is that non-razor view:
public class FooView<T>
where T : BaseBarType
{
BaseFooModel<T> Model;
public FooView(BaseFooModel<T> model)
{
Model = model;
}
}
With that structure, the following does work:
new FooView<MyBarType>(new MyFooModel());
My Question:
How can I do that with Razor? How can I pass in a type like I am doing with FooView?
I can't, but is there any way around this? Can I achieve the same architecture somehow?
Let me know if I can provide more info. I'm using .NET 4 and MVC 3.
EDIT:
For now, I am just adding a razor view for each subclass of BaseFooModel<BaseBarType>. I'm not psyched about that because I don't want to have to create a new view every time I add a new model.
The other option is to just take advantage of the fact that I am able to get this working in regular c# classes without razor. I could just have my razor view #inherits the c# view and then call some render method. I dislike that option because I don't like having two ways of rendering html.
Any other ideas? I know its hard to understand the context of the problem when I'm giving class names with Foo and Bar, but I can't provide too much info since it is a bit sensitive. My apologies about that.
What I have so far, using Benjamin's answer:
public interface IFooModel<out T>
where T : BaseBarModel
{
string Title { get; }
Table<T> Table { get; } // this causes an error:
// Invalid variance: The type parameter 'T' must be
// invariantly valid on IFooModel<T>.Table.
// 'T' is covariant.
}
public abstract class BaseFooModel<T> : IFooModel<T>
where T : BaseBarModel
{
// ...
}
What ended up working:
public interface IFooModel<out T>
where T : BaseBarModel
{
string Title { get; }
BaseModule Table { get; } // Table<T> inherits from BaseModule
// And I only need methods from BaseModule
// in my view.
}
public abstract class BaseFooModel<T> : IFooModel<T>
where T : BaseBarModel
{
// ...
}
You need to introduce an interface with a covariant generic type parameter into your class hierarchy:
public interface IFooModel<out T> where T : BaseBarType
{
}
And derive your BaseFooModel from the above interface.
public abstract class BaseFooModel<T> : IFooModel<T> where T : BaseBarType
{
}
In your controller:
[HttpGet]
public ActionResult Index()
{
return View(new MyFooModel());
}
Finally, update your view's model parameter to be:
#model IFooModel<BaseBarType>
Using interfaces-based models was deliberately changed between ASP.NET MVC 2 and MVC 3.
You can see here
MVC Team:
Having interface-based models is not something we encourage (nor, given the limitations imposed by the bug fix, can realistically support). Switching to abstract base classes would fix the issue.
"Scott Hanselman"
The problem you are experiencing is not a Razor error, but a C# error. Try to do that with classes, and you'll get the same error. This is because the model is not BaseFooModel<BaseBarType>, but BaseFooModel<MyFooModel>, and an implicit conversion cannot happen between the two. Normally, in a program you'd have to do a conversion to do that.
However, with .NET 4, introduced was contravariance and covariance, which sounds like the ability of what you are looking for. This is a .NET 4 feature only, and I honestly don't know if Razor in .NET 4 makes use of it or not.
I've a ViewModel which has some DataAnnotations validations and then for more complex validations implements IValidatableObject and uses Validate method.
The behavior I was expecting was this one: first all the DataAnnotations and then, only if there were no errors, the Validate method. How ever I find out that this isn't always true. My ViewModel (a demo one) has three fileds one string, one decimal and one decimal?. All the three properties have only Required attribute. For the string and the decimal? the behavior is the expected one, but for the decimal, when empty, Required validation fails (so far so good) and then executes the Validate method. If I inspect the property its value is zero.
What is going on here? What am I missing?
Note: I know that Required attribute is suppose to check if the value is null. So I'd expect to be told not to use Required attribute in not-nullable types (because it wont ever trigger), or, that somehow the attribute understand the POST values and note that the field wasn't filled. In the first case the attribute shouldn't trigger and the Validate method should fire. In the second case the attribute should trigger and the Validate method shouldn't fire. But my result are: the attributes triggers and the Validate method fires.
Here is the code (nothing too special):
Controller:
public ActionResult Index()
{
return View(HomeModel.LoadHome());
}
[HttpPost]
public ActionResult Index(HomeViewModel viewModel)
{
try
{
if (ModelState.IsValid)
{
HomeModel.ProcessHome(viewModel);
return RedirectToAction("Index", "Result");
}
}
catch (ApplicationException ex)
{
ModelState.AddModelError(string.Empty, ex.Message);
}
catch (Exception ex)
{
ModelState.AddModelError(string.Empty, "Internal error.");
}
return View(viewModel);
}
Model:
public static HomeViewModel LoadHome()
{
HomeViewModel viewModel = new HomeViewModel();
viewModel.String = string.Empty;
return viewModel;
}
public static void ProcessHome(HomeViewModel viewModel)
{
// Not relevant code
}
ViewModel:
public class HomeViewModel : IValidatableObject
{
[Required(ErrorMessage = "Required {0}")]
[Display(Name = "string")]
public string String { get; set; }
[Required(ErrorMessage = "Required {0}")]
[Display(Name = "decimal")]
public decimal Decimal { get; set; }
[Required(ErrorMessage = "Required {0}")]
[Display(Name = "decimal?")]
public decimal? DecimalNullable { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
yield return new ValidationResult("Error from Validate method");
}
}
View:
#model MVCTest1.ViewModels.HomeViewModel
#{
Layout = "~/Views/Shared/_Layout.cshtml";
}
#using (Html.BeginForm(null, null, FormMethod.Post))
{
<div>
#Html.ValidationSummary()
</div>
<label id="lblNombre" for="Nombre">Nombre:</label>
#Html.TextBoxFor(m => m.Nombre)
<label id="lblDecimal" for="Decimal">Decimal:</label>
#Html.TextBoxFor(m => m.Decimal)
<label id="lblDecimalNullable" for="DecimalNullable">Decimal?:</label>
#Html.TextBoxFor(m => m.DecimalNullable)
<button type="submit" id="aceptar">Aceptar</button>
<button type="submit" id="superAceptar">SuperAceptar</button>
#Html.HiddenFor(m => m.Accion)
}
Considerations after comments' exchange:
The consensual and expected behavior among developers is that IValidatableObject's method Validate() is only called if no validation attributes are triggered. In short, the expected algorithm is this (taken from the previous link):
Validate property-level attributes
If any validators are invalid, abort validation returning the failure(s)
Validate the object-level attributes
If any validators are invalid, abort validation returning the failure(s)
If on the desktop framework and the object implements IValidatableObject, then call its Validate method and return any failure(s)
However, using question's code, Validate is called even after [Required] triggers. This seems an obvious MVC bug. Which is reported here.
Three possible workarounds:
There's a workaround here although with some stated problems with it's usage, apart from breaking the MVC expected behavior. With a few changes to avoid showing more than one error for the same field here is the code:
viewModel
.Validate(new ValidationContext(viewModel, null, null))
.ToList()
.ForEach(e => e.MemberNames.ToList().ForEach(m =>
{
if (ModelState[m].Errors.Count == 0)
ModelState.AddModelError(m, e.ErrorMessage);
}));
Forget IValidatableObject and use only attributes. It's clean, direct, better to handle localization and best of all its reusable among all models. Just implement ValidationAttribute for each validation you want to do. You can validate the all model or particular properties, that's up to you. Apart from the attributes available by default (DataType, Regex, Required and all that stuff) there are several libraries with the most used validations. One which implements the "missing ones" is FluentValidation.
Implement only IValidatableObject interface throwing away data annotations. This seems a reasonable option if it's a very particular model and it doesn't requires much validation. On most cases the developer will be doing all that regular and common validation (i.e. Required, etc.) which leads to code duplication on validations already implemented by default if attributes were used. There's also no re-usability.
Answer before comments:
First of all I've created a new project, from scratch with only the code you provided. It NEVER triggered both data annotations and Validate method at the same time.
Anyway, know this,
By design, MVC3 adds a [Required]attribute to non-nullable value types, like int, DateTime or, yes, decimal. So, even if you remove required attribute from that decimal it works just like it is one there.
This is debatable for its wrongness (or not) but its the way it's designed.
In you example:
'DataAnnotation' triggers if [Required] is present and no value is given. Totally understandable from my point of view
'DataAnnotation' triggers if no [Required] is present but value is non-nullable. Debatable but I tend to agree with it because if the property is non-nullable, a value must be inputted, otherwise don't show it to the user or just use a nullable decimal.
This behavior, as it seems, may be turned off with this within your Application_Start method:
DataAnnotationsModelValidatorProvider.AddImplicitRequiredAttributeForValueTypes = false;
I guess the property's name is self-explanatory.
Anyway, I don't understand why do you want to the user to input something not required and don't make that property nullable. If it's null then it is your job to check for it, if you don't wan't it to be null, before validation, within the controller.
public ActionResult Index(HomeViewModel viewModel)
{
// Complete values that the user may have
// not filled (all not-required / nullables)
if (viewModel.Decimal == null)
{
viewModel.Decimal = 0m;
}
// Now I can validate the model
if (ModelState.IsValid)
{
HomeModel.ProcessHome(viewModel);
return RedirectToAction("Ok");
}
}
What do you think it's wrong on this approach or shouldn't be this way?
We have a need within some of our logic to iterate through the properties of a model to auto-bind properties and want to extend the functionality to include the new dataannotations in C# 4.0.
At the moment, I basically iterate over each property loading in all ValidationAttribute instances and attempting to validate using the Validate/IsValid function, but this doesn't seem to be working for me.
As an example I have a model such as:
public class HobbyModel
{
[Required(AllowEmptyStrings = false, ErrorMessage = "Do not allow empty strings")]
[DisplayName("Hobby")]
[DataType(DataType.Text)]
public string Hobby
{
get;
set;
}
}
And the code to check the attributes is:
object[] attributes = propertyInfo.GetCustomAttributes(true);
TypeConverter typeConverter =
TypeDescriptor.GetConverter(typeof(ValidationAttribute));
bool isValid = false;
foreach (object attr in attributes)
{
ValidationAttribute attrib = attr as ValidationAttribute;
if (attrib != null)
{
attrib.Validate(obj, propertyInfo.Name);
}
}
I've debugged the code and the model does have 3 attributes, 2 of which are derived from ValidationAttribute, but when the code passes through the Validate function (with a empty or null value) it does thrown an exception as expected.
I'm expecting I'm doing something silly, so am wondering whether anyone has used this functionality and could help.
Thanks in advance,
Jamie
This is because you are passing the source object to the Validate method, instead of the property value. The following is more likely to work as expected (though obviously not for indexed properties):
attrib.Validate(propertyInfo.GetValue(obj, null), propertyInfo.Name);
You would certainly have an easier time using the Validator class as Steven suggested, though.
You do use the System.ComponentModel.DataAnnotations.Validator class to validate objects.