2011-08-16 19 views
9

Tôi cố gắng thực hiện bộ lọc động người dùng, nơi được sử dụng chọn một số thuộc tính, chọn một số toán tử và chọn cũng là giá trị.Biểu thức động bằng LINQ. Làm thế nào để tìm nhà bếp?

Vì tôi chưa tìm thấy câu trả lời cho this question, tôi đã cố gắng sử dụng các biểu thức LINQ.
Chủ yếu là tôi cần phải xác định tất cả các nhà mà phòng chính là nhà bếp (bất kỳ cảm giác nào, tôi biết).

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 
    } 
} 

Trả lời

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

Nhưng bạn phải thiết lập các RoomType tài sản trên các phòng trước.

Ok, chỉnh sửa:

vì vậy bạn phải xác định lại:

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

Sau đó, khi bạn sử dụng nó:

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

Chỉnh sửa # 2:

Ok, ở đây bạn go:

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); 

Chỉnh sửa # 3: Về, cho nhu cầu của bạn, tôi không còn ý tưởng cho bây giờ nữa. Tôi cung cấp cho bạn một mới nhất:

Khai báo một phương pháp mở rộng vào loại String:

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

    return value; 
} 

Sau đó, sử dụng nó trong biểu thức như:

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

Đó là bởi vì rõ ràng là sự đếm được đối xử khác nhau. Phần mở rộng đó sẽ để lại chuỗi không thay đổi cho các loại khác. Nhược điểm: bạn phải thêm typeof() vào đó.

+0

Tôi cần truy vấn * động *. Truy vấn này là một truy vấn tĩnh. Nếu người dùng sẽ chọn thuộc tính khác thay vì "Loại" của phòng, phương pháp của bạn sẽ được cập nhật ... loại RoomType là Kitchen (0) theo mặc định. – serhio

+0

nhận xét tốt. Bây giờ ngoại lệ thời gian chạy là "Loại đối số không khớp" khi xây dựng "so sánh". PS. Không cần phải "biên dịch" một ưu tiên ... – serhio

+0

@serhio Kiểm tra xem điều này có phù hợp với bạn hay không. – Vladimir

-1

gì về vấn đề này

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

Tôi cần truy vấn * động *. Truy vấn này là một truy vấn tĩnh.Nếu người dùng sẽ chọn thuộc tính khác thay vì "Loại" của phòng, phương pháp của bạn sẽ được cập nhật ... – serhio

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

Phương pháp Where mất một Func<House, bool> hoặc một Expression<Func<House, bool>> như tham số, nhưng biến comparison là loại LambdaExpression, mà không phù hợp. Bạn cần sử dụng một tình trạng quá tải khác của phương thức:

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

Tôi đã hiểu điều này từ Visual Studio. Cảm ơn bạn. Tuy nhiên, tôi không biết làm thế nào để tiến hành. – serhio

0

Tôi không xây dựng mệnh đề where theo cách đó - tôi nghĩ nó phức tạp hơn nhu cầu của bạn. Thay vào đó, bạn có thể kết hợp nơi khoản như thế này:

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); 

Tôi đã thử nghiệm này, trung thực :)

+0

cảm ơn bạn, tôi sẽ kiểm tra nó. Để rõ ràng hơn, tôi cố gắng làm một việc như sau: http://stackoverflow.com/q/7026855/185593 – serhio

+0

Trừ khi bạn cần bao gồm thứ tự do người dùng chỉ định kết quả, tôi khuyên bạn nên sử dụng các biến vị ngữ như thế này. Nếu bạn cần cho phép người dùng đặt hàng kết quả, tôi khuyên bạn nên sử dụng LINQ động. –

+0

như bạn có thể thấy trong GUI từ liên kết nhận xét đầu tiên, tôi không thể biết * a ưu tiên * (các) thuộc tính áp dụng cho bộ lọc. Vì vậy, các biến như "userEnteredName" không có cảm giác trong mã của tôi, cho đến nay tôi không xử lý thuộc tính "Name", nhưng với một thuộc tính chưa biết (danh sách) (các) toán tử chưa biết. – serhio

-1

Để thêm một Enum loại mới để động LINQ, bạn phải thêm đoạn mã sau:

typeof(Enum), 
typeof(T) 

T : Enum type 

trong các loại động được xác định trước. Nó ổn với tôi.

+0

rõ ràng hơn, ý của bạn là gì .. – nawfal

Các vấn đề liên quan