簡體   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