linq EF6到EFCore为Guid.ToString()生成不同的大小写

h6my8fg2  于 2023-09-28  发布在  其他
关注(0)|答案(1)|浏览(218)

我正在使用Azure SQL Server,并且正在从EF 6迁移到EF Core 7。
在EF 6中,Guid.ToString()方法生成了选择查询,并在其中生成了查询,而现在,EF Core 7正在生成查询。我在我的项目中使用了很多这样的转换,那么有没有一种简单的方法来覆盖EF Core 7中Guid.ToString()Guid?.ToString()生成的默认查询?
在EF Core Github中已经有一个问题。但是提供的解决方案对我的情况来说还不够好。
https://github.com/dotnet/efcore/issues/24419

jpfvwuh4

jpfvwuh41#

有几件事要提一下:

  • 不管你喜欢与否,EF Core(以及整个Net Core堆栈)没有向后兼容性要求。对于EF Core,它甚至在文档中明确说明,因此他们不会被迫提供与EF 6相同(甚至任何)的翻译。您可以继续使用EF 6,也可以切换到EF Core,为许多领域的突破性变化做好准备。
  • 一般来说,依赖数据库进行字符串转换(格式化/解析)不是一个好主意,因为每个数据库都有自己的愿景,没有真实的的标准,而且在大多数情况下,它与C#不同。因此,最好使用原始数据类型,并在需要的地方(如序列化或UI表示组件等)进行客户端到/从字符串转换。在这方面,我不认为我们可以把问题中的行为视为EF核心错误/问题。

现在谈具体问题
那么有没有一种简单的方法来覆盖Guid.ToString()或Guid生成的默认查询呢?EF Core 7中的.ToString()?
幸运的是,EF Core是围绕服务和DI构建的,因此比EF 6更具可定制性。这并不容易,但有可能。
对于这种特殊情况,有几种方法可以覆盖默认的EF核心转换-IMethodCallTranslatorPlugin提供一个或多个自定义IMethodCallTranslator。这些都很容易实现,但不太容易插入EF Core管道(需要大量的样板管道代码)。因此,我发现使用标准的ReplaceService方法替换默认的IMethodCallTranslatorProvider服务(它使用前面提到的子服务)更容易。一个小问题是,它是特定于数据库提供程序的,因此您必须继承相应的服务实现类(在本例中-对于SqlServer,它是一个名为SqlServerMethodCallTranslatorProvider的类)。第二,所有这些都是公开的(幸运的是),但被标记为“EF Core内部API的一部分”,所以你必须抑制相关的警告(并观察EF Core版本的破坏性更改)。
下面是EF Core 7与SqlServer数据库的解决方案:

#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 override中添加以下内容:

optionsBuilder.UseCustomSqlServerTranslation();

然后你就完成了-所有的Guid.ToString()Guid?.ToString()投影都将被转换,就像你在最后写了附加的.ToLower()一样。
请记住,如果您的目标是SqlServer(即,optionsBuilder.UseSqlServer(...))。

相关问题