通用DuplicateValidationRule(检查重复项的业务对象)

时间:2009-07-24 07:17:47

标签: c# validation generics reflection

我正在尝试使这个Generic DuplicateValidationRule工作,它基本上检查Collection是否有重复项(基于传入的通用业务对象类型)。让我们以IBankAccount业务对象为例:

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

    IBankAccountType BankAccountType
    {
        get;
        set;
    }

    string AccountName
    {
        get;
        set;
    }

    string AccountNumber
    {
        get;
        set;
    }

    DateTime EffectiveDate
    {
        get;
        set;
    }
}

假设我有以下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);

参数:

T = BusinessObject类型类(Person,Address,BankAccount,Bank,BankBranch等)string [] entityPropertyName =检查重复项时要包含的属性数组。

现在让我们使用以下参数:假设我想检查重复的BankAccounts,检查帐号和银行。帐号在银行中是唯一的,但可以存在于另一家银行。因此,如果您查看上面的列表,Acc1,Acc2和Acc3是有效的。 Acc4无效,因为该银行的帐号已存在。

因此,对validate的调用看起来像这样。

DuplicateValidationRule.Validate(newBankAccountEntry,new string [] {“AccountNumber”,“Bank.Name”});

上面的代码传入业务实体以检查重复项,以及包含要检查的属性的属性数组。

    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;
    }

此方法用于获取实际对象以检查重复项:

    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;
    }

上面的代码有效,但它有一个缺陷:只有在检查单个属性的重复匹配时,上面的代码才有效:

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

添加其他要检查的属性时,例如:

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

然后代码将检查列表中下一个出现的第二个属性。它应该测试.Any中所有被比较的对象的属性。希望这有用,也许有人可以就如何解决这个问题给我一些建议!谢谢:))

4 个答案:

答案 0 :(得分:1)

如果我现在没错,你正在检查每一个属性的独特存在,而不是它们的组合。

不能使用反射,你不能使用Equals方法或接口(例如IMyComparable),如下所示:

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);
...

比任何方法都可以使用界面中定义的MyComparableMethod方法。

希望这会有所帮助:)

答案 1 :(得分:1)

假设您有一个BusinessObject实体,并且您希望在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;
}

如果您可能有点担心使用Hastable进行属性映射,那么我建议您为此创建自己的自定义类型。

希望有所帮助。

答案 2 :(得分:0)

我解决问题的方法。没有对getPropertyValue进行任何修改。请提供您的意见。感谢

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;
}

答案 3 :(得分:0)

我用Timespan测试了你的代码,它似乎需要相同的时间,这对我来说很好。你的代码看起来好多了,所以我会选择你的解决方案,非常感谢你花时间帮助我。

PS:我必须更改以下内容才能使其正常工作。所有单元测试现在都通过了:)

    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;
    }