Ja, Sie können. Ein Ansatz, den ich verwendet habe, verwendet ein Objekt, das mit dem Rückgabetyp als Suchfilter identisch ist. Wenn Sie also nach dem Kundennamen "Bill" suchen möchten, setzen Sie Order.Customer.Name
auf Bill. Wenn Sie dieses Objekt an eine Methode übergeben, werden alle anwendbaren Suchen angewendet.
Um dies zu tun, starten, indem Sie eine Liste der durchsuchbaren Felder definieren:
Field<Order>[] Fields;
diese füllen, indem er erklärt neue Felder:
var newField = new Field<Order>(o => o.Customer.Name, true, "Customer Name");
Die „wahre“ Parameter bedeutet, dass es als das handeln Sortierfeld für die Ergebnisse. Das Objekt Field
enthält genügend Informationen, um später Ausdrücke zu generieren. Es wird wie folgt aussehen:
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);
}
}
}
Nachdem Sie alle Suchfelder definiert haben, werden Sie eine Methode aufrufen, um die Suchergebnisse auf der Basis der Suchfilter (T
) Sie von dem Benutzer gesammelt haben zu holen:
//get the results in ascending order, 10 items per page, first page
var results = GetSearchResults(searchFilters, "ASC", 10, 1);
Die Methode erfordert eine abfragbare Sammlung von Daten. Ich nehme an, dass Sie eine Methode wie context.GetCollection()
haben, die Ihre Daten abruft. Die GetSearchResults
Methode wird wie folgt aussehen:
//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();
}
Diese Methode verwendet Ihr Field[]
Array Ausdrücke für Ihre Kriterien zu bauen und sie auf den Datensatz anwenden.
Ich hoffe, dass hilft! Lass es mich wissen, wenn du irgendwelche Fragen hast.
Haben Sie das versucht? Warum hat es nicht funktioniert? – Will
@Will möchte ich das dynamisch erstellen. Eigentlich habe ich ähnliche Fragen gestellt, aber immer noch keine richtige Antwort. http://stackoverflow.com/questions/38118300/how-to-filter-child-collection-with-linq-dynamic – Kadir
dynamisch ist ein sehr breites Wort. Es reicht von * dynamischer Spaltenname * bis * unmöglich *. Bitte seien Sie genauer, vielleicht ein paar Beispiele. – user3185569