Linq to entities无法识别system.string get_Item方法

时间:2017-12-06 09:12:30

标签: c# sql entity-framework linq

我试图将orderByExpression传递给在实体框架中的dbContext对象上使用它的函数

> Dictionary<int, string> dict = new Dictionary<int, string>();
> Expression<Func<DbQuestion, string>> orderByExpression = r => dict[r.Id];

这会引发以下错误。

  

Exception = Exceptions.DBConnectionException:LINQ   to Entities无法识别方法&#39; System.String   get_Item(的Int32)&#39;方法,这个方法无法翻译成一个   商店表达。

据我所知,我无法访问Linq中的[]到Sql查询。你能否提出一个替代方案或指出我正确的方向?

编辑:

关于我想解决的问题的更多细节:

dict有15-20项,键保持不变,只有值动态变化

我试图根据r.Id的范围来排序问题,即如果floor(r.Id)== 14那么我返回&#34; a&#34;如果它在楼层之间(r.Id)== 15那么我返回&#34; b&#34;如果它在floor(r.id)== 13之间,我返回&#34; c&#34;(由dict中的值决定)这有助于排序行

这是实际的表达方式:

List<int> cqh;
List<int> iqh;
Expression<Func<DbQuestion, string>> orderByExpression = r => cqh.Contains(r.QuestionID)? dict[(int)Math.Floor(r.SearchKey1)] +"2"+Guid.NewGuid() :  
                    iqh.Contains(r.QuestionID)? dict[(int)Math.Floor(r.SearchKey1)] + "1"+Guid.NewGuid() :  
                    dict[(int)Math.Floor(r.SearchKey1)] + "0"+Guid.NewGuid();

谢谢,

1 个答案:

答案 0 :(得分:3)

我看到两个选项,与LINQ to Entities

保持一致
  1. 将数据放入单独的表中,保存,然后加入表。即使您需要拥有每个查询数据,此解决方案也可以应用。
  2. 示例:

    public class SortKey
    {
        [Key]
        public int SortKeyId { get; set; }
        public long SearchId { get; set; }
        public int EntityId { get; set; }
        public string SortId { get; set; }
    }
    
    using (var db = new Db())
    {
        Dictionary<int, string> dict = new Dictionary<int, string>();
        long searchId = DateTime.Now.Ticks; // Simplfied, either use a guid or a FK to another table
        db.Keys.AddRange(dict.Select(kv => new SortKey { SearchId = searchId, EntityId = kv.Key, SortId = kv.Value }));
        db.SaveChanges();
    
        var query = from e in db.Entity
                    join k in db.Keys.Where(k => k.SearchId == searchId) on (int)e.Id equals k.EntityId
                    orderby k.SortId
                    select e;
    }
    
    // Cleanup the sort key table 
    
    1. 动态构建条件。这可以使用表达式处理
    2. 来实现

      示例:

      Expression exp = Expression.Constant(""); //Default order key
      var p = Expression.Parameter(typeof(Entity));
      foreach (var kv in dict)
      {
          exp = Expression.Condition(
              Expression.Equal(
                  Expression.Convert(
                      Expression.MakeMemberAccess(p, p.Type.GetProperty("Id")), typeof(int)
                  ),
                  Expression.Constant(kv.Key)
              ),
              Expression.Constant(kv.Value),
              exp
          );
      }
      
      var orderByExp = Expression.Lambda<Func<Entity, string>>(exp, p);
      
      var query = db.Entity.OrderBy(orderByExp);
      

      您使用哪个选项取决于字典中的数据量。为OrderBy构建的条件对于大量数据可能效率非常低

      修改

      根据更改后的问题,您可以使用表达式访问者将dic [...]调用替换为字典中每个值的条件测试。这种方法的优点是可以轻松更改表达式,替换将以相同的方式工作

      班级:

      class DictionaryReplaceVisitor : ExpressionVisitor
      {
          protected override Expression VisitMethodCall(MethodCallExpression node)
          {
              if(node.Object != null && node.Object.Type == typeof(Dictionary<int, string>) && node.Method.Name == "get_Item")
              {
                  Expression exp = Expression.Constant(""); //Default order key
                  // Compile the tahrget of the index and execute it to get the value
                  // If you know there is a single dictionary you could replace this with a class property intead and set it from the Visit call site, 
                  // but this is the more general appraoch
                  var dict = Expression.Lambda<Func<Dictionary<int, string>>>(node.Object).Compile()();
                  foreach (var kv in dict)
                  {
                      exp = Expression.Condition(
                          Expression.Equal(
                              node.Arguments.Single(),
                              Expression.Constant(kv.Key)
                          ),
                          Expression.Constant(kv.Value),
                          exp
                      );
                  }
      
                  return exp;
              }
              return base.VisitMethodCall(node);
          }
      }
      

      用法:

      Expression<Func<Entity, string>> orderByExpression = r => cqh.Contains(r.QuestionID) ? dict[(int)Math.Floor(r.SearchKey1)] + "2" + Guid.NewGuid() :
              iqh.Contains(r.QuestionID) ? dict[(int)Math.Floor(r.SearchKey1)] + "1" + Guid.NewGuid() :
              dict[(int)Math.Floor(r.SearchKey1)] + "0" + Guid.NewGuid();
      var replace = (Expression<Func<Entity, string>>)new DictionaryReplaceVisitor().Visit(orderByExpression);
      
      var query = db.Entity.OrderBy(replace).ToString();
      

      生成的SQL不会很漂亮但它应该可以工作。

      解决方案3:

      如果数据量不是很大,您可以对查询执行ToListAsEnumerable并在内存中进行排序(在其中一个之后调用OrderBy以上方法)。在这种情况下,它实际上可能表现更好