Уникальное свойство пользовательской проверки 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);
    }
}