Уникальное свойство пользовательской проверки C# - универсальные классы
Я пытаюсь сделать пользовательскую проверку [IsUnique]. Это проверяет, является ли значение свойства уникальным и возвращает правильное сообщение.
Это мой код, но это работает только для указанного класса, можно ли сделать метод, который получает правильный класс по метаданным?
public class ArticleMetaData
{
[Required(AllowEmptyStrings = false)]
[IsUnique("Name")]
public String Name{ get; set; }
}
и моя пользовательская проверка:
class IsUnique : ValidationAttribute
{
public IsUnique(string propertyNames)
{
this.PropertyNames = propertyNames;
}
public string PropertyNames { get; private set; }
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
var myproperty = validationContext.ObjectType.GetProperty(PropertyNames);
var value = propiedad.GetValue(validationContext.ObjectInstance, null);
IEnumerable<String> properties;
List<string> propertiesList = new List<string>();
propertiesList.Add(myproperty.Name);
var dba = new myContext();
if (dba.Articles.Any(article => article.Name == (string)value))
{
return new ValidationResult("The name already exist", propertiesList);
}
return null;
}
}
идея состояла бы в том, чтобы просто использовать аннотацию [isUnique] , а метод принимает класс с аннотацией и ищет соответствующий сущность.
3 ответов
при написании атрибутов проверки можно использовать ValidationContext для получения некоторых сведений о проверке, таких как имя проверяемого свойства, тип проверяемого объекта и т. д.
таким образом, вам не нужно объявлять, какое свойство вы хотите проверить на уникальность, или какой объект вы должны проверить, или событие вам не нужно извлекать значение с помощью отражения, потому что значение было передано методу IsValid.
при использовании DbContext, вы можете выполнять Sql-запросы, поэтому вы можете просто проверить уникальность с помощью sql-запроса. Это проще, чем пытаться создать общий запрос linq на лету.
может быть эта идея поможет вам. Вот некоторые изменения в вашем коде в соответствии с идеей:
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;
}
чтобы использовать этот атрибут, просто поместите [IsUnique] над своим свойством.
[IsUnique]
YourProperty { get; set; }
затем запустите тест, используя такой код:
var db = new YourDbContext();
db.Configuration.ValidateOnSaveEnabled = true;
db.Categories.Add(new YourEntity() { YourProperty = "DuplicateName" });
db.SaveChanges();
Это хорошая практика, чтобы проверить только такой аспект вашего сущность, использующая атрибуты, которые можно проверить в автономном режиме.
атрибуты проверки, такие как StringLength, RegularExpression, Required и такие проверки являются примерами хороших атрибутов и атрибутов проверки, которые проверяют uniqness или другие правила, связанные с базой данных, являются примерами неподходящих атрибутов.
Я думаю, что лучший способ-позволить базе данных делать свою работу.
создайте ограничение в базе данных, чтобы две статьи не имели одного имени (или любой уникальности, которая вам нужна). Затем, когда пользователь создаст новую статью или обновит существующую с именем существующей статьи, база данных выдаст исключение. Поймайте это исключение и сообщите пользователю о проблеме.
было бы неплохо, если бы были общие атрибуты, но такие не поддерживаются. Тем не менее, вы можете попробовать использовать Set
метод DbContext
который принимает тип сущности в качестве параметра. Чтобы запросить non-generic DbSet
можно использовать System.Linq.Dynamic
библиотека (вы можете добавить ее из NuGet). Это позволяет запросить DbSet
использование строковых предикатов. Вот пример:
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);
}
}