【问题标题】:linq expression tree many to many IQueryable extensionlinq 表达式树多对多 IQueryable 扩展
【发布时间】:2020-06-21 14:57:18
【问题描述】:

我目前正在开发一个 asp.net web api 2 应用程序。请记住我的经验是 php、js、css 等,而不是 c#。

我希望有人可以帮助我使用使用 Linq 表达式树的通用 IQueryable 扩展。

我有 3 个表(实体框架)。

交易

Id,Name

ProductTrades(链接表)

Id,Trade_Id,Product_Id

产品

Id,Code

我需要从作为变量传递的产品代码列表中返回具有至少一个产品代码的所有交易

我可以很好地使用以下内容

list<string> tocheck = new list<string>("productcode1","productcode2");
var trades = db.Trades.SqlQuery(
               "Select * from Trades T " +
               "left join ProductTrades PT on T.Id = PT.Trade_Id " +
               "left join Products P on PT.Product_Id = P.Id " +
               "where P.Code in (@prod)", new SqlParameter("@prod", String.Join(",", tocheck))).ToList();

但是我想使用 Linq 表达式树来实现它以使其通用

以下是我的尝试(我在尝试学习时使用多个资源将其放在一起)

public static IQueryable<T> FilterWhereItemIn<T>(this IQueryable<T> queryable, string propertyOrFieldName, List<string> values)
        {
            var elementType = typeof(T);
            var parameterExpression = Expression.Parameter(elementType);
            var propertyOrFieldExpression = propertyOrFieldName.Split('.').Aggregate((Expression)parameterExpression, Expression.PropertyOrField);
            var method = typeof(List<string>).GetMethod("Contains", new Type[] { typeof(string) });
            var someValue = Expression.Constant(values, typeof(List<string>));
            var containsExpression = Expression.Call(propertyOrFieldExpression, method, someValue);
            var selector = Expression.Lambda<Func<T, bool>>(containsExpression, parameterExpression);
            return queryable.Where(selector);
        }

我用以下方式调用它(IQueryable queryable 是 Trade 类型)

list<string> tocheck = new list<string>("productcode1","productcode2");
queryable.FilterWhereItemIn("Products.Code", tocheck );

但是,我收到“代码”不存在的错误消息。我认为这是因为 Products 是一个集合。有谁知道如何实现这一点。

(抱歉这篇文章有点仓促)

【问题讨论】:

    标签: entity-framework asp.net-web-api2 iqueryable linq-expressions


    【解决方案1】:

    如果您想构建上面原始问题中提到的自定义 WHERE 子句。这是一个工作示例

    public static class WhereGenerator
        {
            // Where Generator For Contains
            public static IQueryable<T> FilterWhereItemIn<T>(this IQueryable<T> queryable, string propertyOrFieldName, List<string> values)
            {
                var elementType = typeof(T);
                var parameterExpression = Expression.Parameter(elementType);
                var propertyOrFieldExpression = propertyOrFieldName.Split('.').Aggregate((Expression)parameterExpression, Expression.PropertyOrField);
                var method = typeof(List<string>).GetMethod("Contains", new Type[] { typeof(string) });
                var someValue = Expression.Constant(values, typeof(List<string>));
                var containsExpression = Expression.Call(someValue, method, propertyOrFieldExpression);
                var selector = Expression.Lambda<Func<T, bool>>(containsExpression, parameterExpression);
                return queryable.Where(selector);
            }
        }
    
            public class Products
            {
                public int Id { get; set; }
                public string Code { get; set; }
            }
            public class Trades
            {
                public int Id { get; set; }
                public string Name { get; set; }
            }
            public class ProductTrades
            {
                public int Id { get; set; }
                public Trades Trade { get; set; }
                public Products Product { get; set; }
            }
    
            static void Main(string[] args)
            {
                List<ProductTrades> lpt2 = new List<ProductTrades> { new ProductTrades { Id = 1, 
                                                                                         Trade = new Trades { Id = 13, Name = "trade2" }, 
                                                                                         Product = new Products { Code = "productcode1", Id = 1 } },
                                                                    new ProductTrades { Id = 11,
                                                                                         Trade = new Trades { Id = 13, Name = "trade4" },
                                                                                         Product = new Products { Code = "productcode2", Id = 2 } },
                                                                    new ProductTrades { Id = 1,
                                                                                         Trade = new Trades { Id = 13, Name = "trade2" },
                                                                                         Product = new Products { Code = "productcode5", Id = 1 } }
                                                                  };
    
                List<string> tocheck = new List<string>() { "productcode1", "productcode2" };
                var r1 = lpt2.AsQueryable().FilterWhereItemIn("Product.Code", tocheck);
                var r2 = r1.ToList();
    }
    
    

    【讨论】:

      【解决方案2】:

      Expression.Call 行中的参数混淆了。您应该针对列表而不是属性调用 Contains 方法。改用这个:

      var containsExpression = Expression.Call(someValue, method, propertyOrFieldExpression);
      

      【讨论】:

      • 感谢您的快速回复。明天当我回到工作岗位时我会试试这个,如果它有效,我会接受这个答案。
      • 不幸的是,这并没有解决问题。它在var propertyOrFieldExpression = propertyOrFieldName.Split('.').Aggregate((Expression)parameterExpression, Expression.PropertyOrField); 这一行跌倒,出现错误“ExceptionMessage”:“'Code' is not a member of type 'System.Collections.Generic.ICollection1[TradeSync.Models.Product]'", "ExceptionType": "System.ArgumentException",
      • 看起来我当前的代码可以一对一地工作。但由于收藏而不是多对多。不幸的是,表达式树对我来说是新的。
      • 哦,我明白了,我错过了问题的那一部分,所以是的,这是因为 Products 是一个集合。因此,您还需要在表达式中建模类似 x.Products.Any(x =&gt; x.Code) 的东西。这要复杂得多,因为您需要在每个级别确定属性是否为集合。
      • 旁注,当有其他可能更适合的好工具时,听起来您几乎想构建一个自定义查询引擎。例如,GraphQL 是实现此目的的好方法。
      【解决方案3】:

      在与 DavidG 讨论之后(谢谢),看起来我在尝试实现目标时走错了路。

      我不需要进行扩展,我可以使用简单的单行 linq 查询来获得所需的结果。

      var trades = new List<Trade>();
      trades = db.Trades.Where(t => t.Products.Any(p => getRequest.products.Contains(p.Code))).ToList();
      
      

      getRequest 只是一个简单的类,它有一个 public list&lt;string&gt; products { get; set; } getter 和 setter

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-05-30
        • 1970-01-01
        • 1970-01-01
        • 2010-12-07
        • 1970-01-01
        相关资源
        最近更新 更多