I\'m trying to make a custom validation [IsUnique]. That check if is property value is unique and return a proper message.
This is my code, but this only work for a spec
When writing Validation Attributes, you can use ValidationContext to gain some information about validation such as Name of Property that you are validating, Type of object that you are validating and so on.
So you don't need to declare which property you want to check for uniqueness, or which entity you should check, or event you don't need to retrieve value using reflection, because the value has been passed to IsValid method.
When using DbContext, you canexecute Sql queries, so you can check for uniqueness using sql query simply. It is more simple than try to Create generic linq query on the fly.
May be this idea help you. Here is some changes in your code according to the idea:
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
var db = new YourDBContext();
var className = validationContext.ObjectType.Name.Split('.').Last();
var propertyName = validationContext.MemberName;
var parameterName = string.Format("@{0}", propertyName);
var result = db.Database.SqlQuery<int>(
string.Format("SELECT COUNT(*) FROM {0} WHERE {1}={2}", className, propertyName, parameterName),
new System.Data.SqlClient.SqlParameter(parameterName, value));
if (result.ToList()[0] > 0)
{
return new ValidationResult(string.Format("The '{0}' already exist", propertyName),
new List<string>() { propertyName });
}
return null;
}
To use this attribute, simply put [IsUnique] above your property.
[IsUnique]
YourProperty { get; set; }
Then run a test using such code:
var db = new YourDbContext();
db.Configuration.ValidateOnSaveEnabled = true;
db.Categories.Add(new YourEntity() { YourProperty = "DuplicateName" });
db.SaveChanges();
It is a good practice to validate only such aspect of your entity using attributes, that can be validated offline.
Validation Attributes like StringLength, RegularExpression, Required and such validations are examples of good attributes and Validation Attributes that checks for uniqness or other database related rules are examples of inappropriate attributes.
I think the best way is to let the database do its works.
Create a constraint in the database to prevent two articles have the same name (or whatever uniqueness you need). Then, when the user create a new article or update an existing one with an existing article name, the database will throw an exception. Catch that exception and let the user knows about the issue.
It would have been nice if there were generic attributes, but such are not supported. However, you can try using the Set
method of the DbContext
which takes the entity type as a parameter. To query the non-generic DbSet
you can use the System.Linq.Dynamic
library (you can add it from NuGet). It allows to query the DbSet
using string predicates. Here is an example:
var existingEntityQuery = myContext.Set(validationContext.ObjectType)
.Where("Name= @0", (string)value);
var enumerator = existingEntityQuery.GetEnumerator();
if (enumerator.MoveNext())
{
var entity = enumerator.Current;
if (entity != null)
{
return new ValidationResult("The name already exist", propertiesList);
}
}