前言
本文主要是講解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的時候 并不會生成對應的語句,是以想問問各位大佬,有沒有更好的實作方式.