简体   繁体   中英

C#: Get the name of a property and check its value

I have the following class Franchise:

public class Franchise 
        {
            public string FolderName { get; set; }
            public string InstallerExeName { get; set; }
        }

I have a method that checks specific property value for uniqness among all franchises in the db.

public bool ValidateFolderName(string folderName)
        {
            var allFranchises = _franchiseService.GetAll();
            var result = allFranchises.Any(f => f.FolderName == folderName);
            return result;
        }

The problem is I have to check another property for uniqness:

public bool ValidateInstallerExeName(string installerExeName)
        {
            var allFranchises = _franchiseService.GetAll();
            var result = allFranchises.Any(f => f.InstallerExeName == installerExeName);
            return result;
        }

I want to avoid code duplication by making a generic method. Something like:

public bool ValidateProperty(string propertyName)
        {
            var allFranchises = _franchiseService.GetAll();
            // Not sure how to write this line
            var result = allFranchises.Any(f => f.[propertyName] == propertyName);
            return result;
        }

The problem is I am not sure how to re-write this line of code so that it can get the property name and check its value by the provided parameter:

 var result = allFranchises.Any(f => f.[propertyName] == propertyName);

I know I can do something like this with reflection:

franchise.GetType().GetProperty(propertyName).GetValue(franchise, null);

but I am not sure how can I make this to fit my case. Any help with working example will be greatly appreciated. Thanks!

public bool ValidateProperty<TType, TPropertyType>(Func<TType, TPropertyType> propertySelector, TPropertyType propertyValue)
{
    return _franchiseService.GetAll().Any(f => propertySelector(f) == propertyValue);
}

You can call it like this:

if( ValidateProperty(x => x.FirstName, "Joe") )

This does not use reflection and you have intellisense for your propertyname as well.

Here is a full working example using reflection:

class Program
{
    private static List<Franchise> allFranchises;

    static void Main(string[] args)
    {
        allFranchises = new List<Franchise>
        {
            new Franchise() { FolderName=@"c:\1", InstallerExeName="1.exe" },
            new Franchise() { FolderName=@"c:\2", InstallerExeName="2.exe" },
            new Franchise() { FolderName=@"c:\3", InstallerExeName="3.exe" },
            new Franchise() { FolderName=@"c:\4", InstallerExeName="4.exe" },
            new Franchise() { FolderName=@"c:\5", InstallerExeName="5.exe" },
        };

        Console.WriteLine(ValidateProperty("FolderName", @"c:\2", allFranchises));
        Console.WriteLine(ValidateProperty("InstallerExeName", "5.exe", allFranchises));
        Console.WriteLine(ValidateProperty("FolderName", @"c:\7", allFranchises));
        Console.WriteLine(ValidateProperty("InstallerExeName", "12.exe", allFranchises));
    }

    public static bool ValidateProperty(string propertyName, object propertyValue, IEnumerable<Franchise> validateAgainst)
    {
        PropertyInfo propertyInfo = typeof(Franchise).GetProperty(propertyName);
        return validateAgainst.Any(f => propertyInfo.GetValue(f, null) == propertyValue);
    }
}

public class Franchise
{
    public string FolderName { get; set; }
    public string InstallerExeName { get; set; }
}

It will print out:

True
True
False
False

as expected.

You may use an extension method:

public static bool ValidateProperty(
    this IEnumerable<Franchise> franchises,
    string property,
    object value)
{
    var prop = typeof(Franchise).GetProperty(property);
    if (prop == null)
        throw new ArgumentException("Property does not exist");
    return franchises.Any(f =>
        prop.GetValue(f) == value);
}

Use it like this:

var r = _franchiseService.GetAll().ValidateProperty("FolderName", "myfolder1");

You can build the function you want using System.Linq.Expressions

public class Franchise 
{
   public string FolderName { get; set; }
   public string InstallerExeName { get; set; }

   bool ValidateProperty(string propertyName) {
     var allFranchises = new List<Franchise>();
     var parameter = Expression.Parameter(typeof(Franchise));
     var property = Expression.Property(parameter, propertyName);
     var thisExpression = Expression.Constant(this, typeof(Franchise));
     var value = Expression.Property(thisExpression, propertyName);
     var notThis = Expression.ReferenceNotEqual(thisExpression, property);
     var equal = Expression.Equal(value, property);
     var lambda = Expression.Lambda<Func<Franchise, bool>>(Expression.And(notThis, equal));


     // lamda is now the equivalent of:
     // x => !object.ReferenceEquals(this, x) && object.Equals(x.Property, this.Property)     
     return allFranchises.Any(lambda.Compile());
   }
}

If allFranchises is of type IQueryable you use allFranchises.Any(lambda)

You could also caches the expression for later use if you are worried about performance. }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM