EFCore3.1+編寫自定義的EF.Functions擴展方法


前言

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM