我正在使用 Azure SQL Server,并且正在从 EF 6 迁移到 EF Core 7。
在 EF 6 中,
Guid.ToString()
方法生成带有小写 GUID 的选择查询,而现在 EF Core 7 正在生成大写。我在项目中使用了很多此类转换,因此有没有一种简单的方法可以覆盖 EF Core 7 中由 Guid.ToString()
或 Guid?.ToString()
生成的默认查询?
EF core Github 中已经存在问题。但提供的解决方案对于我的情况来说不够好。
有几件事要提:
不管你喜欢与否,EF Core(以及整个 Net Core 堆栈)没有向后兼容性要求。对于 EF Core,甚至在文档中明确说明,因此它们不会被迫提供与 EF 6 相同(甚至任何)的翻译。您可以继续使用 EF 6,也可以切换到 EF Core 并准备好进行重大更改在许多领域。
一般来说,依赖数据库进行字符串转换(格式化/解析)并不是一个好主意,因为每个数据库都有自己的愿景,没有真正的标准,而且在大多数情况下它与 C# 不同。因此,最好使用原始数据类型,并在需要时在客户端进行字符串转换(例如序列化或 UI 演示组件等)。在这方面,我认为我们不能将相关行为视为 EF Core bug/问题。
现在来说具体的问题
有没有一种简单的方法可以覆盖 EF Core 7 中由 Guid.ToString() 或 Guid?.ToString() 生成的默认查询?
幸运的是,EF Core 是围绕服务和 DI 构建的,因此比 EF 6 更具可定制性。这不容易,但可能。
对于这种特殊情况,有多种方法可以覆盖默认的 EF Core 翻译 - IMethodCallTranslatorPlugin 提供一个或多个自定义 IMethodCallTranslator。这些很容易实现,但插入 EF Core 管道却不那么容易(需要大量样板管道代码)。因此,我发现使用标准 ReplaceService 方法替换默认的 IMethodCallTranslatorProvider 服务(使用上述子服务)更容易。一个小问题是它是特定于数据库提供程序的,因此您必须继承相应的服务实现类(在本例中 - 对于 SqlServer 来说,它是一个名为
SqlServerMethodCallTranslatorProvider
的类)。其次,所有这些都是公开的(幸运的是),但被标记为“EF Core 内部 API 的一部分”,因此您必须抑制相关警告(并观察 EF Core 版本是否有重大更改)。
话虽如此,这是使用 SqlServer 数据库的 EF Core 7 的解决方案:
#nullable enable
using System.Reflection;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Query;
using Microsoft.EntityFrameworkCore.Query.SqlExpressions;
using Microsoft.EntityFrameworkCore.SqlServer.Query.Internal;
namespace Microsoft.EntityFrameworkCore
{
public static partial class CustomSqlServerDbContextOptionsExtensions
{
public static DbContextOptionsBuilder UseCustomSqlServerTranslation(
this DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.ReplaceService<IMethodCallTranslatorProvider, CustomSqlServerMethodCallTranslatorProvider>();
return optionsBuilder;
}
}
}
namespace Microsoft.EntityFrameworkCore.SqlServer.Query.Internal
{
#pragma warning disable EF1001 // Internal EF Core API usage.
public class CustomSqlServerMethodCallTranslatorProvider : SqlServerMethodCallTranslatorProvider
{
#nullable disable
private static readonly MethodInfo ToLowerMethodInfo = typeof(string).GetRuntimeMethod("ToLower", Type.EmptyTypes);
#nullable enable
public CustomSqlServerMethodCallTranslatorProvider(RelationalMethodCallTranslatorProviderDependencies dependencies)
: base(dependencies)
{
}
public override SqlExpression? Translate(IModel model, SqlExpression? instance, MethodInfo method, IReadOnlyList<SqlExpression> arguments, IDiagnosticsLogger<DbLoggerCategory.Query> logger)
{
var result = base.Translate(model, instance, method, arguments, logger);
if (instance != null && instance.Type == typeof(Guid) && method.Name == nameof(Guid.ToString) && arguments.Count == 0)
{
// result = result.ToLower()
result = base.Translate(model, result, ToLowerMethodInfo, arguments, logger);
}
return result;
}
}
#pragma warning restore EF1001 // Internal EF Core API usage.
}
将上述内容放入派生数据库上下文所在项目中您选择的代码文件中。然后在
OnConfiguring
覆盖中添加以下内容:
optionsBuilder.UseCustomSqlServerTranslation();
你就完成了 - 所有
Guid.ToString()
和 Guid?.ToString()
投影都将被翻译,就像你在末尾写了附加的 .ToLower()
一样。
请记住,如果您的目标是 SqlServer(即optionsBuilder.UseSqlServer(...)
),请
仅使用上述解决方法。