簡體   English   中英

基於在運行時確定的屬性名稱的過濾器列表

[英]Filter List based on property names decided at runtime

我想知道是否有可能使過濾器表達式中的“屬性名稱”動態化

考慮場景

List<Person> GetPerson(int countryID, int stateID, int cityID, int zip)
{
//List of person can be filtered based on below line of code
  List<Person> filteredPersons= persons.FindAll(rule => rule.CountryID == countryID).ToList();
//is it possible to specify ".Country" dynamically. something like

List<Person> filteredPersons= persons.FindAll(rule => rule."propertyName"== countryID).ToList();

}

考慮到您的示例,您可以采用的一種方法是使用.Where()擴展名而不是FindAll() ,然后可以使用它手動構建表達式。 一個簡單的例子如下。

static List<Person> GetPerson(int countryID, int stateID, int cityID, int zip)
{
    //create a new expression for the type of person this.
    var paramExpr = Expression.Parameter(typeof(Person));

    //next we create a property expression based on the property named "CountryID" (this is case sensitive)
    var property = Expression.Property(paramExpr, "CountryID");

    //next we create a constant express based on the country id passed in.
    var constant = Expression.Constant(countryID);

    //next we create an "Equals" express where property equals containt. ie. ".CountryId" = 1
    var idEqualsExpr = Expression.Equal(property, constant);

    //next we convert the expression into a lamba expression
    var lExpr = Expression.Lambda<Func<Person, bool>>(idEqualsExpr, paramExpr);

    //finally we query our dataset
    return persons.AsQueryable().Where(lExpr).ToList();
}

因此,這看起來像很多代碼,但是我們基本上要做的是手動構建表達式樹,最終結果看起來與(和功能類似)

return persons.AsQueryable().Where(p => p.CountryId = countryId);

現在我們可以繼續前進,假設您要使用and \\ or或基於方法調用來查詢多個屬性。 也就是說,您可以將所有“過濾器”參數更改為Nullable,然后檢查是否在我們的過濾器中傳遞了一個值,例如。

static List<Person> GetPerson(int? countryID = null, int? stateID = null, int? cityID = null, int? zip = null)
{
    //create a new expression for the type of person this.
    var paramExpr = Expression.Parameter(typeof(Person));

    //var equalExpression = Expression.Empty();
    BinaryExpression equalExpression = null;

    if (countryID.HasValue)
    {
        var e = BuildExpression(paramExpr, "CountryId", countryID.Value);
        if (equalExpression == null)
            equalExpression = e;
        else
            equalExpression = Expression.And(equalExpression, e);
    }
    if (stateID.HasValue)
    {
        var e = BuildExpression(paramExpr, "StateID", stateID.Value);
        if (equalExpression == null)
            equalExpression = e;
        else
            equalExpression = Expression.And(equalExpression, e);
    }
    if (equalExpression == null)
    {
        return new List<Person>();
    }

    //next we convert the expression into a lamba expression
    var lExpr = Expression.Lambda<Func<Person, bool>>(equalExpression, paramExpr);
    //finally we query our dataset
    return persons.AsQueryable().Where(lExpr).ToList();
}

static BinaryExpression BuildExpression(Expression expression, string propertyName, object value)
{
    //next we create a property expression based on the property named "CountryID" (this is case sensitive)
    var property = Expression.Property(expression, propertyName);

    //next we create a constant express based on the country id passed in.
    var constant = Expression.Constant(value);

    //next we create an "Equals" express where property equals containt. ie. ".CountryId" = 1
    return Expression.Equal(property, constant);
}

現在,這是更多代碼,但是您可以看到,我們現在接受所有參數的null值,並為其他屬性構建查詢。

現在,您可以進一步(假設需要使用更通用的方法),傳入屬性\\值的Dictionary<string, object>進行查詢。 可以作為IEnumerable<T>上的擴展方法來完成,如下所示。

public static class LinqExtensions
{
    public static IEnumerable<T> CustomParameterQuery<T>(this IEnumerable<T> entities, Dictionary<string, object> queryVars)
    {
        if (entities.Count() == 0 || queryVars.Count == 0)
        {
            return entities;
        }

        //create a new expression for the type of person this.
        var paramExpr = Expression.Parameter(typeof(T));

        BinaryExpression equalExpression = null;
        foreach (var kvp in queryVars)
        {
            var e = BuildExpression(paramExpr, kvp.Key, kvp.Value);
            if (equalExpression == null)
                equalExpression = e;
            else
                equalExpression = Expression.And(equalExpression, e);
        }

        if (equalExpression == null)
        {
            return new T[0];
        }
        //next we convert the expression into a lamba expression
        var lExpr = Expression.Lambda<Func<T, bool>>(equalExpression, paramExpr);
        //finally we query our dataset
        return entities.AsQueryable().Where(lExpr);
    }

    static BinaryExpression BuildExpression(Expression expression, string propertyName, object value)
    {
        //next we create a property expression based on the property name
        var property = Expression.Property(expression, propertyName);

        //next we create a constant express based on the country id passed in.
        var constant = Expression.Constant(value);

        //next we create an "Equals" express where property equals containt. ie. ".CountryId" = 1
        return Expression.Equal(property, constant);
    }
}

現在,可以輕松地將其稱為:

var dict = new Dictionary<string, object>
{
    { "CountryID", 1 },
    { "StateID", 2 }
};

var e = persons.CustomParameterQuery(dict);

現在,令人討厭的是,這並不是一個完美的例子,但是應該使您朝着正確的方向前進。 現在,通過使用Expression.Or而不是Expression.And在組合表達式時,您也可以“支持” OR語句等。

我必須添加這非常容易出錯,因為它要求屬性名稱與實體完全相同,您可以在T上使用反射,並確定PropertyName是否存在以及是否在正確的大小寫中。

在NuGet中搜索System.Linq.Dynamic 這是從Dynamic Linq開始的最簡單方法。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM