[英]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.