我不确定这是否可行,因为我刚刚进入实体框架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>
。
思考/建议。谢谢!
已更新 - Ivan为看到这个的人提供了一个很好的更新答案。更容易出现以下答案的错误,以及相当光滑的IMO。 enter link description here
答案 0 :(得分:2)
实体框架6是愚蠢的。它只是尝试从方法中获取关联的[DbFunction]
属性,并使用DbExpression
私有类将该方法替换为System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.Translator
。因此,无法从外部代码注册自定义转换器。此外,.NET不提供动态地将属性附加到语言结构的功能。
因此,要解决此问题,您可以执行以下操作之一:
DbFunctions
类的方法替换方法(ReSharper SSR对此有用); ExpressionVisitor
(以及可能的IQueryProvider
),它将使用DbFunctions
类的方法替换方法调用。ExpressionVisitor
(以及可能的IQueryProvider
),它会将方法调用表达式转换为DbFunctionExpression
。答案 1 :(得分:1)
我们发现修补入站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);
}