2011-08-01 49 views
7

我有一個名爲「CreateCriteriaExpression」的函數,它接受一個json字符串並從中創建一個linq表達式。用linq表達式的協變/逆變換

該方法由另一個稱爲「GetByCriteria」的方法調用,該方法調用「CreateCriteriaExpression」方法,然後針對實體框架上下文執行該表達式。

對於我所有的實體框架對象,「GetByCriteria」方法除了它的類型外都是相同的。所以我試圖將其轉換爲使用泛型而不是硬編碼類型。

當「GetByCriteria」方法到達必須調用「CreateCriteriaExpression」方法的地步時,我使用工廠類來確定要使用的適當類/方法。然後在「linq表達式」類中,創建並返回特定類型的linq表達式。

我遇到的問題是linq表達式必須爲特定類型創建,但返回值是泛型類型,它不會自動在兩者之間進行轉換,即使其中一個是父類(協方差)。

有什麼辦法可以使這項工作?

一些示例代碼:

的 「GetByCriteria」 的方法:

/// <summary> 
    /// Gets a <see cref="System.Collections.Generic.List"/> of <see cref="TEntity"/> 
    /// objects that match the passed JSON string. 
    /// </summary> 
    /// <param name="myCriteria">A list of JSON strings containing a key/value pair of "parameterNames" and "parameterValues".</param> 
    /// <param name="myMatchMethod">Defines which matching method to use when finding matches on the <paramref name="myCriteria"/>.</param> 
    /// <returns> 
    /// A <see cref="System.Collections.Generic.List"/> of <see cref="TEntity"/> 
    /// objects. 
    /// </returns> 
    /// <seealso cref="TEntity"/> 
    /// 
    /// <seealso cref="Common.MultipleCriteriaMatchMethod"/> 
    /// <remarks> 
    /// This method takes a <see cref="System.Collections.Generic.List"/> of JSON strings, and a <see cref="Common.MultipleCriteriaMatchMethod"/> and returns a 
    /// <see cref="System.Collections.Generic.List"/> of all matching 
    /// <see cref="TEntity"/> objects from the back-end database. The <paramref name="myMatchMethod"/> is used to determine how to match when multiple <paramref name="myCriteria"/> are passed. You can require that any results must match on ALL the passed JSON criteria, or on ANY of the passed criteria. This is essentially an "AND" versus and "OR" comparison. 
    /// </remarks> 
    [ContractVerification(true)] 
    public static List<TEntity> GetByCriteria<TContext, TEntity>(List<string> myCriteria, Common.MultipleCriteriaMatchMethod myMatchMethod) 
     where TContext : System.Data.Objects.ObjectContext, new() 
     where TEntity : System.Data.Objects.DataClasses.EntityObject 
    { 
     // Setup Contracts 
     Contract.Requires(myCriteria != null); 

     TContext db = new TContext(); 


     // Intialize return variable 
     List<TEntity> result = null; 

     // Initialize working variables 
     // Set the predicates to True by default (for "AND" matches) 
     var predicate = PredicateBuilder.True<TEntity>(); 
     var customPropertiesPredicate = PredicateBuilder.True<TEntity>(); 

     // Set the predicates to Falase by default (for "OR" matches) 
     if (myMatchMethod == Common.MultipleCriteriaMatchMethod.MatchOnAny) 
     { 
      predicate = PredicateBuilder.False<TEntity>(); 
      customPropertiesPredicate = PredicateBuilder.False<TEntity>(); 
     } 


     // Loop over each Criteria object in the passed list of criteria 
     foreach (string x in myCriteria) 
     { 
      // Set the Criteria to local scope (sometimes there are scope problems with LINQ) 
      string item = x; 
      if (item != null) 
      { 
       JsonLinqParser parser = JsonLinqParserFactory.GetParser(typeof(TEntity)); 

       // If the designated MultipleCriteriaMatchMethod is "MatchOnAll" then use "AND" statements 
       if (myMatchMethod == Common.MultipleCriteriaMatchMethod.MatchOnAll) 
       { 
        predicate = predicate.Expand().And<TEntity>(parser.CreateCriteriaExpression<TEntity>(item).Expand()); 
        customPropertiesPredicate = customPropertiesPredicate.Expand().And<TEntity>(parser.CreateCriteriaExpressionForCustomProperties<TEntity>(item).Expand()); 
       } 
       // If the designated MultipleCriteriaMatchMethod is "MatchOnAny" then use "OR" statements 
       else if (myMatchMethod == Common.MultipleCriteriaMatchMethod.MatchOnAny) 
       { 
        predicate = predicate.Expand().Or<TEntity>(parser.CreateCriteriaExpression<TEntity>(item).Expand()); 
        customPropertiesPredicate = customPropertiesPredicate.Expand().Or<TEntity>(parser.CreateCriteriaExpressionForCustomProperties<TEntity>(item).Expand()); 
       } 
      } 
     } 

     // Set a temporary var to hold the results 
     List<TEntity> qry = null; 

     // Set some Contract Assumptions to waive Static Contract warnings on build 
     Contract.Assume(predicate != null); 
     Contract.Assume(customPropertiesPredicate != null); 


     // Run the query against the backend database 
     qry = db.CreateObjectSet<TEntity>().AsExpandable<TEntity>().Where<TEntity>(predicate).ToList<TEntity>(); 
     //qry = db.CreateObjectSet<TEntity>().Where(predicate).ToList<TEntity>(); 
     // Run the query for custom properties against the resultset obtained from the database 
     qry = qry.Where<TEntity>(customPropertiesPredicate.Compile()).ToList<TEntity>(); 

     // Verify that there are results 
     if (qry != null && qry.Count != 0) 
     { 
      result = qry; 
     } 

     // Return the results 
     return result; 
    } 

的JsonLinqParser類(不建立):

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using LinqKit; 
using Newtonsoft.Json.Linq; 

namespace DAL 
{ 
    internal class JsonLinqParser_Paser : JsonLinqParser 
    { 
     internal override System.Linq.Expressions.Expression<Func<TEntity, bool>> CreateCriteriaExpression<TEntity>(string myCriteria) 
     { 
      var predicate = PredicateBuilder.True<BestAvailableFIP>(); 

      JObject o = JObject.Parse(myCriteria); 

      // bmp 
      decimal _bmp; 
      if (o["bmp"] != null && decimal.TryParse((string)o["bmp"], out _bmp)) 
      { 
       predicate = predicate.And<BestAvailableFIP>(x => x.bmp == _bmp); 
      } 
      // COUNTY 
      if (!string.IsNullOrWhiteSpace((string)o["COUNTY"])) 
      { 
       string _myStringValue = (string)o["COUNTY"]; 
       predicate = predicate.And<BestAvailableFIP>(x => x.COUNTY.Contains(_myStringValue)); 
      } 
      // emp 
      decimal _emp; 
      if (o["emp"] != null && decimal.TryParse((string)o["emp"], out _emp)) 
      { 
       predicate = predicate.And<BestAvailableFIP>(x => x.emp == _emp); 
      } 
      // FIPSCO_STR 
      if (!string.IsNullOrWhiteSpace((string)o["FIPSCO_STR"])) 
      { 
       string _myStringValue = (string)o["FIPSCO_STR"]; 
       predicate = predicate.And<BestAvailableFIP>(x => x.FIPSCO_STR.Contains(_myStringValue)); 
      } 
      // FIPSCODE 
      double _FIPSCODE; 
      if (o["FIPSCODE"] != null && double.TryParse((string)o["FIPSCODE"], out _FIPSCODE)) 
      { 
       predicate = predicate.And<BestAvailableFIP>(x => x.FIPSCODE == _FIPSCODE); 
      } 
      // FROMDESC 
      if (!string.IsNullOrWhiteSpace((string)o["FROMDESC"])) 
      { 
       string _myStringValue = (string)o["FROMDESC"]; 
       predicate = predicate.And<BestAvailableFIP>(x => x.FROMDESC.Contains(_myStringValue)); 
      } 
      // LANEMI 
      decimal _LANEMI; 
      if (o["LANEMI"] != null && decimal.TryParse((string)o["LANEMI"], out _LANEMI)) 
      { 
       predicate = predicate.And<BestAvailableFIP>(x => x.LANEMI == _LANEMI); 
      } 
      // MPO_ABBV 
      if (!string.IsNullOrWhiteSpace((string)o["MPO_ABBV"])) 
      { 
       string _myStringValue = (string)o["MPO_ABBV"]; 
       predicate = predicate.And<BestAvailableFIP>(x => x.MPO_ABBV.Contains(_myStringValue)); 
      } 
      // owner 
      if (!string.IsNullOrWhiteSpace((string)o["owner"])) 
      { 
       string _myStringValue = (string)o["owner"]; 
       predicate = predicate.And<BestAvailableFIP>(x => x.owner.Contains(_myStringValue)); 
      } 
      // PASER 
      decimal _PASER; 
      if (o["PASER"] != null && decimal.TryParse((string)o["PASER"], out _PASER)) 
      { 
       predicate = predicate.And<BestAvailableFIP>(x => x.PASER == _PASER); 
      } 
      // PASER_GROUP 
      if (!string.IsNullOrWhiteSpace((string)o["PASER_GROUP"])) 
      { 
       string _myStringValue = (string)o["PASER_GROUP"]; 
       predicate = predicate.And<BestAvailableFIP>(x => x.PASER_GROUP.Contains(_myStringValue)); 
      } 
      // pr 
      decimal _pr; 
      if (o["pr"] != null && decimal.TryParse((string)o["pr"], out _pr)) 
      { 
       predicate = predicate.And<BestAvailableFIP>(x => x.pr == _pr); 
      } 
      // RDNAME 
      if (!string.IsNullOrWhiteSpace((string)o["RDNAME"])) 
      { 
       string _myStringValue = (string)o["RDNAME"]; 
       predicate = predicate.And<BestAvailableFIP>(x => x.RDNAME.Contains(_myStringValue)); 
      } 
      // SPDR_ABBV 
      if (!string.IsNullOrWhiteSpace((string)o["SPDR_ABBV"])) 
      { 
       string _myStringValue = (string)o["SPDR_ABBV"]; 
       predicate = predicate.And<BestAvailableFIP>(x => x.SPDR_ABBV.Contains(_myStringValue)); 
      } 
      // TODESC 
      if (!string.IsNullOrWhiteSpace((string)o["TODESC"])) 
      { 
       string _myStringValue = (string)o["TODESC"]; 
       predicate = predicate.And<BestAvailableFIP>(x => x.TODESC.Contains(_myStringValue)); 
      } 
      // TYPE 
      if (!string.IsNullOrWhiteSpace((string)o["TYPE"])) 
      { 
       string _myStringValue = (string)o["TYPE"]; 
       predicate = predicate.And<BestAvailableFIP>(x => x.TYPE.Contains(_myStringValue)); 
      } 

      return predicate; 
     } 

     internal override System.Linq.Expressions.Expression<Func<TEntity, bool>> CreateCriteriaExpressionForCustomProperties<TEntity>(string myCriteria) 
     { 
      var predicate = PredicateBuilder.True<TEntity>(); 

      return predicate; 
     } 
    } 
} 

的JsonLinqParser基類:

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Linq.Expressions; 

namespace DAL 
{ 
    abstract class JsonLinqParser 
    { 
     abstract internal Expression<Func<TEntity, bool>> CreateCriteriaExpression<TEntity>(string myCriteria) 
      where TEntity : System.Data.Objects.DataClasses.EntityObject; 
     abstract internal Expression<Func<TEntity, bool>> CreateCriteriaExpressionForCustomProperties<TEntity>(string myCriteria) 
      where TEntity : System.Data.Objects.DataClasses.EntityObject; 
    } 
} 

工廠類:

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 

namespace DAL 
{ 
    internal static class JsonLinqParserFactory 
    { 
     internal static JsonLinqParser GetParser(Type type) 
     { 
      switch (type.Name) 
      { 
       case "BestAvailableFIP": 
        return new JsonLinqParser_Paser(); 
       default: 
        //if we reach this point then we failed to find a matching type. Throw 
        //an exception. 
        throw new Exception("Failed to find a matching JsonLinqParser in JsonLinqParserFactory.GetParser() - Unknown Type: " + type.Name); 
      } 
     } 
    } 
} 

回答

4

的問題是,JsonLinqParser_Paser的簽名是通用的,類型無關,和你的實現是其混凝土BestAvailableFIP型專業化。這不是一個協變問題,它只是鍵入不兼容性(在編譯器級別)。

解決方法是使JsonLinqParser爲通用類型(不具有通用方法) - 或者甚至是接口,然後使JsonLinqParser_Paser執行JsonLinqParser<BestAvailableFIP>。然後我們會將所有內容匹配起來

IJsonLinqParser接口:

interface IJsonLinqParser<TEntity> 
    where TEntity : System.Data.Objects.DataClasses.EntityObject 
{ 
    Expression<Func<TEntity, bool>> CreateCriteriaExpression(string myCriteria); 
    Expression<Func<TEntity, bool>> CreateCriteriaExpressionForCustomProperties(string myCriteria) 
} 

實施 - 簽名JsonLinqParser_Paser

internal class JsonLinqParser_Paser : IJsonLinqParser<BestAvailableFIP> 
{ 
    public Expression<Func<BestAvailableFIP, bool>> CreateCriteriaExpression(string myCriteria) 
    { 
     // implementation as yours 
    } 

    public Expression<Func<BestAvailableFIP, bool>> CreateCriteriaExpressionForCustomProperties(string myCriteria) 
    { 
     // implementation as yours 
    } 
} 

工廠需要返回IJsonLinqParser<TEntity>,究竟是不是一個問題,因爲我們知道TEntity有:

internal static class JsonLinqParserFactory 
{ 
    internal static IJsonLinqParser<TEntity> GetParser<TEntity>() 
     where TEntity : System.Data.Objects.DataClasses.EntityObject 
    { 
     switch (typeof(TEntity).Name) 
     { 
      case "BestAvailableFIP": 
       return (IJsonLinqParser<TEntity>) new JsonLinqParser_Paser(); 
      default: 
       //if we reach this point then we failed to find a matching type. Throw 
       //an exception. 
       throw new Exception("Failed to find a matching JsonLinqParser in JsonLinqParserFactory.GetParser() - Unknown Type: " + typeof(TEntity).Name); 
     } 
    } 
} 

最後在GetByCriteria你可以有:

IJsonLinqParser<TEntity> parser = JsonLinqParserFactory.GetParser<TEntity>(); 

無需<TEntity>解析器方法現在調用,如解析器已經TEntity特異性。

希望這會有所幫助。

順便說一句,您的工廠基礎設施可以很容易地被IoC工具取代。

+0

謝謝。得到這個工作!我會研究IoC。之前我曾經閱讀過有關這方面的內容,但是讓我的頭部纏繞起來比我有時間多一點。可能值得重遊。 –