2009-07-24 12 views
0

J'essaie de faire fonctionner cette générique DuplicateValidationRule, qui vérifie fondamentalement une collection pour les doublons (basé sur un type d'objet métier générique transmis). Permet de prendre l'objet métier IBankAccount par exemple:Générique DuplicateValidationRule (Vérification des objets métier pour les doublons)

public interface IBankAccount : IMyBusinessObjectBase 
{ 
    IBank Bank 
    { 
     get; 
     set; 
    } 

    IBankAccountType BankAccountType 
    { 
     get; 
     set; 
    } 

    string AccountName 
    { 
     get; 
     set; 
    } 

    string AccountNumber 
    { 
     get; 
     set; 
    } 

    DateTime EffectiveDate 
    { 
     get; 
     set; 
    } 
} 

Disons que j'ai la collection suivante de IBankAccount

IBankAccount acc1 = new BankAccount(); 
acc1.AccountName = "Account1"; 
acc1.AccountNumber = "123456"; 
acc1.Bank = FetchBusinessObjectByID(1); //Fetch Bank 1 

IBankAccount acc2 = new BankAccount(); 
acc2.AccountName = "Account2"; 
acc2.AccountNumber = "654321"; 
acc2.Bank = FetchBusinessObjectByID(1); //Fetch Bank 1 

IBankAccount acc3 = new BankAccount(); 
acc3.AccountName = "Account3"; 
acc3.AccountNumber = "123456"; 
acc3.Bank = FetchBusinessObjectByID(2); //Fetch Bank 2 

IBankAccount acc4 = new BankAccount(); 
acc4.AccountName = "Account3"; 
acc4.AccountNumber = "123456"; 
acc4.Bank = FetchBusinessObjectByID(1); //Fetch Bank 2 

ICollection<IBankAccount> bankAccounts = new List<IBankAccount>(); 
bankAccount.Add(acc1); 
bankAccount.Add(acc2); 
bankAccount.Add(acc2); 
bankAccount.Add(acc4); 

Paramètres:

T = BusinessObject Classe (personne, Adresse, BankAccount, Bank, BankBranch etc) string [] entityPropertyName = Tableau de propriétés à inclure lors de la recherche de doublons.

Utilisons maintenant le senario suivant: Disons que je veux vérifier les BankAccounts en double, en cochant la case sur le numéro de compte, et la banque. Le numéro de compte est unique dans une banque, mais peut exister dans une autre banque. Donc, si vous regardez la liste ci-dessus, Acc1, Acc2 et Acc3 est valide. Acc4 est invalide car ce numéro de compte existe déjà pour cette banque. Ainsi, l'appel à valider ressemblerait à ceci.

DuplicateValidationRule.Validate (newBankAccountEntry, nouvelle chaîne [] {"AccountNumber", "Bank.Name"});

Le code ci-dessus passe dans l'entité commerciale pour vérifier les doublons et le tableau de propriétés qui inclut les propriétés à vérifier.

public ValidationError Validate<T>(T entityProperty, string[] entityPropertyName) 
    {   
     ICollection<T> businessObjectList = FetchObjectsByType<T>(); 

     bool res = true; 
     for (int i = 0; i < entityPropertyName.Length; i++) 
     { 
      object value = getPropertyValue(entityProperty, entityPropertyName[i]); 

//By Using reflection and the getPropertyValue method I can substitute the properties to //compare. 
      if (businessObjectList.Any(x => getPropertyValue(x, entityPropertyName[i]).Equals(value) && 
             x.GetType().GetProperty("ID").GetValue(x,null).ToString()           
             != ((IBusinessObjectBase)entityProperty).ID.ToString())) 
       res &= true; 
      else 
       res &= false;    
     } 

     if (res) 
      return new ValidationError(_DuplicateMessage); 
     else 
      return ValidationError.Empty; 
    } 

Cette méthode est utilisée pour obtenir l'objet réel de vérifier les doublons sur:

private static object getPropertyValue(object obj, string propertyName) 
    { 
     string[] PropertyNames = propertyName.Split('.'); 

     object propertyValue = obj; 

     for (int i = 0; i < PropertyNames.Length; i++) 
     { 
      propertyValue = propertyValue.GetType().GetProperty(PropertyNames[i], BindingFlags.Public | 
        BindingFlags.Instance | BindingFlags.Static | BindingFlags.FlattenHierarchy). 
        GetValue(propertyValue, null); 
     } 

     if (propertyValue.GetType() == typeof(string)) 
      return propertyValue.ToString().ToLower(CultureInfo.CurrentCulture); 
     else 
      return propertyValue; 
    } 

Le code ci-dessus fonctionne, mais il a un défaut: Le code ci-dessus ne fonctionnera que si vous vérifiez une propriété unique pour un match en double:

DuplicateValidationRule.Validate<IBankAccount>(newBankAccountEntry,new string[] {"AccountNumber"}); 

Lorsque vous ajoutez une propriété à vérifier contre, par exemple:

DuplicateValidationRule.Validate<IBankAccount>(newBankAccountEntry,new string[] {"AccountNumber","Bank.Name"}); 

puis le code vérifie la deuxième propriété, sur le prochain occurrence dans la liste. Il devrait tester toutes les propriétés dans le .Any pour l'objet actuellement en cours de comparaison. En espérant que cela fait sourire, peut-être que quelqu'un peut me donner des conseils sur la façon de résoudre ce problème! Merci :)

Répondre

1

moi ici en supposant que vous avez une entité BusinessObject et que vous voulez faire correspondre toutes les propriétés contre une autre dans une liste de BusinessObjects:

public ValidationError Validate<T>(T entityProperty, string[] entityPropertyName) 
{     
    ICollection<T> businessObjectList = FetchObjectsByType<T>(); 
    Hashtable properties = EnumeratePropertyInfo<T>(entityProperty, entityPropertyName); 
    return businessObjectList.Any(obj => IsDuplicate<T>(obj, properties)) == true ? new ValidationError(_DuplicateMessage) : ValidationError.Empty; 
} 


private Hashtable EnumeratePropertyInfo<T>(T entityProperty, string[] entityPropertyName) 
{ 
    Hashtable properties = new Hashtable(); 
    for (int i = 0; i < entityPropertyName.Length; i++)   
    {    
     object value = getPropertyValue(entityProperty, entityPropertyName[i]); 
     properties.Add(entityPropertyName[i], value); 
    } 
    return properties; 
} 

// all properties must be matched for a duplicate to be found 
private bool IsDuplicate<T>(T entityProperty, Hashtable properties) 
{ 
    foreach(DictionaryEntry prop in properties) 
    { 
     var curValue = getPropertyValue(entityProperty, prop.Key.ToString()); 
     if (!prop.Value.Equals(curValue)) 
     { 
      return false; 
     } 
    } 
    return true; 
} 

Si vous êtes peut-être Un peu préoccupé par l'utilisation d'un Hastable pour les mappages de propriétés, je vous conseille de créer votre propre type personnalisé pour cela.

Espérons que cela aide.

+0

On dirait que votre code pourrait fonctionner ... Il semble être une meilleure solution à celle que je viens de trouver. Permettez-moi de poster ma solution actuelle et j'espère que certaines entrées de l'utilisateur me diront lequel serait le mieux à utiliser. Merci beaucoup!! – FaNIX

1

Si je ne me trompe pas maintenant, vous vérifiez l'existence unique de chaque propriété et non leur combinaison.

Au lieu d'utiliser la réflexion ne peut pas utiliser la méthode Equals ou une interface (par exemple IMyComparable.) Comme ceci:

public interface IMyComparable<T> 
{ 
    bool MyComparableMethod(T account); 
} 

public interface IBankAccount : IMyBusinessObjectBase, IMyComparable<T> 
{ 
... 
    public bool MyComparableMethod(IBankAccount account) 
    { 
     return this.AccountNumber == account.AccountNumber && 
       this.Bank.Name == account.Bank.Name && 
       this.Id != account.Id; 
    } 
} 

public ValidationError Validate<T>(T entityProperty) where T : IMyComparable<T> 
{ 
... 
    if (!businessObjectList.Any(x => entityProperty.MyComparableMethod(x)) 
     return new ValidationError(_DuplicateMessage); 
... 

que dans la Toute méthode, vous pouvez utiliser la méthode MyComparableMethod définie dans l'interface.

Hope this helps :)

+0

Je ne suis pas sûr de ce que vous essayez de faire. Vous avez raison de dire que je vérifie actuellement l'existence unique de chaque propriété et non leur combinaison, ce qui est mon problème. S'il vous plaît fournir un exemple complet pour ce que vous essayez de dire? Rappelez-vous qu'il seens à prendre dans tout type d'objet métier, de sorte que le IMyComparable devrait ressembler à ceci: interface publique IMyComparable { bool MyComparableMethod (entité T); } – FaNIX

+0

Ma méthode de comparaison n'est pas parfaite (par exemple ne vérifie pas null) mais je pense maintenant que le code exprime mon idée – marcob

0

Ma solution au problème. Aucune modification apportée à getPropertyValue. S'il vous plaît fournir votre contribution. Merci

public ValidationError Validate<T>(T entityProperty, string[] entityPropertyName) 
{ 
    ICollection<T> businessObjectList = 
     BusinessContextManagerService.Fetch<ICollection<T>>(Criteria.ActiveAndDormant); 

    bool res = true; 
    object entityPropertyValue = null; 

    // Only Checking one property 
    if (entityPropertyName.Length == 1) 
    { 
     entityPropertyValue = getPropertyValue(entityProperty, entityPropertyName[0]); 

     if (businessObjectList.Any(x => getPropertyValue(x, entityPropertyName[0]).Equals(entityPropertyValue) && 
            x.GetType().GetProperty("ID").GetValue(x, null).ToString() 
            != ((IBusinessObjectBase)entityProperty).ID.ToString())) 
      res &= true; 
     else 
      res &= false; 
    } 
    else 
    { 
     foreach (object obj in businessObjectList) 
     { 
      res = true; 
      int objID = (Int32)obj.GetType().GetProperty("ID").GetValue(obj, null); 

      for (int i = 0; i < entityPropertyName.Length; i++) 
      { 
       entityPropertyValue = getPropertyValue(entityProperty, entityPropertyName[i]); 
       object objValue = getPropertyValue(obj, entityPropertyName[i]); 

       if (objValue.Equals(entityPropertyValue) && objID != ((IBusinessObjectBase)entityProperty).ID) 
        res &= true; 
       else 
        res &= false; 

       if (res == false) 
        break; 
      } 

      if (res == true) 
       break; 
     } 
    } 

    if (res) 
     return new ValidationError(_DuplicateMessage); 
    else 
     return ValidationError.Empty; 
} 
+0

Votre solution fera sans doute l'affaire mais à des fins de lisibilité c'est un peu encombré. Il y a aussi du code dupliqué là-dedans. Vous devriez essayer de le refactoriser un peu et de le déléguer à des méthodes génériques. Ma solution le fait bien et réduit votre méthode de validation à 3 lignes. Vous pouvez également améliorer la méthode de duplication en passant simplement 2 ensembles de propriétés de hachage et en les comparant à l'aide de .Equals. Je le mettrai à jour plus tard pour vous montrer. Cependant, à la fin de la journée, c'est votre décision que vous voulez faire. – James

0

J'ai testé votre code avec un Timespan et il semble prendre la même quantité de temps, ce qui est bien avec moi. Votre code a l'air beaucoup plus agréable, donc je vais aller avec votre solution, merci beaucoup d'avoir pris le temps de m'aider. PS: J'ai dû modifier les éléments suivants pour que cela fonctionne. Tous les tests unitaires passent maintenant :)

private bool IsDuplicate<T>(T entityProperty, Hashtable properties) 
    { 
     bool res = true; 

     foreach (DictionaryEntry prop in properties) 
     { 
      var curValue = getPropertyValue(entityProperty, prop.Key.ToString()); 
      if (prop.Value.Equals(curValue)) 
      { 
       res &= true; 
      } 
      else 
       res &= false; 
     } 
     return res; 
    } 
+0

Content de pouvoir aider! Btw je viens de le remarquer, mon code est correct pour la méthode IsDuplicate, j'ai juste oublié d'ajouter un! l'opérateur devrait être "if (! prop.Value.Equals (curValue))". – James

+0

avoir un drapeau et le mettre constamment à vrai/faux est assez inutile car une fois qu'il est faux, vous savez que les jeux de propriétés ne sont pas égaux. Donc, vous voulez sauter la boucle dès que possible. J'ai mis à jour mon IsDuplicateMethod – James

Questions connexes