2011-06-23 69 views
5

我期待實現一個系統,通過該系統可以「建立」條件,然後從數據庫中返回結果數據。目前,有一個存儲過程可以即時生成SQL並執行它。這是我想要刪除的特定問題。具有多個/未知條件的動態LINQ查詢

我的問題來自於我可以在我的標準中有多個字段,並且對於這些字段中的每個字段,可能有一個或多個值,以及不同的潛在操作符。

例如,

from t in Contacts 
where t.Email == "[email protected]" || t.Email.Contains ("mydomain") 
where t.Field1 == "valuewewant" 
where t.Field2 != "valuewedontwant" 
select t 

的字段,條件和操作者被存儲在數據庫(以及List<FieldCriteria>)和會是這樣一些東西(基於上文);

Email, Equals, "[email protected]" 
Email, Contains, "mydomain" Field1, 
Equals, "valuewewant" Field2, 
DoesNotEqual, "valuewedontwant" 

new FieldCriteria 
{ 
FieldName = "Email", 
Operator = 1, 
Value = "[email protected]" 
} 

所以利用我的資料,我希望能夠建立一個查詢任意數量的條件。我已經看到以前的鏈接到動態Linq和PredicateBuilder,但我無法將其視爲解決我自己的問題。

任何建議,將不勝感激。

更新

約動態LINQ的建議繼,我想出了一個非常基本的解決方案,使用單個操作員,2場和多個標準。目前在LinqPad中有一些簡單的代碼,但結果正是我想要的;

enum Operator 
{ 
    Equals = 1, 
} 

class Condition 
{ 
    public string Field { get; set; } 
    public Operator Operator { get; set;} 
    public string Value { get; set;} 
} 

void Main() 
{ 
    var conditions = new List<Condition>(); 

    conditions.Add(new Condition { 
     Field = "Email", 
     Operator = Operator.Equals, 
     Value = "[email protected]" 
    }); 

    conditions.Add(new Condition { 
     Field = "Email", 
     Operator = Operator.Equals, 
     Value = "[email protected]" 
    }); 

    conditions.Add(new Condition { 
     Field = "Field1", 
     Operator = Operator.Equals, 
     Value = "Chris" 
    }); 

    var statusConditions = "Status = 1"; 

    var emailConditions = from c in conditions where c.Field == "Email" select c; 
    var field1Conditions = from c in conditions where c.Field == "Field1" select c; 


    var emailConditionsFormatted = from c in emailConditions select string.Format("Email=\"{0}\"", c.Value); 
    var field1ConditionsFormatted = from c in field1Conditions select string.Format("Field1=\"{0}\"", c.Value); 

    string[] conditionsArray = emailConditionsFormatted.ToArray(); 
    var emailConditionsJoined = string.Join("||", conditionsArray); 
    Console.WriteLine(String.Format("Formatted Condition For Email: {0}",emailConditionsJoined)); 

    conditionsArray = field1ConditionsFormatted.ToArray(); 
    var field1ConditionsJoined = string.Join("||", conditionsArray); 
    Console.WriteLine(String.Format("Formatted Condition For Field1: {0}",field1ConditionsJoined)); 



    IQueryable results = ContactView.Where(statusConditions); 

    if (emailConditions != null) 
    { 
     results = results.Where(emailConditionsJoined); 
    } 

    if (field1Conditions != null) 
    { 
     results = results.Where(field1ConditionsJoined); 
    } 

    results = results.Select("id"); 

    foreach (int id in results) 
    { 
     Console.WriteLine(id.ToString()); 
    } 
} 

隨着生成的SQL;

-- Region Parameters 
DECLARE @p0 VarChar(1000) = 'Chris' 
DECLARE @p1 VarChar(1000) = '[email protected]' 
DECLARE @p2 VarChar(1000) = '[email protected]' 
DECLARE @p3 Int = 1 
-- EndRegion 
SELECT [t0].[id] 
FROM [Contacts].[ContactView] AS [t0] 
WHERE ([t0].[field1] = @p0) AND (([t0].[email] = @p1) OR ([t0].[email] = @p2)) AND ([t0].[status] = @p3) 

和控制檯輸出:

Formatted Condition For Email: Email="[email protected]"||Email="[email protected]" 
Formatted Condition For Field1: Field1="Chris" 

只需要打掃一下,並添加其他運營商,它是好​​看。

如果有人至今對這個有任何意見,任何輸入,將不勝感激

回答

1

我覺得動態LINQ將是選項之一。 DLINQ允許您將部分LINQ查詢指定爲「字符串」,然後DLINQ將該字符串編譯爲表達式樹,以便傳遞給底層的LINQ提供程序。您的需求也是相同的,即您需要在運行時創建表達式樹。

我建議您將Operator作爲FieldCriteria作爲Enum,代表所有必需的操作(等於,小於等等)。然後,你將需要編寫一個函數,該函數採用FieldCriteria的列表並返回一個「表達式」字符串,然後可以將其輸入到DLINQ中以獲取表達式樹。

+0

「LINQ to 」的整個想法是將靜態C#編譯時表達式轉換爲某種類型的字符串以發送到底層數據庫以供執行。你建議使用一個字符串轉換爲一個linq表達式,然後轉換爲一個字符串似乎有點多餘:) –

+0

你是正確的關於「靜態」表達式的SQL,但在用戶不知道的問題關於編譯時的表達式,因此您需要在運行時生成表達式。 DLINQ是通過「字符串」實現的一種方式,其他方式是使用Expression API在運行時創建所需的表達式。 – Ankur

9

LINQ的訣竅是從數據中構建一個Expression。作爲一個例子,來說明所示的例子:

var param = Expression.Parameter(typeof(MyObject), "t"); 

var body = Expression.Or(
      Expression.Equal(Expression.PropertyOrField(param, "Email"), Expression.Constant("[email protected]")), 
      Expression.Call(Expression.PropertyOrField(param, "Email"), "Contains", null, Expression.Constant("mydomain")) 
     ); 

body = Expression.AndAlso(body, Expression.Equal(Expression.PropertyOrField(param, "Field1"), Expression.Constant("valuewewant"))); 
body = Expression.AndAlso(body, Expression.NotEqual(Expression.PropertyOrField(param, "Field2"), Expression.Constant("valuewedontwant"))); 

var lambda = Expression.Lambda<Func<MyObject, bool>>(body, param); 

var data = source.Where(lambda); 

特別要注意的是如何AndAlso可以用來構成的各種操作(相同的多個Where,但更簡單)。

+0

上面的代碼不需要你知道字段/標準/操作符。在我的場景中。在編譯時沒有人知道它,在這方面它需要是完全動態的。原諒我,我錯了,因爲Lambda函數有點超出了我目前的知識範圍。 – ChrisBint

+0

@Chris不,但你必須編寫一些代碼來生成表達式 –

+0

如何在where子句中使用IN? – Neo

-2

這可以簡單地通過Linq來完成,您可以將附加運算符附加到查詢對象。這是一個例子。

query = db.Contacts.Where(...); 
query = query.Where(...); 
query = query.Where(...); 

這是一個更簡單和簡短的解決方案。