前言
本文主要是講解EF Core3.0+ 如何實現自定義的資料庫擴充套件函式
雖然EF.Functions 提供了很多資料庫函式,但是並不全面.比如加密解密..
這樣的話 我們就需要自己擴充套件這些資料庫函式 從而達到呼叫的目的.
本文以達夢資料庫為例(其他資料庫都一樣)..
上篇文章推薦:
EF Core3.0+ 通過攔截器實現讀寫分離與SQL日誌記錄
正文
1.建立擴充套件方法
首先我們需要建立自定義的擴充套件方法如下:
public static class DbFunctionsExtensions { /// <summary> /// 呼叫資料庫的加密方法 /// </summary> /// <param name="_"></param> /// <param name="context"></param> /// <param name="typeid"></param> /// <param name="key"></param> /// <returns></returns> public static string DmAlgorithmsEncrypt(this DbFunctions _, string context, int typeid, string key) { throw new InvalidOperationException( "該方法僅用於實體框架核心,沒有記憶體實現。"); } /// <summary> /// 呼叫資料庫的解密方法 /// </summary> /// <param name="_"></param> /// <param name="context"></param> /// <param name="typeid"></param> /// <param name="key"></param> /// <returns></returns> public static string DmAlgorithmsDecrypt(this DbFunctions _, string context, int typeid, string key) { throw new InvalidOperationException( "該方法僅用於實體框架核心,沒有記憶體實現。"); }
很簡單,我們只需要定義2個靜態擴充套件方法,並且丟擲一個InvalidOperationException異常即可.
2.建立呼叫方法轉換器(IMethodCallTranslator)
這裡記住IMethodCallTranslator這個介面,我們需要實現它,如下:
public class DmDbFunctionsTranslateImpl : IMethodCallTranslator { private readonly ISqlExpressionFactory _expressionFactory; private static readonly MethodInfo _dmAlgorithmsEncryptMethod = typeof(DbFunctionsExtensions).GetMethod( nameof(DbFunctionsExtensions.DmAlgorithmsEncrypt), new[] { typeof(DbFunctions), typeof(string), typeof(int), typeof(string) }); private static readonly MethodInfo _dmAlgorithmsDecryptMethod = typeof(DbFunctionsExtensions).GetMethod( nameof(DbFunctionsExtensions.DmAlgorithmsDecrypt), new[] { typeof(DbFunctions), typeof(string), typeof(int), typeof(string) }); public DmDbFunctionsTranslateImpl(ISqlExpressionFactory expressionFactory) { _expressionFactory = expressionFactory; } public SqlExpression Translate(SqlExpression instance, MethodInfo method, IReadOnlyList<SqlExpression> arguments) { //判斷方法是否一致 if (method == _dmAlgorithmsEncryptMethod) { var args = new List<SqlExpression> { arguments[1], arguments[2], arguments[3] }; return _expressionFactory.Function(instance, "CFALGORITHMSENCRYPT", args, typeof(string)); } if (method == _dmAlgorithmsDecryptMethod) { var args = new List<SqlExpression> { arguments[1], arguments[2], arguments[3] }; return _expressionFactory.Function(instance, "CFALGORITHMSDECRYPT", args, typeof(string)); } return null; } }
3.建立呼叫轉換器提供程式(RelationalMethodCallTranslatorProvider)
public sealed class DmAlgorithmsMethodCallTranslatorPlugin : RelationalMethodCallTranslatorProvider { public DmAlgorithmsMethodCallTranslatorPlugin(RelationalMethodCallTranslatorProviderDependencies dependencies) : base(dependencies) { ISqlExpressionFactory expressionFactory = dependencies.SqlExpressionFactory; AddTranslators( new IMethodCallTranslator[] { //這裡,將剛剛的方法轉換器新增到擴充套件 new DmDbFunctionsTranslateImpl(expressionFactory) }); } }
這個類主要是將我們剛剛建立的方法轉換器新增SQL表示式工廠(SqlExpressionFactory)當中.
4.建立DbContext擴充套件類(IDbContextOptionsExtension)
程式碼如下,關鍵點加了註釋,自行參考..
public class DmDbContextOptionsExtension : IDbContextOptionsExtension { private DbContextOptionsExtensionInfo _info; public void Validate(IDbContextOptions options) { } public DbContextOptionsExtensionInfo Info { get { return this._info ??= new MyDbContextOptionsExtensionInfo(this); } } void IDbContextOptionsExtension.ApplyServices(IServiceCollection services) { //這裡將轉換器注入到服務當中. services.AddSingleton<IMethodCallTranslatorProvider, DmAlgorithmsMethodCallTranslatorPlugin>(); } private sealed class MyDbContextOptionsExtensionInfo : DbContextOptionsExtensionInfo { public MyDbContextOptionsExtensionInfo(IDbContextOptionsExtension instance) : base(instance) { } public override bool IsDatabaseProvider => false; public override string LogFragment => ""; public override void PopulateDebugInfo(IDictionary<string, string> debugInfo) { } public override long GetServiceProviderHashCode() { return 0; } } }
5.建立DbContext生成時的擴充套件方法
public static class DmDbContextOptionsBuilderExtensions { public static DbContextOptionsBuilder UseDmAlgorithmsEncryptionFunctions( this DbContextOptionsBuilder optionsBuilder) { //將自定義的配置類新增到配置選項中 var extension = GetOrCreateExtension(optionsBuilder); ((IDbContextOptionsBuilderInfrastructure)optionsBuilder).AddOrUpdateExtension(extension); return optionsBuilder; } //生成建立擴充套件類 private static DmDbContextOptionsExtension GetOrCreateExtension(DbContextOptionsBuilder optionsBuilder) => optionsBuilder.Options.FindExtension<DmDbContextOptionsExtension>() ?? new DmDbContextOptionsExtension(); }
6.編寫測試程式碼,檢視使用效果
我們先在資料庫插入一條加密資料如下:
insert into "tab"."tab"( "XingMing", "ZhengJianHao", "ShouJiHao") VALUES( '測試資料1', CFALGORITHMSENCRYPT('123456789',514,'ABC'),'77777');
然後我們編寫查詢程式碼:
var ddd= Context.Where(a => EF.Functions.DmAlgorithmsDecrypt(a.ZhengJianHao, 514, "ABC") == "123456789").First();
這裡,我們將資料解密後在對比
查詢效果如下:
我們通過監控SQL語句 可以看到如下SQL語句:
這裡,已經將我們的自定義擴充套件函式轉換成了SQL函式 並在資料庫執行了.
寫在最後
這裡我們就完成了整個SQL函式的擴充套件. 寫這篇主要是為了拋磚引玉..
目前這種擴充套件方式,在查詢的時候 可以正常的生成SQL語句,
但是在ADD 和Update的時候 並不會生成對應的語句,所以想問問各位大佬,有沒有更好的實現方式.