none
请教一个 Lambda 表达式的 写法? RRS feed

  • 问题

  • 首先我有一个 Product 类

    public class Product
    {
        /// <summary>
        /// 主键Id
        /// </summary>
        public int Id { get; set; }
    
        /// <summary>
        /// 名称
        /// </summary>
        public string Name { get; set; }
    
        /// <summary>
        /// 种类Id,外键列
        /// </summary>
        public int? CategoryId { get; set; }
    }

    我可以得到:

    static void Main(string[] args)
    {
        var products = GetAllProducts();
        var query = products.AsQueryable();
                
        IList<Tuple<int, string>> result1 = GetList_IdAndName(query, c => c.Id, c => c.Name);
        Console.WriteLine("调用 GetList_IdAndName 方法:");
        foreach (var item in result1)
        {
            Console.WriteLine(" Id: {0}, Name: {1}", item.Item1, item.Item2);
        }
                
    }
    
    static IEnumerable<Product> GetAllProducts()
    {
        yield return new Product() { Id = 1, Name = "娃哈哈", CategoryId = 1 };
        yield return new Product() { Id = 2, Name = "喜乐多", CategoryId = null };
        yield return new Product() { Id = 3, Name = "康师傅", CategoryId = 1 };
    }
    
    static IList<Tuple<int, string>> GetList_IdAndName(IQueryable<Product> source,
        Expression<Func<Product, int>> expProperty1,
        Expression<Func<Product, string>> expProperty2)
    {
        return source.Select(c => Tuple.Create(c.Id, c.Name)).ToList();
    }

    上面这么写是没有任何问题的,能得到结果,并且打印。

    现在我想把 GetList_IdAndName 泛型化,即抽象一个泛型方法出来,比如:

    static IList<Tuple<TProperty1, TProperty2>> GetList_X<TEntity, TProperty1, TProperty2>(IQueryable<TEntity> source,
        Expression<Func<TEntity, TProperty1>> expProperty1,
        Expression<Func<TEntity, TProperty2>> expProperty2)
    {
        // 这里需要写一个类似于上面那个 GetList_IdAndName 方法 的 Lambda 表达式,该如何写啊?
        // 如果可以写,且 IQueryable 的 Provider 是 Entity Framework 的话,能正常运行吗?
        var resultExpress = (Expression<Func<TEntity, Tuple<TProperty1, TProperty2>>>)null; // 这里改如何写?
    
        return source.Select(resultExpress).ToList();
    }

    并且向下面这样调用,也可以运行,并且打印。

    static void Main(string[] args)
    {
        var products = GetAllProducts();
        var query = products.AsQueryable();
                
        Console.WriteLine("调用 GetList_X 方法:");
        IList<Tuple<int, string>> result2 = GetList_X(query, c => c.Id, c => c.Name);
        foreach (var item in result2)
        {
            Console.WriteLine(" Id: {0}, Name: {1}", item.Item1, item.Item2);
        }
    }

    请问该怎么创建 Lambda 表达式啊?我对创建 Lambda 表达式不熟,请大侠们帮帮忙,谢谢!

    下面是我随便写的,不知道思路对不对?还请大侠们重新帮我写一个高性能的,谢谢!

    //var tupleConstructor = typeof(Tuple<TProperty1, TProperty2>).GetConstructor(new Type[] { typeof(TProperty1), typeof(TProperty2) });
    
    //ParameterExpression propExpress1 = Expression.Parameter(typeof(TProperty1), "item1");
    //ParameterExpression propExpress2 = Expression.Parameter(typeof(TProperty1), "item2");
    
    ////Expression.Call(
    
    //NewExpression newExpress = Expression.New(tupleConstructor, propExpress1, propExpress2);
    
    MethodInfo Method = typeof(Tuple).GetMethod("Create", new Type[] { typeof(TProperty1), typeof(TProperty2) });
    Expression[] paramExpressArray = null;
    MethodCallExpression methodCallExpress = Expression.Call(Method, paramExpressArray);
    
    Expression.Lambda<Func<TEntity, Tuple<TProperty1, TProperty2>>>(methodCallExpress,
    
    var resultExpress = (Expression<Func<TEntity, Tuple<TProperty1, TProperty2>>>)methodCallExpress;
    
    return source.Select(methodCallExpress).ToList();

    请教!


    2014年4月17日 3:18

全部回复

  • 应该是这样:

            static IList<Tuple<TProperty1, TProperty2>> GetList_X<TEntity, TProperty1, TProperty2>(IQueryable<TEntity> source,
                Expression<Func<TEntity, TProperty1>> expProperty1,
                Expression<Func<TEntity, TProperty2>> expProperty2)
            {
                return source.Select(c => Tuple.Create(expProperty1.Compile()(c), expProperty2.Compile()(c))).ToList();
            }

    使用时:

           static void Main(string[] args)
            {
                var products = GetAllProducts();
                var query = products.AsQueryable();
    
                IList<Tuple<int, string>> result1 = GetList_X<Product, int, string>(query, c => c.Id, c => c.Name);
                Console.WriteLine("调用 GetList_X<Product, int, string> 方法:");
                foreach (var item in result1)
                {
                    Console.WriteLine(" Id: {0}, Name: {1}", item.Item1, item.Item2);
                }
    
                IList<Tuple<int, int?>> result2 = GetList_X<Product, int, int?>(query, c => c.Id, c => c.CategoryId);
                Console.WriteLine("调用 GetList_X<Product, int, int?> 方法:");
                foreach (var item in result2)
                {
                    Console.WriteLine(" Id: {0}, CategoryId: {1}", item.Item1, item.Item2);
                }
            }


    • 已编辑 lapheal 2014年4月17日 3:53
    • 已标记为答案 陈书函 2014年4月17日 4:07
    • 取消答案标记 陈书函 2014年4月22日 13:06
    2014年4月17日 3:52
  • 嗯,确实是这样,感觉自己上面那些创建 Lambda 表达式的语句 走了弯路,非常感谢啊!

    2014年4月17日 4:07
  • 哥们,你的方法在 Entity Framework 中用不了啊,抛出 System.NotSupportedException:LINQ to Entities 不识别方法 System.Tuple 方法。我想要的是,EF 生成 SQL 时,也只是查询这 两列 出来。再帮忙想想,有什么版本可以办到?

    比如,如图:

    我想要 EF 生成 SQL 时,只查询出 Name 和 CategoryId 。且能自动封装到 IList<Tuple<string, int?>> 中。


    2014年4月22日 13:12
  • 另外由于 System.Tuple 类的构造函数必须带参数,EF 识别会出错,所以自定义一个名为 MyTuple 的类。可 EF 还是不支持,如下图:


    请大侠们帮帮忙!


    2014年4月22日 13:49