是的,你可以。我使用的一種方法是使用與返回類型相同的對象作爲搜索過濾器。因此,如果您想搜索「Bill」的客戶名稱,那麼您將Bill設置爲Order.Customer.Name
。將該對象傳遞給方法將應用所有適用的搜索。
要做到這一點,通過定義搜索字段列表開始:
Field<Order>[] Fields;
,宣佈新的字段填寫這些:
var newField = new Field<Order>(o => o.Customer.Name, true, "Customer Name");
的「真實」參數表示,這將是該法案排序字段的結果。
Field
對象包含足夠的信息以在稍後生成表達式。它看起來是這樣的:
public class Field<T>
{
public Field(Expression<Func<T, object>> field, bool sortField = false, string displayName = null)
{
//get & validate member
MemberExp = field.Body is UnaryExpression ? ((UnaryExpression)field.Body).Operand as MemberExpression
: (MemberExpression)field.Body;
Field = MemberExp?.Member;
if (Field == null) throw new ArgumentException("Field expression is not a member.");
//set field type
switch (Field.MemberType)
{
case MemberTypes.Property:
PropertyInfo p = (PropertyInfo)Field;
FieldType = p.PropertyType;
break;
case MemberTypes.Field:
FieldInfo f = (FieldInfo)Field;
FieldType = f.FieldType;
break;
default:
throw new Exception("Unsupported member type detected.");
}
//store input values
FieldExpression = field;
SortField = sortField;
DisplayName = displayName ?? Field.Name;
}
public bool SortField { get; set; }
public string DisplayName { get; private set; }
public MemberExpression MemberExp { get; private set; }
public Expression<Func<T, object>> FieldExpression { get; private set; }
public Func<T, object> GetValue => FieldExpression.Compile();
public Type FieldType { get; set; }
/// <summary>
/// Gets the full field name, i.e o => o.Customer.CustomerName returns "Customer.CustomerName"
/// </summary>
public string UnqualifiedFieldName
{
get
{
var stringExp = MemberExp.ToString();
var paramEnd = stringExp.IndexOf('.') + 1;
return stringExp.Substring(paramEnd);
}
}
}
一旦你定義的所有搜索域,你會調用一個方法來獲取搜索的基礎上(T
)你從用戶收集的搜索過濾器的結果:
//get the results in ascending order, 10 items per page, first page
var results = GetSearchResults(searchFilters, "ASC", 10, 1);
該方法將要求您有一個可查詢的數據集合。我假設你有一些方法可以檢索你的數據,例如context.GetCollection()
。該GetSearchResults
方法是這樣的:
//Returns a filtered dataset based on provided search filters
//searchFilters is an object T which contains the search filters entered.
private List<T> GetSearchResults(T searchFilters, string sortDir = "ASC", int pageSize, int currentPage)
{
IQueryable<T> searchResults = context.GetCollection(); //get your data context here
var filterExpressions = new List<Expression<Func<T, bool>>>();
//Add filters
foreach (var field in Fields)
{
//try to get the search value, ignoring null exceptions because it's much harder
//to check for null objects at multiple levels. Instead the exception tells us there's
//no search value
string searchValue = null;
try
{
searchValue = field.GetValue(searchFilters)?.ToString();
}
catch (NullReferenceException) { }
if (string.IsNullOrWhiteSpace(searchValue)) continue;
//shared expression setup
ParameterExpression param = field.FieldExpression.Parameters.First();
Expression left = field.FieldExpression.Body;
ConstantExpression right = Expression.Constant(searchValue);
Expression body = null;
//create expression for strings so we can use "contains" instead of "equals"
if (field.FieldType == typeof(string))
{
//build the expression body
MethodInfo method = typeof(string).GetMethod("Contains", new[] { typeof(string) });
body = Expression.Call(left, method, right);
}
else
{ //handle expression for all other types
body = Expression.Equal(left, right);
}
//finish expression
Expression<Func<T, bool>> lambda = Expression.Lambda<Func<T, bool>>(body, param);
filterExpressions.Add(lambda);
}
//apply the expressions
searchResults = filterExpressions.Aggregate(searchResults, (current, expression) => current.Where(expression));
//get sort field
Field<T> sortField = Fields.FirstOrDefault(f => f.SortField);
searchResults = searchResults.OrderBy($"{sortField.UnqualifiedFieldName} {sortDir}");
// Get the search results
int count = searchResults.Count();
int maxPage = count/pageSize;
if (maxPage * pageSize < count) maxPage++;
if (currentPage > maxPage) currentPage = maxPage;
int skip = Math.Max(0, (filters.page - 1) * pageSize);
int display = Math.Max(0, Math.Min(count - skip, pageSize));
return searchResults.Skip(skip).Take(display).ToList();
}
此方法使用Field[]
陣列構建表達你的標準,並將其應用到數據集。
我希望有幫助!如果您有任何問題,請告訴我。
你試過嗎?爲什麼它不工作? – Will
@我想創建動態。其實我問過類似的問題,但仍然沒有正確的答案。 http://stackoverflow.com/questions/38118300/how-to-filter-child-collection-with-linq-dynamic – Kadir
動態是一個非常廣泛的詞。它的範圍從*動態列名*到*不可能*。請更具體一些,也許是一些例子。 – user3185569