![](/img/trans.png)
[英]How to use AddDbContextPool if all configuration in OnConfiguring method of DbContext
[英]How to insert custom codes in dbContext OnConfiguring generation?
我尝试遵循这个答案有没有办法将 mysql json 脚手架成自定义类型? 使自定义 json 类型转换,它工作完美!
唯一困扰我的是我应该修改Context
代码手册,插入builder => builder.UseNewtonsoftJson()
。
我想知道它是否可以在生成过程中,这将是一个救生员。
我受到上面提到的答案的启发,并尝试使其发挥作用。
我想要的是
public partial class spckContext : DbContext
{
...
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (!optionsBuilder.IsConfigured)
{
#warning To protect potentially sensitive information in your connection string, you should move it out of source code. You can avoid scaffolding the connection string by using the Name= syntax to read it from configuration - see https://go.microsoft.com/fwlink/?linkid=2131148. For more guidance on storing connection strings, see http://go.microsoft.com/fwlink/?LinkId=723263.
optionsBuilder
.UseMySql("server=localhost;port=3306;database=spck;user=root;password=;treattinyasboolean=true", Microsoft.EntityFrameworkCore.ServerVersion.Parse("8.0.29-mysql"), builder => builder .UseNewtonsoftJson())
.EnableSensitiveDataLogging()
.LogTo(Log, LogFilter, DbContextLoggerOptions.DefaultWithLocalTime); // <= stucked here, how to pass method as parameter?
}
}
...
}
我将这些添加到我的项目中:
using System.Drawing;
using Microsoft.Extensions.Logging;
using Console = Colorful.Console;
public partial class spckContext
{
public static void Log(string content)
{
Console.WriteLineFormatted(content, Color.Aqua);
}
public static bool LogFilter(Microsoft.Extensions.Logging.EventId id, LogLevel level)
{
switch (level)
{
case LogLevel.Trace:
case LogLevel.Debug:
case LogLevel.Warning:
case LogLevel.None:
return false;
case LogLevel.Error:
case LogLevel.Critical:
case LogLevel.Information:
return true;
default:
return false;
}
}
}
public class MyDesignTimeServices : IDesignTimeServices
{
public void ConfigureDesignTimeServices(IServiceCollection services)
{
...
//Type Mapping
services.AddSingleton<IRelationalTypeMappingSource, CustomTypeMappingSource>(); // <= add this line
//Option Generator
services.AddSingleton<IProviderConfigurationCodeGenerator, ProviderConfigurationCodeGenerator>(); // <= and this line
...
}
}
using System.Reflection;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Design;
using Microsoft.EntityFrameworkCore.Design.Internal;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Scaffolding;
using Microsoft.Extensions.Logging;
using Pomelo.EntityFrameworkCore.MySql.Infrastructure.Internal;
using Pomelo.EntityFrameworkCore.MySql.Scaffolding.Internal;
using Pomelo.EntityFrameworkCore.MySql.Storage.Internal;
public class ProviderConfigurationCodeGenerator : MySqlCodeGenerator
{
private static readonly MethodInfo _enableSensitiveDataLoggingMethodInfo = typeof(DbContextOptionsBuilder).GetRequiredRuntimeMethod(
nameof(DbContextOptionsBuilder.EnableSensitiveDataLogging),
typeof(bool));
private static readonly MethodInfo _useNewtonJsonMethodInfo = typeof(MySqlJsonNewtonsoftDbContextOptionsBuilderExtensions).GetRequiredRuntimeMethod(
nameof(MySqlJsonNewtonsoftDbContextOptionsBuilderExtensions.UseNewtonsoftJson),
typeof(MySqlDbContextOptionsBuilder),
typeof(MySqlCommonJsonChangeTrackingOptions));
private static readonly MethodInfo _logToMethodInfo = typeof(DbContextOptionsBuilder).GetRequiredRuntimeMethod(
nameof(DbContextOptionsBuilder.LogTo),
typeof(Action<string>),
typeof(Func<EventId, LogLevel, bool>),
typeof(DbContextLoggerOptions?));
private static readonly MethodInfo _logMethodInfo = typeof(spckContext).GetRequiredRuntimeMethod(
nameof(spckContext.Log),
typeof(string));
private static readonly MethodInfo _logFilterMethodInfo = typeof(spckContext).GetRequiredRuntimeMethod(
nameof(spckContext.LogFilter),
typeof(EventId),
typeof(LogLevel));
private readonly ProviderCodeGeneratorDependencies _dependencies;
private readonly IMySqlOptions _options;
public ProviderConfigurationCodeGenerator(ProviderCodeGeneratorDependencies dependencies, IMySqlOptions options) : base(dependencies, options)
{
_dependencies = dependencies;
_options = options;
}
public override MethodCallCodeFragment GenerateUseProvider(string connectionString, MethodCallCodeFragment? providerOptions)
{
if (providerOptions == null)
{
providerOptions = new MethodCallCodeFragment(_useNewtonJsonMethodInfo);
}
else
{
providerOptions = providerOptions.Chain(new MethodCallCodeFragment(_useNewtonJsonMethodInfo));
}
var fragment = base.GenerateUseProvider(connectionString, providerOptions); //works
fragment = fragment.Chain(_enableSensitiveDataLoggingMethodInfo); //works
fragment = fragment.Chain(_logToMethodInfo,
new NestedClosureCodeFragment("str", new MethodCallCodeFragment(_logMethodInfo)), // <= try and failed! it convert into `str => str.Log()`
new MethodCall(_logFilterMethodInfo), // <= try and failed! error reported
DbContextLoggerOptions.DefaultWithLocalTime);
return fragment;
}
}
public static class TypeExtensions
{
public static MethodInfo GetRequiredRuntimeMethod(this Type type, string name, params Type[] parameters)
=> type.GetTypeInfo().GetRuntimeMethod(name, parameters)
?? throw new InvalidOperationException($"Could not find method '{name}' on type '{type}'");
}
using Microsoft.EntityFrameworkCore.Storage;
using Pomelo.EntityFrameworkCore.MySql.Infrastructure.Internal;
using Pomelo.EntityFrameworkCore.MySql.Storage.Internal;
public class CustomTypeMappingSource : MySqlTypeMappingSource
{
public CustomTypeMappingSource(TypeMappingSourceDependencies dependencies, RelationalTypeMappingSourceDependencies relationalDependencies, IMySqlOptions options) : base(dependencies, relationalDependencies, options)
{
}
protected override RelationalTypeMapping FindMapping(in RelationalTypeMappingInfo mappingInfo)
{
if (mappingInfo.ClrType == typeof(MethodCall))
{
return new MethodCallTypeMapping();
}
return base.FindMapping(mappingInfo);
}
}
using System.Linq.Expressions;
using System.Reflection;
using Microsoft.EntityFrameworkCore.Storage;
public class MethodCall
{
public MethodInfo Method;
public MethodCall(MethodInfo info)
{
Method = info;
}
}
public class MethodCallTypeMapping : RelationalTypeMapping
{
private const string DummyStoreType = "clrOnly";
public MethodCallTypeMapping()
: base(new RelationalTypeMappingParameters(new CoreTypeMappingParameters(typeof(MethodCall)), DummyStoreType))
{
}
protected MethodCallTypeMapping(RelationalTypeMappingParameters parameters)
: base(parameters)
{
}
protected override RelationalTypeMapping Clone(RelationalTypeMappingParameters parameters)
=> new MethodCallTypeMapping(parameters);
public override string GenerateSqlLiteral(object value)
=> throw new InvalidOperationException("This type mapping exists for code generation only.");
public override Expression GenerateCodeLiteral(object value)
{
return value is MethodCall methodCall
? Expression.Call(methodCall.Method) // <= not working, how to fix this?
: null;
}
}
所以我的问题是如何使用方法参数制作MethodCallCodeFragment
? 我尝试了谷歌,但找不到任何有价值的东西。 并且 MSDN 没有此功能的示例代码。
注入.UseNewtonsoftJson()
和.EnableSensitiveDataLogging()
调用可以简单地通过为设计时服务提供您自己的IProviderCodeGeneratorPlugin
实现来完成:
public class MyDesignTimeServices : IDesignTimeServices
{
public void ConfigureDesignTimeServices(IServiceCollection services)
{
services.AddSingleton<IProviderCodeGeneratorPlugin, CustomProviderCodeGeneratorPlugin>();
services.AddEntityFrameworkMySqlJsonNewtonsoft();
}
}
public class CustomProviderCodeGeneratorPlugin : IProviderCodeGeneratorPlugin
{
private static readonly MethodInfo EnableSensitiveDataLoggingMethodInfo = typeof(DbContextOptionsBuilder).GetRequiredRuntimeMethod(
nameof(DbContextOptionsBuilder.EnableSensitiveDataLogging),
typeof(bool));
private static readonly MethodInfo UseNewtonJsonMethodInfo = typeof(MySqlJsonNewtonsoftDbContextOptionsBuilderExtensions).GetRequiredRuntimeMethod(
nameof(MySqlJsonNewtonsoftDbContextOptionsBuilderExtensions.UseNewtonsoftJson),
typeof(MySqlDbContextOptionsBuilder),
typeof(MySqlCommonJsonChangeTrackingOptions));
public MethodCallCodeFragment GenerateProviderOptions()
=> new MethodCallCodeFragment(UseNewtonJsonMethodInfo);
public MethodCallCodeFragment GenerateContextOptions()
=> new MethodCallCodeFragment(EnableSensitiveDataLoggingMethodInfo);
}
实现复杂的.LogTo(Log, LogFilter, DbContextLoggerOptions.DefaultWithLocalTime)
调用并不那么简单,因为 EF Core 用于将代码生成表达式树转换为 C# 代码的转换逻辑充其量只是非常基本的。
实现虚拟类型映射以返回复杂表达式最终将不起作用,因为 EF Core 将无法翻译 lambda 表达式content => LogTo(content)
和(id, level) => LogFilter(id, level)
. 您可以尝试欺骗它,但最简单的解决方案是绕过整个表达式翻译机制。
对于 output 任何字符串作为 C# 代码,只需在您自己的实现中覆盖ICSharpHelper.UnknownLiteral(object value)
。
这是一个完整的工作示例:
using System;
using System.Diagnostics;
using System.Reflection;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Design;
using Microsoft.EntityFrameworkCore.Design.Internal;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Scaffolding;
using Microsoft.EntityFrameworkCore.Storage;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
namespace IssueConsoleTemplate;
public class MyDesignTimeServices : IDesignTimeServices
{
public void ConfigureDesignTimeServices(IServiceCollection services)
{
services.AddSingleton<IProviderCodeGeneratorPlugin, CustomProviderCodeGeneratorPlugin>();
services.AddSingleton<ICSharpHelper, CustomCSharpHelper>();
services.AddEntityFrameworkMySqlJsonNewtonsoft();
}
}
public static class TypeExtensions
{
public static MethodInfo GetRequiredRuntimeMethod(this Type type, string name, params Type[] parameters)
=> type.GetTypeInfo().GetRuntimeMethod(name, parameters)
?? throw new InvalidOperationException($"Could not find method '{name}' on type '{type}'");
}
public class CustomProviderCodeGeneratorPlugin : IProviderCodeGeneratorPlugin
{
private static readonly MethodInfo EnableSensitiveDataLoggingMethodInfo = typeof(DbContextOptionsBuilder).GetRequiredRuntimeMethod(
nameof(DbContextOptionsBuilder.EnableSensitiveDataLogging),
typeof(bool));
private static readonly MethodInfo UseNewtonJsonMethodInfo = typeof(MySqlJsonNewtonsoftDbContextOptionsBuilderExtensions).GetRequiredRuntimeMethod(
nameof(MySqlJsonNewtonsoftDbContextOptionsBuilderExtensions.UseNewtonsoftJson),
typeof(MySqlDbContextOptionsBuilder),
typeof(MySqlCommonJsonChangeTrackingOptions));
private static readonly MethodInfo LogToMethodInfo = typeof(DbContextOptionsBuilder).GetRequiredRuntimeMethod(
nameof(DbContextOptionsBuilder.LogTo),
typeof(Action<string>),
typeof(Func<EventId, LogLevel, bool>),
typeof(DbContextLoggerOptions?));
public MethodCallCodeFragment GenerateProviderOptions()
=> new MethodCallCodeFragment(UseNewtonJsonMethodInfo);
public MethodCallCodeFragment GenerateContextOptions()
=> new MethodCallCodeFragment(EnableSensitiveDataLoggingMethodInfo)
.Chain(GenerateLogToMethodCallCodeFragment());
private MethodCallCodeFragment GenerateLogToMethodCallCodeFragment()
=> new MethodCallCodeFragment(
LogToMethodInfo,
new CSharpCodeGenerationExpressionString("Log"),
new CSharpCodeGenerationExpressionString("LogFilter"),
new CSharpCodeGenerationExpressionString("Microsoft.EntityFrameworkCore.Diagnostics.DbContextLoggerOptions.DefaultWithLocalTime"));
}
public class CSharpCodeGenerationExpressionString
{
public string ExpressionString { get; }
public CSharpCodeGenerationExpressionString(string expressionString)
=> ExpressionString = expressionString;
}
public class CustomCSharpHelper : CSharpHelper
{
public CustomCSharpHelper(ITypeMappingSource typeMappingSource)
: base(typeMappingSource)
{
}
public override string UnknownLiteral(object value)
=> value is CSharpCodeGenerationExpressionString codeGenerationExpressionString
? codeGenerationExpressionString.ExpressionString
: base.UnknownLiteral(value);
}
public partial class Context
{
public static void Log(string content)
=> Console.Write(content);
public static bool LogFilter(EventId id, LogLevel level)
=> level >= LogLevel.Information;
}
internal static class Program
{
private static void Main()
{
}
}
我们基本上只是创建我们自己的名为CSharpCodeGenerationExpressionString
的类型来保存我们想要 output 的 C# 代码字符串,然后告诉CustomCSharpHelper.UnknownLiteral()
方法按原样返回它。
生成的OnConfiguring()
方法如下所示:
public partial class Context : DbContext
{
// ...
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (!optionsBuilder.IsConfigured)
{
#warning To protect potentially sensitive information in your connection string, you should move it out of source code. You can avoid scaffolding the connection string by using the Name= syntax to read it from configuration - see https://go.microsoft.com/fwlink/?linkid=2131148. For more guidance on storing connection strings, see http://go.microsoft.com/fwlink/?LinkId=723263.
optionsBuilder
.UseMySql("server=127.0.0.1;port=3306;user=root;database=So73163124_01", Microsoft.EntityFrameworkCore.ServerVersion.Parse("8.0.29-mysql"), x => x.UseNewtonsoftJson())
.EnableSensitiveDataLogging()
.LogTo(Log, LogFilter, Microsoft.EntityFrameworkCore.Diagnostics.DbContextLoggerOptions.DefaultWithLocalTime);
}
}
// ...
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.