2016-07-11 4 views
2

Ich möchte eine dynamische Lambda-Ausdrücke für die Filterung erstellen.So erstellen Sie dynamische Entity Framework Filterausdruck wie Expression <Func <T, bool>>

Ich suchte wenig, aber konnte keine nützlichen Sachen für Kindersammlungen finden. Wie kann ich einen solchen Ausdruck erstellen?

Expression<Func<ORDERS, bool>> filter1 = c => c.ORDER_DETAILS.Any(x => x.PRODUCTS.HEADING.Contains("foo")); 

PS: Ich fragte ähnliche Frage, aber ich bekam keine richtige Antwort. Also habe ich beschlossen, aus dieser Richtung zu gehen, wenn ich nicht falsch liege.

Mehr Informationen über meine Frage: (How to filter child collection with linq dynamic)

Ich versuche Ergebnisse für Benutzeranforderung zu filtern. Zum Beispiel haben Sie Bestellungen und Bestelldetails und Produkte ist Kindersammlung.

Wenn Benutzer möchte von Produkt filtern Ich erhalte eine Fehlermeldung wegen keine Eigenschaft oder ein Feld ‚Produkte‘ existiert in Typ ‚ICollection1'`

ich meine Abfrage wie folgt zu schreiben.

var orders = _uow.Repository() .Query() .Where ("PRODUCTS.HEADING.ToLower(). Enthält (\" foo \ ")") .INCLUDE ("ORDER_DETAILS") . Include ("ORDER_DETAILS.PRODUCTS") .ToList(); Also ist es nicht möglich, Kindersammlungen so zu filtern? Oder irgendeine Art zu filtern?

+0

Haben Sie das versucht? Warum hat es nicht funktioniert? – Will

+0

@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

+0

dynamisch ist ein sehr breites Wort. Es reicht von * dynamischer Spaltenname * bis * unmöglich *. Bitte seien Sie genauer, vielleicht ein paar Beispiele. – user3185569

Antwort

2

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.

Verwandte Themen