繁体   English   中英

实体框架6 - DbFunctions的扩展方法

[英]Entity Framework 6 - Extension Methods for DbFunctions

我不确定这是否可行,因为我刚刚进入实体框架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<DbFunctions>

思考/建议。 谢谢!

更新 - 伊万为看到这个的人提供了一个很好的更新答案。 更容易出现以下答案的错误,以及相当光滑的IMO。 在此输入链接描述

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

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

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

我们发现只修补入站Linq2Sql代码是最容易的。 我们还注意到DbFunctions.AddMinutes()只接受Int32,其中DateTime.AddMinutes()接受double。 这可能会破坏预期的行为,所以我们也补丁了。 使用一些正则表达式和一些简单的字符串操作,产生了这个修补代码。

这可能对每个人都不起作用,但如果您从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);
    }

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM