簡體   English   中英

如何使用反射獲取屬性並在查詢中使用它?

[英]How do I use reflection to get a property and use it in a query?

我有一個通用方法,我想為我的方法添加搜索功能。 作為參數,我得到屬性(字符串)的名稱和它應該在列表中搜索的值(字符串)。 我怎樣才能做到這一點?

**這段代碼不是我擁有的確切代碼,所以看起來我可以使用其他選項,如表達式函數,這在我的情況下是不可能的,因為它應該在 Api 控制器中使用 **我使用工作單元和存儲庫模式在實際項目中,為了簡單起見,我嘗試將其添加到一個簡單的函數中

public async Task<ActionResult<List<T>>> GetAll(string? filterProperty = null, string? filterValue = null)
{
    IQueryable<T> query = dbSet;
    if (filterProperty != null)
    {
        PropertyInfo property = typeof(T).GetProperty(filterProperty);
        query = query. Where(u=> u.property.Contains(filterValue));
    }
    return await query.ToListAsync();
}

對於IQueryable ,您需要為過濾謂詞創建一個LambdaExpression (對於IEnumerable ,您可以將該表達式編譯成適當的Func<> 。)

這一切都是通過構建一個表示您要執行的操作的表達式樹來實現的。 在本例中,您在獲取屬性值的結果上調用Contains ,並為過濾器值傳遞一個常量。

讓我們從您可以重用的Contains方法開始。 這里不是基本的反射,而是使用表達式獲取它的方法:

static readonly MethodInfo _contains =
  (((Expression<Func<string, bool>>)(s => s.Contains("a"))).Body as MethodCallExpression)
  .Method;

雖然這看起來有點令人困惑,但它正在利用編譯器為我們完成反射工作。 有時這比搜索具有多個重載的方法的正確版本更容易,或者當涉及哪個擴展方法不明顯時。 這里的結果是_contains使用我們需要的方法信息進行了初始化。

您已經獲得了目標屬性的屬性信息,所以讓我們將它們放在一起:

// The parameter for the predicate
var row = Expression.Parameter(typeof(T), "row");

// Constant for the filter value
var filter = Expression.Constant(filterValue);

// Get the value of the property
var prop = Expression.Property(property);

// Call 'Contains' on the property value
var body = Expression.Call(prop, _contains, filter);

// Finally, generate the lambda
var predicate = Expression.Lambda<Func<T, bool>(body, row);

// Apply to the query
query = query.Where(predicate);

或者以更緊湊的形式:

var row = Expression.Parameter(typeof(T), "row");
var predicate = 
    Expression.Lambda<Func<T, bool>
    (
        Expression.Call
        (
            Expression.Property(row, property),
            _contains,
            Expression.Constant(filterValue)
        ),
        row
    );

當您通過IEnumerable<T>處理數據時, predicate.Compile()將生成一個有效的Func<T, bool>以傳遞給IEnumerable.Where()

private static readonly MethodInfo _tostring = typeof(Object).GetMethod("ToString");
static readonly MethodInfo _compare = (((Expression<Func<string, bool>>)(s => s.Contains(""))).Body as MethodCallExpression).Method;

public static IEnumerable<T> Search<T>(this IEnumerable<T> items, string propertyName, string filterValue)
{
    var property = typeof(T).GetProperty(propertyName);
    var row = Expression.Parameter(typeof(T), "row");
    
    // Let's make sure we're actually dealing with a string here
    Expression prop = Expression.Property(row, property);
    if (property.PropertyType != typeof(string))
        prop = Expression.Call(prop, _tostring);
    
    var func = 
        Expression.Lambda<Func<T, bool>>
        (
            Expression.Call
            (
                prop,
                _compare,
                Expression.Constant(filterValue)
            ),
            row
        ).Dump().Compile();

    return items.Where(func);
}

表達式非常通用,並且有很多地方可以派上用場。 組合一個函數並多次調用它比一直通過反射更有效率,你可以用合並表達式等做一些有趣的事情。

暫無
暫無

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

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