2011-08-16 61 views
9

我嘗試實現一個用戶動態過濾器,其中使用選擇一些屬性,選擇一些運算符並選擇值。使用LINQ的動態表達。如何找到廚房?

由於我還沒有找到this question的答案,我嘗試使用LINQ表達式。
主要我需要確定所有房間的主要房間是廚房(任何敏感,我知道)。

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Linq.Expressions; 
//using System.Linq.Dynamic; 

namespace ConsoleApplication2 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      Room aRoom = new Room() { Name = "a Room" }; 
      Room bRoom = new Room() { Name = "b Room" }; 
      Room cRoom = new Room() { Name = "c Room" }; 

      House myHouse = new House 
      { 
       Rooms = new List<Room>(new Room[] { aRoom }), 
       MainRoom = aRoom 
      }; 
      House yourHouse = new House() 
      { 
       Rooms = new List<Room>(new Room[] { bRoom, cRoom }), 
       MainRoom = bRoom 
      }; 
      House donaldsHouse = new House() 
      { 
       Rooms = new List<Room>(new Room[] { aRoom, bRoom, cRoom }), 
       MainRoom = aRoom 
      }; 

      var houses = new List<House>(new House[] { myHouse, yourHouse, donaldsHouse }); 

      //var kitchens = houses.AsQueryable<House>().Where("MainRoom.Type = RoomType.Kitchen"); 
      //Console.WriteLine("kitchens count = {0}", kitchens.Count()); 

      var houseParam = Expression.Parameter(typeof(House), "house"); 
      var houseMainRoomParam = Expression.Property(houseParam, "MainRoom"); 
      var houseMainRoomTypeParam = Expression.Property(houseMainRoomParam, "Type"); 

      var roomTypeParam = Expression.Parameter(typeof(RoomType), "roomType"); 

      var comparison = Expression.Lambda(
       Expression.Equal(houseMainRoomTypeParam, 
       Expression.Constant("Kitchen", typeof(RoomType))) 
       ); 

      // ???????????????????????? DOES NOT WORK 
      var kitchens = houses.AsQueryable().Where(comparison); 

      Console.WriteLine("kitchens count = {0}", kitchens.Count()); 
      Console.ReadKey(); 

     } 
    } 

    public class House 
    { 
     public string Address { get; set; } 
     public double Area { get; set; } 
     public Room MainRoom { get; set; } 
     public List<Room> Rooms { get; set; } 
    } 

    public class Room 
    { 
     public double Area { get; set; } 
     public string Name { get; set; } 
     public RoomType Type { get; set; } 
    } 

    public enum RoomType 
    { 
     Kitchen, 
     Bedroom, 
     Library, 
     Office 
    } 
} 

回答

6
var kitchens = from h in houses 
       where h.MainRoom.Type == RoomType.Kitchen 
       select h; 

但必須前設置在房間RoomType財產。

好吧,編輯:

,所以你必須重新定義:

var comparison = Expression.Lambda<Func<House, bool>>(... 

然後,當你使用它:

var kitchens = houses.AsQueryable().Where(comparison.Compile()); 

編輯#2:

好了,在這裏你去:

var roomTypeParam = Expression.Parameter(typeof(RoomType), "roomType"); 



// ???????????????????????? DOES NOT WORK 
var comparison = Expression.Lambda<Func<House, bool>>(
    Expression.Equal(houseMainRoomTypeParam, 
    Expression.Constant(Enum.Parse(typeof(RoomType), "Kitchen"), typeof(RoomType))), houseParam); 



// ???????????????????????? DOES NOT WORK 
var kitchens = houses.AsQueryable().Where(comparison); 

編輯#3:爲了您的需要,我現在沒有想法。我給你最後一個一個:

字符串類型聲明的擴展方法:

internal static object Prepare(this string value, Type type) 
{ 
    if (type.IsEnum) 
     return Enum.Parse(type, value); 

    return value; 
} 

然後用它在表達這樣的:

Expression.Constant("Kitchen".Prepare(typeof(RoomType)), typeof(RoomType)) 

這是因爲顯然枚舉的處理方式不同。該擴展將使字符串不會改變爲其他類型。缺點:你必須在那裏添加另一個typeof()

+0

我需要*動態*查詢。這個查詢是一個靜態的。如果用戶將選擇其他屬性而不是房間的「類型」,則應更新您的方法...默認情況下RoomType爲Kitchen(0)。 – serhio

+0

好評。在構建「比較」時,現在運行時異常是「參數類型不匹配」。 PS。不需要先「編譯」... – serhio

+0

@serhio檢查是否適合你。 – Vladimir

-1

這個怎麼

var kitchens = houses 
       .SelectMany(h => h.Rooms, (h, r) => new {House = h, Room = r}) 
       .Where(hr => hr.Room.Type == RoomType.Kitchen) 
       .Select(hr => hr.House); 
+2

我需要*動態*查詢。這個查詢是一個靜態的。如果用戶將選擇其他屬性而不是「房間類型」,則應更新您的方法... – serhio

0
// ???????????????????????? DOES NOT WORK 
var kitchens = houses.AsQueryable().Where(comparison); 

Where方法以一個Func<House, bool>Expression<Func<House, bool>>作爲參數,但可變comparisonLambdaExpression類型,它不匹配的。您需要使用該方法的另一重載:

var comparison = Expression.Lambda<Func<House, bool>>(
       Expression.Equal(houseMainRoomTypeParam, 
       Expression.Constant("Kitchen", typeof(RoomType)))); 
//now the type of comparison is Expression<Func<House, bool>> 

//the overload in Expression.cs 
public static Expression<TDelegate> Lambda<TDelegate>(Expression body, params ParameterExpression[] parameters); 
+0

我已經從Visual Studio中理解了這一點。謝謝。但是,我不知道如何繼續。 – serhio

0

我不會建在這樣的where子句 - 我認爲這是比它需要爲你的需求更加複雜。相反,你可以結合where子句是這樣的:

var houses = new List<House>(new House[] { myHouse, yourHouse, donaldsHouse }); 

// A basic predicate which always returns true: 
Func<House, bool> housePredicate = h => 1 == 1; 

// A room name which you got from user input: 
string userEnteredName = "a Room"; 

// Add the room name predicate if appropriate: 
if (!string.IsNullOrWhiteSpace(userEnteredName)) 
{ 
    housePredicate += h => h.MainRoom.Name == userEnteredName; 
} 

// A room type which you got from user input: 
RoomType? userSelectedRoomType = RoomType.Kitchen; 

// Add the room type predicate if appropriate: 
if (userSelectedRoomType.HasValue) 
{ 
    housePredicate += h => h.MainRoom.Type == userSelectedRoomType.Value; 
} 

// MainRoom.Name = \"a Room\" and Rooms.Count = 3 or 
// ????????????????????????? 
var aRoomsHouses = houses.AsQueryable<House>().Where(housePredicate); 

我測試這一塊,說實話:)

+0

謝謝,我會測試它。爲了更清楚起見,我嘗試去做這樣的事情:http://stackoverflow.com/q/7026855/185593 – serhio

+0

除非你需要包含用戶指定的結果順序,否則我建議使用這樣的謂詞。如果您需要允許用戶訂購結果,我建議使用動態LINQ。 –

+0

,正如您在GUI中從第一條評論的鏈接中看到的那樣,我無法知道* priori *應用於過濾器的屬性。所以像「userEnteredName」這樣的變量在我的代碼中沒有任何意義,至今我沒有處理屬性「名稱」,但具有最初未知的(列表)屬性和未知的運算符。 – serhio

-1

要將新Enum類型添加到動態的LINQ,您必須添加以下代碼:

typeof(Enum), 
typeof(T) 

T : Enum type 

在預定義類型的動態。這對我行得通。

+0

更清楚你是什麼意思.. – nawfal

相關問題