是否可以更改Func <t,bool =“”>委托的泛型类型参数?</t,>

时间:2012-08-16 11:24:09

标签: c# generics delegates func type-parameter

我想这个问题的答案是“不”,但无论如何都要进行。

基本上,我有一个Linq2Sql数据提供程序及其生成的类型。我还有业务对象,其属性名称(为了这个问题)与其相关生成类型的属性名称完全匹配。业务类型在整个应用程序中使用,生成的类型仅用于访问数据库 - 由于多种原因,此设置是可取的,因此请不要建议需要对其进行任何更改的答案。

在UI层中,有各种控件允许用户调整搜索方式,例如。要搜索哪些字段,搜索术语等。使用这些控件,我可以创建一个很好的Func<T, bool>委托来封装搜索条件/查询。我遇到的问题是Func委托是使用T类型参数作为业务对象创建的,当它传递给数据访问层时,我需要它是相关生成的类型代替。

所以我的问题是,是否可以在保持相同条件的同时将Func委托的泛型类型参数从业务对象类型更改为相关生成的类型?

eg. can Func<MasterTrack, bool>   =>   Func<DbMasterTrack, bool> when properties match?

请注意,我可以将所有用户选择的搜索参数传递给数据访问层,但其中有很多,所以我希望避免这种情况。

2 个答案:

答案 0 :(得分:3)

我不相信这是可能的,但你可以做到以下几点:

  • DbMasterTrack隐式转换为MasterTrack;
  • 查询时只需将Func<MasterTack,bool>包裹在Func<DbMasterTrack,bool>

我还必须注意,如果您使用的是Func<T, bool>而不是Expression<Func<T, bool>,那么您实际上并未在数据库级别过滤结果集,但这可能是您所需要的已经意识到了。

示例如下:

class MasterTrack
{
    public int Id { get; set; }
    public string Name { get; set; }
}
class DbMasterTrack
{
    public int Id { get; set; }
    public string Name { get; set; }
    public static implicit operator MasterTrack(DbMasterTrack @this)
    {
        return new MasterTrack { Id = @this.Id, Name = @this.Name };
    }
}
class Program
{
    static void Main(string[] args)
    {
        var tracks = new List<DbMasterTrack>
        {
            new DbMasterTrack { Id = 1, Name = "T1" },
            new DbMasterTrack { Id = 2, Name = "T2" },
        };

        Func<MasterTrack, bool> query = t => t.Id == 1;

        var result = tracks.Where((Func<DbMasterTrack, bool>)(t => query(t)));

        foreach (var item in result)
        {
            Console.WriteLine("{0}|{1}", item.Id, item.Name);
        }
    }
}

答案 1 :(得分:2)

一些事情

  1. Linq2Sql也可以使用Expression<Func<T, bool>>代替Func<T, bool>

  2. 无法更改 Expression<Func<T, bool>>的类型

  3. 可以复制/重新创建Expression<Func<T, bool>>,用其他类型替换类型T.

  4. Linq2Sql NOT 在其表达式中接受interface-type。因此,如果您考虑创建“抽象”实际类型的接口,那将无效。

  5. 现在,要从Expression<Func<T2, bool>>创建Expression<Func<T, bool>>,我创建了以下代码。它不是“完整的”,因为并非支持表达式中的所有可能路径。但是检查值的属性(&lt;&gt; =!=或combinaties)的基本和/或组合工作正常。

    使用此代码即可:

    Expression<Func<MasterTrack, bool>> criteria = m => m.Id == 1;
    Expression<Func<DbMasterTrack, bool>> dbCriteria = ExpressionRewriter.CastParam<MasterTrack, DbMasterTrack>(criteria);
    

    我们走了。

    public static class ExpressionRewriter {
        /// <summary>
        /// Casts the param of an expression.
        /// </summary>
        /// <typeparam name="TIn">The type of the in.</typeparam>
        /// <typeparam name="TOut">The type of the out.</typeparam>
        /// <param name="inExpr">The in expr.</param>
        /// <returns></returns>
        public static Expression<Func<TOut, bool>> CastParam<TIn, TOut>(Expression<Func<TIn, bool>> inExpr) {
            if (inExpr.NodeType == ExpressionType.Lambda &&
                inExpr.Parameters.Count > 0) {
    
                var inP = inExpr.Parameters[0];
                var outP = Expression.Parameter(typeof(TOut), inP.Name);
    
                var outBody = Rewrite<TIn, TOut>(
                    inExpr.Body,
                    expr => (expr is ParameterExpression) ? outP : expr
                );
                return Expression.Lambda<Func<TOut, bool>>(
                        outBody,
                        new ParameterExpression[] { outP });
            } else {
                throw new NotSupportedException();
            }
        }
    
        /// <summary>
        /// Rewrites the specified expression.
        /// </summary>
        /// <typeparam name="TIn">The type of the in.</typeparam>
        /// <typeparam name="TOut">The type of the out.</typeparam>
        /// <param name="exp">The exp.</param>
        /// <param name="c">The c.</param>
        /// <returns></returns>
        private static Expression Rewrite<TIn, TOut>(Expression exp, Func<Expression, Expression> c) {
            Expression clone = null;
            var be = exp as BinaryExpression;
            switch (exp.NodeType) {
                case ExpressionType.AndAlso:
                    clone = Expression.AndAlso(Rewrite<TIn, TOut>(be.Left, c), Rewrite<TIn, TOut>(be.Right, c), be.Method);
                    break;
                case ExpressionType.OrElse:
                    clone = Expression.OrElse(Rewrite<TIn, TOut>(be.Left, c), Rewrite<TIn, TOut>(be.Right, c), be.Method);
                    break;
                case ExpressionType.Equal:
                    clone = Expression.Equal(Rewrite<TIn, TOut>(be.Left, c), Rewrite<TIn, TOut>(be.Right, c), be.IsLiftedToNull, be.Method);
                    break;
                case ExpressionType.GreaterThan:
                    clone = Expression.GreaterThan(Rewrite<TIn, TOut>(be.Left, c), Rewrite<TIn, TOut>(be.Right, c), be.IsLiftedToNull, be.Method);
                    break;
                case ExpressionType.GreaterThanOrEqual:
                    clone = Expression.GreaterThanOrEqual(Rewrite<TIn, TOut>(be.Left, c), Rewrite<TIn, TOut>(be.Right, c), be.IsLiftedToNull, be.Method);
                    break;
                case ExpressionType.LessThan:
                    clone = Expression.LessThan(Rewrite<TIn, TOut>(be.Left, c), Rewrite<TIn, TOut>(be.Right, c), be.IsLiftedToNull, be.Method);
                    break;
                case ExpressionType.LessThanOrEqual:
                    clone = Expression.LessThanOrEqual(Rewrite<TIn, TOut>(be.Left, c), Rewrite<TIn, TOut>(be.Right, c), be.IsLiftedToNull, be.Method);
                    break;
                case ExpressionType.NotEqual:
                    clone = Expression.NotEqual(Rewrite<TIn, TOut>(be.Left, c), Rewrite<TIn, TOut>(be.Right, c), be.IsLiftedToNull, be.Method);
                    break;
                case ExpressionType.Not:
                    var ue = exp as UnaryExpression;
                    clone = Expression.Not(Rewrite<TIn, TOut>(ue.Operand, c));
                    break;
                case ExpressionType.MemberAccess:
                    var me = exp as MemberExpression;
    
                    MemberInfo newMember = me.Member;
                    Type newType = newMember.DeclaringType;
                    if (newType == typeof(TIn)) {
                        newType = typeof(TOut);
                        MemberInfo[] members = newType.GetMember(me.Member.Name);
                        if (members.Length == 1) {
                            newMember = members[0];
                        } else {
                            throw new NotSupportedException();
                        }
                    }
                    clone = Expression.MakeMemberAccess(Rewrite<TIn, TOut>(me.Expression, c), newMember);
                    break;
                case ExpressionType.Constant:
                    var ce = exp as ConstantExpression;
                    clone = Expression.Constant(ce.Value);
                    break;
                case ExpressionType.Parameter:
                    var pe = exp as ParameterExpression;
                    Type peNewType = pe.Type;
                    if (peNewType == typeof(TIn)) {
                        peNewType = typeof(TOut);
                    }
                    clone = Expression.Parameter(peNewType, pe.Name);
                    break;
                case ExpressionType.Call:
                    MethodCallExpression mce = exp as MethodCallExpression;
                    if (mce.Arguments != null && mce.Arguments.Count > 0) {
                        List<Expression> expressionList = new List<Expression>();
                        foreach (Expression expression in mce.Arguments) {
                            expressionList.Add(Rewrite<TIn, TOut>(expression, c));
                        }
                        clone = Expression.Call(Rewrite<TIn, TOut>(mce.Object, c), mce.Method, expressionList.ToArray());
                    } else {
                        clone = Expression.Call(Rewrite<TIn, TOut>(mce.Object, c), mce.Method);
                    }
                    break;
                case ExpressionType.Invoke:
                    InvocationExpression ie = exp as InvocationExpression;
                    List<Expression> arguments = new List<Expression>();
                    foreach (Expression expression in ie.Arguments) {
                        arguments.Add(Rewrite<TIn, TOut>(expression, c));
                    }
                    clone = Rewrite<TIn, TOut>(ie.Expression, c);
                    //clone = Expression.Invoke(Rewrite<TIn, TOut>(ie.Expression, c), arguments);
                    break;
                case ExpressionType.Convert:
                    var ue2 = exp as UnaryExpression;
                    //clone = Expression.Not(Rewrite<TIn, TOut>(ue2.Operand, c));
                    clone = Expression.Convert(ue2.Operand, ue2.Type, ue2.Method);
                    break;
                default:
                    throw new NotImplementedException(exp.NodeType.ToString());
            }
            return c(clone);
        }
    }