Ich verwende den Namespace System.ComponentModel.DataAnnotations
, um meine Domänenklassen zu validieren. Wie kann ich ein benutzerdefiniertes Attribut erstellen, um die Eindeutigkeit einer Eigenschaft unabhängig von der Datenbank (z. B. über eine Schnittstelle) zu überprüfen?Eindeutige Einschränkung mit Datenannotation
Antwort
Wenn ich Sie richtig verstehe, sollten Sie in der Lage sein, ein benutzerdefiniertes ValidationAttribute zu erstellen und über eine benutzerdefinierte Factory einen Kontext zu Ihrem Repository zu erhalten.
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);
}
}
Sie würden eine IRepository Schnittstelle mit einem IsUnique() -Methode haben. Die MyRepositoryFactory würde eine statische Methode namens Create() haben, die das konkrete Repository für Ihre Datenbank erstellen würde. Wenn sich der Datenbanktyp ändert, müssen Sie nur die Factory aktualisieren, um ein neues Repository für Ihre neue Datenbank zurückzugeben.
Dies ist die Lösung, die ich für diese Situation entwickelt habe, es überprüft einfach die Tabelle auf einen Datensatz mit einer anderen ID, die den gleichen Wert für die zu validierende Eigenschaft hat. Es wird davon ausgegangen, dass Sie LinqToSQL verwenden und dass jede Tabelle, für die diese Art von Validierung erforderlich ist, über eine einzige ID-Spalte verfügt.
Ich würde auch eine eindeutige Einschränkung auf die zugrunde liegende Tabelle in der Datenbank setzen. Dieses Attribut ermöglicht es mir, eine nette Fehlermeldung in das Formular einzufügen und es der entsprechenden Eigenschaft zuzuordnen.
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;
}
}
Beispiel Nutzung:
[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; }
}
}
I Liebe @ DAVEB-Lösung. Leider brauchte ich drei Jahre später eine ziemlich schwere Modifikation für mich. Hier ist seine Lösung für EF6 aktualisiert. Hoffentlich rettet jemand eine Stunde oder so.
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;
}
}
nur etwas tun, wie dies auf dem Modell
[StringLength(100)]
[Index("IX_EntidadCodigoHabilitacion", IsUnique = true)]
public string CodigoHabilitacion { get; set; }
- 1. MongoDB: entfernen eindeutige Einschränkung
- 2. Eindeutige Einschränkung für Tabellenspalte
- 3. Eindeutige Einschränkung vs Vorprüfung
- 4. Umbau bestehender eindeutige Einschränkung
- 5. Eindeutige Einschränkung in MySQL
- 6. PostgreSQL: Bedingte eindeutige Einschränkung
- 7. Problem Eindeutige Einschränkung
- 8. Postgresql: Bedingte eindeutige Einschränkung
- 9. Eindeutige Einschränkung mit weichen gelöschten Zeilen ausgeschlossen
- 10. ORA-00001 eindeutige Einschränkung - mit gültigem Wert
- 11. Eindeutige Einschränkung über mehrere Spalten
- 12. Postgres eindeutige Einschränkung vs Index
- 13. EF6 eindeutige Einschränkung auf Fremdschlüssel
- 14. JPA 2.0 eindeutige Einschränkung als XML
- 15. Haben eindeutige Einschränkung zwischen den Tabellen
- 16. Eindeutige Einschränkung über Fremdschlüssel in Sequelize-Modell
- 17. Alembic + Sqlalchemy Multi Column Eindeutige Einschränkung
- 18. INSERT INTO .. SELECT .. eindeutige Einschränkung Verletzung
- 19. Eindeutige Namen in INSTALLED_APPS Django Einschränkung
- 20. Hibernate: doppelte Schlüsselwert verletzt eindeutige Einschränkung
- 21. Django: doppelte Schlüsselwert verletzt eindeutige Einschränkung
- 22. Eindeutige Einschränkung wurde nicht in JPA erstellt
- 23. Eindeutige case-insensitive Einschränkung in Oracle-Datenbank
- 24. MySQL: Eindeutige Einschränkung für mehrere Felder
- 25. Kombination von zwei Spalten eindeutige Einschränkung
- 26. DB2 - eindeutige Einschränkung für mehrere Spalten
- 27. @oneToMany mit @JoinTable haben eindeutige Einschränkung in jpa?
- 28. Sollte ich eine eindeutige Einschränkung mit PHP überprüfen?
- 29. IntegrityError: unterscheiden zwischen eindeutige Einschränkung und nicht null Verletzungen
- 30. JPA-Sammlung hat unerwünschte eindeutige Einschränkung in Zuordnungstabelle