使用 LINQ 的动态表达式。如何找到厨房?

发布于 2024-11-30 05:06:18 字数 2679 浏览 1 评论 0 原文

我尝试实现一个用户动态过滤器,其中使用选择一些属性,选择一些运算符并选择值。

由于我还没有找到这个问题的答案,因此我尝试使用 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
    }
}

I try do implement a user dynamic filter, where used selects some properties, selects some operators and selects also the values.

As I didn't find yet an answer to this question, I tried to use LINQ expressions.
Mainly I need to identify all houses which main rooms are kitchens(any sens, I know).

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

一个人练习一个人 2024-12-07 05:06:18
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()

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

But you must set the RoomType property on the rooms before.

Ok, edit:

so you must redefine:

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

Then, when you use it:

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

Edit #2:

Ok, here you 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);

Edit #3: Of, for your needs, I am out of ideas for now. I give you one last one:

Declare an extension method on the String type:

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

    return value;
}

Then use it in that expression like:

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

That's because apparently enums are treated differently. That extension will leave the string unaltered for other types. Drawback: you have to add another typeof() there.

一片旧的回忆 2024-12-07 05:06:18
// ???????????????????????? DOES NOT WORK
var kitchens = houses.AsQueryable().Where(comparison);

Where 方法采用 FuncExpression> 作为参数,但变量 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);
// ???????????????????????? DOES NOT WORK
var kitchens = houses.AsQueryable().Where(comparison);

The Where method takes a Func<House, bool> or a Expression<Func<House, bool>> as the parameter, but the variable comparison is of type LambdaExpression, which doesn't match. You need to use another overload of the method:

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);
三寸金莲 2024-12-07 05:06:18

我不会以这种方式构建 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);

我测试了这个,诚实:)

I wouldn't build the where clause in that way - I think it's more complex than it needs to be for your needs. Instead, you can combine where clauses like this:

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

I tested this one, honest :)

讽刺将军 2024-12-07 05:06:18

这个怎么样

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

what about this

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);
得不到的就毁灭 2024-12-07 05:06:18

添加以下代码:

typeof(Enum),
typeof(T)

T : Enum type

要向动态 Linq 添加新的 Enum 类型,您必须在预定义的动态类型中 这对我有用。

To add a new Enum type to dynamic Linq, you must add the following code :

typeof(Enum),
typeof(T)

T : Enum type

in predefined types of dynamic. That works for me.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文