Reputation: 664
I am currently trying to make a custom constraint with the C# Entity Framework. In more detail:
I have a number field which only can have certain values (for example 1, 2 and 3). How do I achieve this constraint in a code first environment?
Upvotes: 4
Views: 1143
Reputation: 109109
Entity Framework automatically validates any validations you add to your model by ValidationAttribute
s. RequiredAttribute
or RangeAttribute
are two examples of built-in subclasses of this attribute.
If you want some custom validation, the most convenient way is to piggyback on this mechanism and create you own ValidationAttribute
subclass.
If you want to validate a non-contiguous range of values you can't use RangeAttribute
but you could make an own attribute, for instance like this:
public class AllowedValuesAttribute : ValidationAttribute
{
private readonly ICollection<object> _validValues;
private readonly Type _type;
public AllowedValuesAttribute(Type type, object[] validValues)
{
_type = type;
_validValues = validValues.Where(v => v != null && v.GetType() == type).ToList();
}
public override bool IsValid(object value)
{
return value.GetType() == _type && _validValues.Contains(value);
}
public override string FormatErrorMessage(string name)
{
return string.Format("Value for '{0}' is not an allowed value", name);
}
}
Usage:
[AllowedValues(typeof(int), new object[] { 1, 2, 4, 8, 16 })]
public int Base { get; set; }
Note that we have to use fixed values here, because the content of the attribute must be known at compile time. Also, we have to use object
because (currently) C# doesn't support generic attributes. Apart from that, there are numerous options. For example, the attribute could also have a method that finds allowed values at runtime, maybe from a named source, so you can supply this name in its constructor.
I don't see any problem in adorning entity classes with validation attributes. The entity model is not a domain model, it's part of a data access layer. It's primary purpose is (and should be) to facilitate an application's data access. If an entity model also happens to support business logic that's a mere bonus.
Upvotes: 3
Reputation: 5946
It's very bad practice to add dataannotations in domain-model, like D.Mac wrote. So what about doing it the nicer way?
public MyClass
{
private int myNumberField;
public int MyNumberField
{
get { return myNumberField; }
set
{
if (value >= 1 && value <=3)
myNumberField = value;
else
// throw exception?
// set default-value (maybe 1)?
// do nothing?
}
}
}
you could do whatever you want in the setter of your property
and only restricting it in the front-end is not the best solution, since you always can modify javascript/html - but you should show the user, that he only can insert values 1, 2 or 3. Also restrict it in the viewmodel with data annotations.
OR:
you could also override EntityFrameworks SaveChanges
and add your businesslogic:
public override int SaveChanges(SaveOptions options)
{
foreach (ObjectStateEntry entry in
ObjectStateManager.GetObjectStateEntries(
EntityState.Added | EntityState.Modified))
{
// Validate the objects in the Added and Modified state
// if the validation fails, e.g. throw an exeption.
}
return base.SaveChanges(options);
}
Upvotes: 0