2011-08-01 34 views
7

Tôi có một chức năng gọi là "CreateCriteriaExpression" có một chuỗi json và tạo ra một biểu thức LINQ từ nó.Hiệp phương sai/Contravariance với một biểu thức linq

Phương pháp này được gọi bằng một phương thức khác gọi là "GetByCriteria", gọi phương thức "CreateCriteriaExpression" và sau đó thực hiện biểu thức đó trong bối cảnh khung thực thể.

Đối với tất cả các đối tượng khung thực thể của tôi, phương thức "GetByCriteria" giống hệt nhau ngoại trừ loại của nó. Vì vậy, tôi đang cố gắng chuyển đổi nó để sử dụng Generics thay vì các loại mã hóa cứng.

Khi phương thức "GetByCriteria" đến điểm mà nó phải gọi phương thức "CreateCriteriaExpression", tôi đang sử dụng phương thức này để xác định lớp/phương thức thích hợp để sử dụng. Sau đó, trong lớp "linq expression", biểu thức linq cho một kiểu cụ thể được tạo và trả về.

Vấn đề tôi đang gặp phải là biểu thức LINQ phải được tạo cho một loại cụ thể, nhưng giá trị trả về là loại chung và nó sẽ không tự động chuyển đổi giữa hai loại, mặc dù nó là cha mẹ của người khác (hiệp phương sai).

Có cách nào tôi có thể thực hiện công việc này không?

Một số mã ví dụ:

Các "GetByCriteria" phương pháp:

/// <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; 
    } 

Lớp JsonLinqParser (không xây dựng):

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; 
     } 
    } 
} 

Các JsonLinqParser lớp cơ sở:

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; 
    } 
} 

Lớp nhà máy:

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

Trả lời

4

Vấn đề là chữ ký JsonLinqParser_Paser 's mang tính tổng quát, loại-agnostic, và thực hiện của bạn là chuyên môn của mình cho bê tông BestAvailableFIP loại. Đây không phải là một vấn đề hiệp phương sai, nó chỉ là loại không tương thích (ở trình biên dịch).

Giải pháp là làm cho JsonLinqParser trở thành loại chung (không có phương pháp chung) - hoặc thậm chí là giao diện, sau đó thực hiện JsonLinqParser_Paser để triển khai JsonLinqParser<BestAvailableFIP>. Sau đó chúng tôi sẽ có mọi thứ phù hợp.

Giao diện 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) 
} 

Việc thực hiện - chữ ký cho 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 
    } 
} 

Nhà máy cần phải trả lại IJsonLinqParser<TEntity>, những gì không phải là một vấn đề như chúng ta biết TEntity có:

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

Và cuối cùng trong GetByCriteria bạn có thể có:

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

Không cần <TEntity> trong phân tích cú pháp phương pháp gọi bây giờ, khi phân tích cú pháp đã là TEntity cụ thể.

Hy vọng điều này sẽ hữu ích.

Nhân tiện, cơ sở hạ tầng nhà máy của bạn có thể dễ dàng được thay thế bằng công cụ tốt IoC.

+0

Cảm ơn. Có điều này để làm việc! Tôi sẽ nhìn vào IoC. Tôi đã đọc về nó trước đây, nhưng nhận được đầu của tôi quấn quanh nó là một chút nhiều hơn tôi đã có thời gian cho. Có thể đáng xem xét lại. –

Các vấn đề liên quan