2012-08-16 92 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

相關問題