如何在可查询的linq表达式树中调用sql标量函数?

时间:2019-04-15 07:21:18

标签: c# linq function lambda expression

我正在为Iqueryable创建lambda表达式以从集合中获取值,但我想将该值转换为其他数据类型,例如int或小数。因此,由于我无法在Iqueryable中使用c#强制转换,因此我在sql中创建了用户定义的标量函数,并尝试在表达式中访问它,但是它抛出了“方法名”无法转换为sql表达式的异常。

public class Context
{

[DbFunction("dbo", "ConvertToDouble")]
        public int? ConvertToDouble(string value)
        {
            var sql = $"set @result = dbo.[ConvertToDouble]('{value}')";
            var output = new SqlParameter { ParameterName = @"result", DbType = DbType.Int32, Size = 16, Direction = ParameterDirection.Output };
            var result = Database.ExecuteSqlCommand(sql, output);
            return output.Value as int?;
        }
}


private static Expression<Func<TSource, TDataType>> CreateLamdaExpression<TSource, TDataType>(string fieldName)
        {
            var parameterExpression = Expression.Parameter(typeof(TSource));

            var collectionParameter = Expression.Property(parameterExpression, "CustomFieldValues");
            var childType = collectionParameter.Type.GetGenericArguments()[0];
            var propertyParameter = Expression.Parameter(childType, childType.Name);

            var left = Expression.Property(propertyParameter, "Name");
            var right = Expression.Constant(fieldName);

            var innerLambda = Expression.Equal(left, right);

            var innerFunction = Expression.Lambda(innerLambda, propertyParameter);

            var method = typeof(Enumerable).GetMethods().Where(m => m.Name == "FirstOrDefault" && m.GetParameters().Length == 2).FirstOrDefault().MakeGenericMethod(typeof(CustomFieldValue));

            var outerLambda = Expression.Call(method, Expression.Property(parameterExpression, collectionParameter.Member as System.Reflection.PropertyInfo), innerFunction);
            var propertyGetter = Expression.Property(outerLambda, "Value");

            if (typeof(TDataType) != typeof(object))
            {
               /var changeTypeCall = Expression.Call(Expression.Constant(Context), Context.GetType().GetMethod("ConvertToDouble", BindingFlags.Public | BindingFlags.Instance),
                                                            propertyGetter
                                                               );

                Expression convert = Expression.Convert(changeTypeCall,
                                                        typeof(TDataType));

                return Expression.Lambda<Func<TSource, TDataType>>(convert, new ParameterExpression[] { parameterExpression });
            }

            var result = Expression.Lambda<Func<TSource, TDataType>>(propertyGetter, new ParameterExpression[] { parameterExpression });
            return result;
        }

0 个答案:

没有答案