2012-08-16 46 views
4

我想这个问题的答案是'不',但无论如何。是否可以更改Func <T, bool>委托的泛型类型参数?

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

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

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

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

还请注意,我可以只通过所有的用户选择的搜索参数对数据访问层的,但也有相当多的人,所以我希望能避免这种情况。

+1

制作接口IMasterTrack并在'Func'中使用它。你们两个班都必须实施'IMasterTrack'。我希望这会起作用。 – Leri 2012-08-16 11:28:00

+0

你能告诉我们你的'MasterTrack'和'DbMasterTrack'对象是什么样子,它们之间是否存在任何形式的继承,它们是遵循共享契约'接口'还是抽象,还是仅仅是两个独立的具体类完全相同的财产结构? – 2012-08-16 11:30:23

+0

正如@Maarten所说,'Linq2Sql将不会在其表达式中接受一个接口类型。 – Sheridan 2012-08-16 12:57:16

回答

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); 
     } 
    } 
} 
+0

我喜欢这个解决方案背后的想法,并感谢发现隐式运算符,所以+1。我曾尝试实施您的解决方案(因此延迟响应),但我已经陷入了最后的障碍!这一切都编译好了,但在运行时,我得到一个异常,说明'方法'MasterTrack op_Implicit(DbMasterTrack)'没有支持转换为SQL'。这对我来说看起来像是一个表演挡风玻璃,但如果你能帮上忙,我会很感激。 – Sheridan 2012-08-16 13:02:23

+0

你看到的原因是因为't => query(t)'被认为是'Expression >',然后LINQ2SQL试图将它转换为SQL。如果您将其转换为'Func ',它应该可以工作,但我必须再次警告您,您不会在数据库中进行过滤。如果你想过滤数据库,你需要从头开始使用'Expression >'并尝试Maarten方法。 – 2012-08-16 13:13:34

+0

Ahhhh。非常感谢您指出Func 在本地进行过滤,这当然会造成很大的差异。我将不得不使用通用的'Expression'类。非常感谢所有相同的,我相信我会再次使用'隐式'的东西。 – Sheridan 2012-08-16 13:30:03

2

几件事情

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

  2. 这是不可能的变化Expression<Func<T, bool>>

  3. 是可以复制/重新创建一个Expression<Func<T, bool>>,你用另一种类型的替代类型T的类型。

  4. Linq2Sql将不是在其表达式中接受接口类型。所以如果你考虑创建接口来'抽象'实际的类型,那就行不通了。

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

使用此代码,你可以这样做:

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); 
    } 
} 
+0

再一次,我喜欢这个解决方案,所以+1 ......这毕竟是对我的问题最接近的答案。首先实现了@Joao Angelo的解决方案,我将尽力使所有这些工作都正常进行,但如果失败了,我会回到这里。非常感谢。 – Sheridan 2012-08-16 13:05:14

+0

尝试过您的解决方案后,我不敢说它也无法正常工作。我得到了一个异常,因为你的Rewrite方法中没有'ExpressionType.Lambda'的'Case'。 – Sheridan 2012-08-16 13:25:52

+0

您是否将您的类型更改为Expression >?你的表达由什么组成? – Maarten 2012-08-16 13:30:29

相关问题