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