【问题标题】:Entity Framework 6 - Extension Methods for DbFunctionsEntity Framework 6 - DbFunctions 的扩展方法
【发布时间】:2015-07-23 21:49:36
【问题描述】:

我不确定这是否可行,因为我刚刚深入研究 Entity Framework 6。我们正在从 Linq2Sql 迁移,但在我们的引擎中有许多基于用户的查询。我们根据用户需求动态编译一些代码,并且必须保持向后兼容性。

从旧版 Linq2Sql 传入的一些动态查询的示例可能如下所示(精简,这只是一个示例):

from item in context.SomeTableOrView 
  let sub = from sub1 in context.SomeTableOrView where 
  sub1.DataTimeStamp > item.DataTimeStamp.AddMinutes(60) &&
  sub1.DataTimeStamp < item.DataTimeStamp.AddMinutes(300) 
select posSub1 where 
sub.Take(1).Any()
select item

当然,Entity Framework 没有任何类型的.AddMinutes(x) 支持,您必须使用新的 DbFunctions 静态方法。我们不能破坏代码,所以我们必须对其进行改造。想到的第一个解决方案就是替换任何具有.AddMinutes(x).AddSeconds(x) 或我们在DateTime 周围所做的任何内容的文本,然后用新函数替换并完成它。这都是预编译的,所以从技术上讲是可行的。我只是很讨厌正则表达式。如果有人知道我会怎么做,我很乐意接受这个答案。

但我想了解 EntityFramework 在扩展方法方面的工作原理。由于DateTime.AddMinutes(x) 的返回返回了一个新的DateTime,我是否可以创建一个扩展方法来返回一个表达式,该表达式相当于DbFunctions.AddMinutes(time, increment) 或类似的创意? DbFunctions 是静态的,所以我不能返回 Func&lt;DbFunctions&gt;

想法/建议。谢谢!

更新 - Ivan 为任何看到这个的人提供了一个很好的更新答案。下面的答案更容易出错,而且 IMO 也很巧妙。 enter link description here

【问题讨论】:

    标签: c# entity-framework entity-framework-6


    【解决方案1】:

    Entity Framework 6 在这种情况下是愚蠢的。它只是尝试从方法中获取关联的[DbFunction] 属性,并使用System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.Translator private 类将方法替换为DbExpression。因此,无法从外部代码注册自定义翻译器。此外,.NET 不提供将属性动态附加到语言结构的能力。

    因此,要解决此问题,您可以执行以下操作之一:

    • 用对源代码中DbFunctions类方法的调用替换方法(ReSharper SSR对此有好处);
    • 实现ExpressionVisitor(可能还有IQueryProvider),它将用DbFunctions类的方法替换方法调用。
    • 实现ExpressionVisitor(可能还有IQueryProvider),它将方法调用表达式转换为DbFunctionExpression

    【讨论】:

      【解决方案2】:

      我们发现只修补入站 Linq2Sql 代码是最简单的。我们还注意到 DbFunctions.AddMinutes() 只接受 Int32,而 DateTime.AddMinutes() 接受双精度。这可能会破坏预期的行为,因此我们也对其进行了修补。使用一些正则表达式和一些简单的字符串操作,得到了这个补丁代码。

      这可能不适用于所有人,但如果您从 Linq2Sql 出发并且保存了遵循 Linq2Sql 的查询并且需要修补 DateTime 表达式...这将适用于 AddMintues、AddDays、AddHours、AddMilliseconds

          private static string Linq2SqlConvertToEntityFramework(string originalQuery)
          {
              // Finds expressions with .Add____)
              const string dateTimeAdditationPattern = @"(@?[a-z_A-Z]\w*(?:\.@?[a-z_A-Z]\w*)*).Add\s*.+?(?=\))\)";
              // Finds all the matches
              var matchces = Regex.Matches(originalQuery, dateTimeAdditationPattern);
      
              // Enumerates all the matches, and creates a patch
              foreach (Match m in matchces)
              {
                  var inputValue = m.Value;
      
                  string valuePassed = inputValue.Between("(", ")").Trim();
                  string typeOfAddition = inputValue.Between(".Add", "(").Trim();
                  string dateTimeExpression = inputValue.Before(".Add").Trim();
      
                  // because DateTime.AddMinutes()  (or any other AddXXX(Y) accepts a double, and 
                  // DbFunctions only accepts an int,
                  // We must move this to milliseconds so we dont lose any expected behavior
                  // The input value could be an int or a input variable (such as a sub query)
                  var mutipler = 1;
                  switch (typeOfAddition)
                  {
                      case "Seconds":
                          mutipler = 1000;
                          break;
                      case "Minutes":
                          mutipler = 1000*60;
                          break;
                      case "Hours":
                          mutipler = 1000 * 60 * 60;
                          break;
                      case "Days":
                          mutipler = 1000 * 60 * 60 * 24;
                          break;
                  }
      
                  // new expression to work with Entity Framework
                  var replacementString = string.Format("DbFunctions.AddMilliseconds({0},(int)({1} * {2}))", dateTimeExpression, valuePassed, mutipler);
      
                  // Simple string replace for the input string
                  originalQuery = originalQuery.Replace(inputValue, replacementString);
              }
      
              return originalQuery;
          }
      
          /// <summary>
          /// Get string value between [first] a and [last] b.
          /// Credit Source: http://www.dotnetperls.com/between-before-after
          /// </summary>
          public static string Between(this string value, string a, string b)
          {
              int posA = value.IndexOf(a, StringComparison.InvariantCulture);
              int posB = value.LastIndexOf(b, StringComparison.InvariantCulture);
              if (posA == -1)
              {
                  return "";
              }
              if (posB == -1)
              {
                  return "";
              }
              int adjustedPosA = posA + a.Length;
              if (adjustedPosA >= posB)
              {
                  return "";
              }
              return value.Substring(adjustedPosA, posB - adjustedPosA);
          }
      
          /// <summary>
          /// Get string value after [first] a.
          /// Credit Source: http://www.dotnetperls.com/between-before-after
          /// </summary>
          public static string Before(this string value, string a)
          {
              int posA = value.IndexOf(a, StringComparison.InvariantCulture);
              if (posA == -1)
              {
                  return "";
              }
              return value.Substring(0, posA);
          }
      
          /// <summary>
          /// Get string value after [last] a.
          /// Credit Source: http://www.dotnetperls.com/between-before-after
          /// </summary>
          public static string After(this string value, string a)
          {
              int posA = value.LastIndexOf(a, StringComparison.InvariantCulture);
              if (posA == -1)
              {
                  return "";
              }
              int adjustedPosA = posA + a.Length;
              if (adjustedPosA >= value.Length)
              {
                  return "";
              }
              return value.Substring(adjustedPosA);
          }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2014-11-07
        • 1970-01-01
        • 2019-06-27
        • 1970-01-01
        • 1970-01-01
        • 2014-06-12
        • 2014-01-30
        相关资源
        最近更新 更多