FluentValidation NotNull on enum values - fluentvalidation

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).

Related

EF Core 3.1 property value returns default field value

I have a class property of enum type LogLevel (using Microsoft.Extensions.Logging) being stored in my database and a class field that ins't mapped but stores the enum type value as such:
class ...
{
private LogLevel LevelName;
[Required]
public int Level { get { return (int)LevelName; } set { LevelName = (LogLevel)Level; } }
}
But since the field has to be initialized as default, every time I retrieve the value from the database I get back the default value of 0 for Level even though its really 3 or something. If the value is never being set, How can I initialize the correct default value?
You can use enums in Entity Framework Core models:
public class YourModel
{
public UserSearchStatus SearchStatus { get; set; }
}
with an enum like this.
public enum UserSearchStatus
{
StatusOne = 1,
StatusTwo = 2
}
If you don't want to use a default value, make the property nullable:
public class YourModel
{
public UserSearchStatus? SearchStatus { get; set; }
}

Validate Property as [Required], but don't validate the value itself. Is it possible with Attributes and conventions in .net core 2 or higher?

public class Post {
// ... other properties here
public Author { get; set; }
}
public class Author {
// ... other properties here
public List<Post> Posts { get; set; }
}
This is an overly-simplistic example. What I basically want is that the Author property in the Post class is never null, it MUST have a value. But because I'm pulling Author from a database, I am 100% sure that it's valid, so I want to skip the validation of the value of the Author property's value
In .net core 3 preview 9, recursive validation is causing unnecessarily long hold-ups where join entities for many-to-many relationships are involved.
I assume one workaround is to use [ValidateNever] in conjunction with the Validate() method just to check that the Author is not null. But I don't know if one of them will override the other.
But I am ideally looking to achieve this with Attributes and/or conventions
You could write your custom validation attribute.When the value is null, it returns an Validation error.Otherwise, it returns ValidationResult.Success.
public class CustomRequiredAttribute :ValidationAttribute
{
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
if(value != null)
{
return ValidationResult.Success;
}
else
{
var propertyName = validationContext.DisplayName;
var type = validationContext.ObjectType;
return new ValidationResult("Could not be null for " + propertyName);
}
}
}
Model:
public class Post {
// ... other properties here
[CustomRequired]
public Author { get; set; }
}

Model binder wont pass checkbox value into integer property

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.

MVC4 WebAPI reject invalid enum values

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.

Transforming legacy values with NHibernate

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)