2015-09-03 56 views
0

我處於需要在運行時在EF上下文中指定查詢的情況。我們的顧問根據客戶的特定情況在客戶現場配置這些查詢。如何在運行時使用不同類型的多個子句構建linq查詢

爲了方便我考慮使用linq構建查詢,基於顧問在某種類型的前端(現在,winforms)中指定的條件列表。顧問基本上從一個對象指定一個屬性,指定操作符,然後指定值。例如:給我所有的客戶,其中的[狀態] [等於] [1]。

此刻我有一個表達式構建器,它在運行時創建where子句,到目前爲止,我可以管理一個group by子句。我在這裏遇到困難的時候,顧問是通過不同類型的子句(f.e.一個字符串和一個datetime屬性)來配置多個組。

例如,我必須能夠處理這個查詢:從bsn,dateofbirth(其中bsn = string和dateofbirth = datetime)的status = 1 group的客戶機中選擇bsn as a,dateofbirth as b。

目前,這是「膠水」,查詢起來代碼:

public List<ClientV2> ExportClients(List<CriteriaV2> criteriaList) 
{ 
    var whereExpression = BuildWhereExpressionChain(criteriaList.Where(c => c.Operator != CriteriaOperatorV2.GROUPBY).ToList()); 

    var groupByExpression = BuildGroupByExpression(criteriaList.Where(c => c.Operator == CriteriaOperatorV2.GROUPBY).ToList()); 

    var sourceClients = _context.Clients.Where(whereExpression).GroupBy(groupByExpression).ToList(); 

    IEnumerable<Client> resultClients = sourceClients.SelectMany(group => group); 

    return ClientToClientV2.MapList(resultClients.ToList()); 
} 

這是where子句建設者:

private Expression<Func<Client, bool>> BuildWhereExpressionChain(List<CriteriaV2> criteriaList) 
{ 
    var expressionList = new List<Expression<Func<Client, bool>>>(); 
    var paramExp = Expression.Parameter(typeof(Client)); 

    foreach (var crit in criteriaList) 
    { 
     var propertyItem = PropertyTranslator.GetPropertyItem(crit.Property); 
     if (propertyItem == null) throw new InvalidFilterCriteriaException("Property " + crit.Property + " niet toegestaan als filter criterium"); 

     var propInfo = typeof(Client).GetProperty(propertyItem.InternalName); 
     var left = Expression.Property(paramExp, propInfo); 
     Expression right; 

     if (propInfo.PropertyType.IsEnum) 
      right = Expression.Constant(Enum.ToObject(propInfo.PropertyType, PropertyTranslator.TranslateEnum(propertyItem.Type, crit.Value))); 
     else if (propInfo.PropertyType == typeof(DateTime) || propInfo.PropertyType == typeof(DateTime?)) 
      right = Expression.Constant(DateTime.Parse(crit.Value), propInfo.PropertyType); 
     else 
      right = Expression.Constant(crit.Value, typeof(string)); 

     var exp = BuildExpression(left, right, crit.Operator); 

     expressionList.Add(Expression.Lambda<Func<Client, bool>>(exp, new ParameterExpression[] { paramExp })); 
    } 

    var firstExpression = expressionList.First(); 
    expressionList.Skip(1).ToList().ForEach(ex => { firstExpression = firstExpression.And(ex); }); 

    return firstExpression; 
} 

而這正是我的一部分)在

private Expression<Func<Client, string>> BuildGroupByExpression(List<CriteriaV2> criteriaList) 
{ 
    var expressionList = new List<Expression<Func<Client, string>>>(); 
    var paramExp = Expression.Parameter(typeof(Client)); 

    foreach (var crit in criteriaList) 
    { 
     var propertyItem = PropertyTranslator.GetPropertyItem(crit.Property); 
     if (propertyItem == null) throw new InvalidFilterCriteriaException("Property " + crit.Property + " niet toegestaan als group by criterium"); 

     var propInfo = typeof(Client).GetProperty(propertyItem.InternalName); 
     var body = Expression.Property(paramExp, propInfo); 
     var lambda = Expression.Lambda<Func<Client, string>>(body, paramExp); 
     expressionList.Add(lambda); 
    } 

    var firstExpression = expressionList.First(); 
    expressionList.Skip(1).ToList().ForEach(ex => { firstExpression = firstExpression.And(ex); }); 

    return firstExpression; 
} 

是否有可能使BuildGroupByExpression(:卡(它爲一個字串類型的從句工作)這樣,它會導致一個表達式,其中包含多個可以直接在.GroupBy(表達式)中使用的不同類型的子句;?

我對linq的專家並不多,但我有一種感覺,我想要的是可能的。如果我在這裏做一些愚蠢的事情,請將它們指出來,然後我會繼續努力。

+0

你試過dynamiclinq? –

+0

我試過了,但不知怎的,它從來沒有得到它的工作,因爲它似乎並不支持多個分組的子句,也不需要自己寫。我確實發現缺少tbh的文檔,它只是一堆.cs文件,所以也許我錯過了一些內容。 – Helfdane

回答

0

我不這麼認爲。至少使用表達式構建的方法,就像你擁有的方式一樣。至少我最終建立了每種類型的表達式。 主要原因是

var lambda = Expression.Lambda<Func<Client, string>>(body, paramExp); 

我不知道你怎麼能做出這樣的λ確定指標動態的還是通用的。

有一種不同的方法,以及一個在運行時使用字符串解釋來構建表達式的庫。見

Install-Package System.Linq.Dynamic.Library 

又見https://msdn.microsoft.com/en-US/vstudio/bb894665.aspx

相關問題