Я использую пространство имен System.ComponentModel.DataAnnotations
для проверки моих классов домена. Как создать настраиваемый атрибут для проверки уникальности свойства независимо от базы данных (например, через какой-либо интерфейс)?Уникальное ограничение с аннотацией данных
ответ
Если я правильно понимаю вас, вы должны иметь возможность создать настраиваемый атрибут ValidationAttribute и получить контекст в своем репозитории с помощью настраиваемой фабрики.
Validator:
using System.ComponentModel.DataAnnotations;
public class DBUniqueAttribute : ValidationAttribute
{
private IRepository Repository{ get; set;}
public DBUniqueAttribute()
{
this.Repository = MyRepositoryFactory.Create();
}
public override bool IsValid(object value)
{
string stringValue = Convert.ToString(value, CultureInfo.CurrentCulture);
return Repository.IsUnique(stringValue);
}
}
Вы бы иметь IRepository интерфейс с методом IsUnique(). MyRepositoryFactory будет иметь статический метод Create(), который создал бы конкретный репозиторий, необходимый для вашей базы данных. Если тип базы данных изменяется, вам нужно только обновить Factory, чтобы вернуть новый репозиторий для вашей новой базы данных.
Это решение, которое я придумал для этой ситуации, просто проверяет таблицу для записи с другим идентификатором, имеющим то же значение для проверяемого свойства. Предполагается, что вы будете использовать LinqToSQL и что любая таблица, для которой требуется эта валидация, имеет один столбец идентификатора.
Я также установил уникальное ограничение для базовой таблицы в базе данных. Этот атрибут позволяет мне добавить хорошее сообщение об ошибке в форму и связать ее с соответствующим свойством.
public class UniqueAttribute : ValidationAttribute
{
public Func<DataContext> GetDataContext { get; private set; }
public string IDProperty { get; private set; }
public string Message { get; private set; }
public UniqueAttribute(Type dataContextType, string idProperty, string message)
{
IDProperty = idProperty;
Message = message;
GetDataContext =() => (DataContext)Activator.CreateInstance(dataContextType);
}
public UniqueAttribute(Type dataContextType, string idProperty, string message, string connectionString)
{
IDProperty = idProperty;
Message = message;
GetDataContext =() => (DataContext)Activator.CreateInstance(dataContextType, new object[] { connectionString });
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
var idProperty = validationContext.ObjectType.GetProperty(IDProperty);
var idType = idProperty.PropertyType;
var id = idProperty.GetValue(validationContext.ObjectInstance, null);
// Unsightly hack due to validationContext.MemberName being null :(
var memberName = validationContext.ObjectType.GetProperties()
.Where(p => p.GetCustomAttributes(false).OfType<DisplayAttribute>().Any(a => a.Name == validationContext.DisplayName))
.Select(p => p.Name)
.FirstOrDefault();
if (string.IsNullOrEmpty(memberName))
{
memberName = validationContext.DisplayName;
}
// End of hack
var validateeProperty = validationContext.ObjectType.GetProperty(memberName);
var validateeType = validateeProperty.PropertyType;
var validatee = validateeProperty.GetValue(validationContext.ObjectInstance, null);
var idParameter = Expression.Constant(id, idType);
var validateeParameter = Expression.Constant(validatee, validateeType);
var objectParameter = Expression.Parameter(validationContext.ObjectType, "o");
var objectIDProperty = Expression.Property(objectParameter, idProperty);
var objectValidateeProperty = Expression.Property(objectParameter, validateeProperty);
var idCheck = Expression.NotEqual(objectIDProperty, idParameter);
var validateeCheck = Expression.Equal(objectValidateeProperty, validateeParameter);
var compositeCheck = Expression.And(idCheck, validateeCheck);
var lambda = Expression.Lambda(compositeCheck, objectParameter);
var countMethod = typeof(Queryable).GetMethods().Single(m => m.Name == "Count" && m.GetParameters().Length == 2);
var genericCountMethod = countMethod.MakeGenericMethod(validationContext.ObjectType);
using (var context = GetDataContext())
{
var table = context.GetTable(validationContext.ObjectType) as IQueryable<Models.Group>;
var count = (int)genericCountMethod.Invoke(null, new object[] { table, lambda });
if (count > 0)
{
return new ValidationResult(Message);
}
}
return null;
}
}
Пример использования:
[MetadataType(typeof(UserMetadata))]
public partial class Group : IDatabaseRecord
{
public class UserMetadata
{
[Required(ErrorMessage = "Name is required")]
[StringLength(255, ErrorMessage = "Name must be under 255 characters")]
[Unique(typeof(MyDataContext), "GroupID", "Name must be unique")]
public string Name { get; set; }
}
}
Я любовь @ решение daveb в. К сожалению, через три года для меня потребовались довольно серьезные изменения. Вот его решение обновлено для EF6. Надеюсь, кто-то спасет час или около того.
public class UniqueAttribute : ValidationAttribute
{
public UniqueAttribute(string idProperty, string message)
{
IdProperty = idProperty;
Message = message;
}
[Inject]
public DataContext DataContext { get; set; }
private string IdProperty { get; set; }
private string Message { get; set; }
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
var objectType = validationContext.ObjectType;
if (objectType.Namespace == "System.Data.Entity.DynamicProxies")
{
objectType = objectType.BaseType;
}
var idProperty = objectType.GetProperty(IdProperty);
var idType = idProperty.PropertyType;
var id = idProperty.GetValue(validationContext.ObjectInstance, null);
var memberName = validationContext.MemberName;
var validateeProperty = objectType.GetProperty(memberName);
var validateeType = validateeProperty.PropertyType;
var validatee = validateeProperty.GetValue(validationContext.ObjectInstance, null);
var idParameter = Expression.Constant(id, idType);
var validateeParameter = Expression.Constant(validatee, validateeType);
var objectParameter = Expression.Parameter(objectType, "o");
var objectIdProperty = Expression.Property(objectParameter, idProperty);
var objectValidateeProperty = Expression.Property(objectParameter, validateeProperty);
var idCheck = Expression.NotEqual(objectIdProperty, idParameter);
var validateeCheck = Expression.Equal(objectValidateeProperty, validateeParameter);
var compositeCheck = Expression.And(idCheck, validateeCheck);
var lambda = Expression.Lambda(compositeCheck, objectParameter);
var countMethod = typeof(Queryable).GetMethods().Single(m => m.Name == "Count" && m.GetParameters().Length == 2);
var genericCountMethod = countMethod.MakeGenericMethod(objectType);
var table = DataContext.Set(objectType);
var count = (int)genericCountMethod.Invoke(null, new object[] { table, lambda });
if (count > 0)
{
return new ValidationResult(Message);
}
return null;
}
}
просто сделать что-то вроде этого на модели
[StringLength(100)]
[Index("IX_EntidadCodigoHabilitacion", IsUnique = true)]
public string CodigoHabilitacion { get; set; }
- 1. mySQL Уникальное ограничение
- 2. УНИКАЛЬНОЕ ограничение в SQLite
- 3. Уникальное ограничение с нулевым столбцом
- 4. Уникальное ограничение с условием MYSQL
- 5. Уникальное ограничение с «игнорировать регистр»
- 6. Найти уникальное ограничение таблицы базы данных
- 7. Уникальное ограничение в подходе базы данных
- 8. Уникальное ограничение на уровне данных в GAE
- 9. Уникальное ограничение JayData
- 10. Уникальное ограничение ограничений
- 11. Как создать уникальное ограничение
- 12. MySQL обновляет ограничение, уникальное
- 13. Уникальное ограничение для NodeEntity
- 14. Уникальное ограничение в Mnesia
- 15. Сложное многостоечное уникальное ограничение
- 16. Уникальное ограничение на колонке
- 17. mysql - drop уникальное ограничение
- 18. Устранить уникальное ограничение Oracle
- 19. Postgres - уникальное ограничение
- 20. Postgres добавить уникальное ограничение
- 21. Уникальное ограничение (SchemaName.DATA1_PK)
- 22. Уникальное ограничение в MySQL
- 23. Odoo: проверить уникальное ограничение?
- 24. cakephp 3 добавить уникальное ограничение
- 25. Изменить уникальное ограничение в Oracle
- 26. Django уникальное ограничение + ошибки формы
- 27. Создать уникальное ограничение изначально отключена
- 28. Уникальное ограничение на столбец таблицы
- 29. Oracle уникальное ограничение с предложением where
- 30. Уникальное ограничение Grails не обновляется