Динамическое выражение с использованием LINQ. Как найти кухню? - PullRequest
9 голосов
/ 16 августа 2011

Я пытаюсь реализовать пользовательский динамический фильтр, где используется выбирает некоторые свойства, выбирает некоторые операторы и выбирает также значения.

Поскольку я пока не нашел ответа на этот вопрос , я попытался использовать выражения 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
    }
}

Ответы [ 5 ]

6 голосов
/ 16 августа 2011
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: Из, для ваших нуждУ меня пока нет идей.Я дам вам один последний:

Объявите метод расширения для типа String:

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 голосов
/ 22 марта 2013

Чтобы добавить новый тип Enum к динамическому Linq, необходимо добавить следующий код:

typeof(Enum),
typeof(T)

T : Enum type

в предварительно определенных типах динамических.Это работает для меня.

0 голосов
/ 16 августа 2011

Я бы не стал формулировать предложение 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 голосов
/ 16 августа 2011
// ???????????????????????? DOES NOT WORK
var kitchens = houses.AsQueryable().Where(comparison);

Метод Where принимает в качестве параметра Func<House, bool> или Expression<Func<House, bool>>, но переменная comparison имеет тип LambdaExpression, что не соответствует.Вам нужно использовать другую перегрузку метода:

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 голосов
/ 16 августа 2011

что по этому поводу

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