C#的AOP(最經典實現)

来源:https://www.cnblogs.com/dotnet-college/p/18128903
-Advertisement-
Play Games

(適用於.NET/.NET Core/.NET Framework) 【目錄】0.前言1.第一個AOP程式2.Aspect橫切麵編程3.一個橫切麵程式攔截多個主程式4.多個橫切麵程式攔截一個主程式5.AOP的泛型處理(擴充)6.AOP的非同步處理(擴充)7.優勢總結8.展望 0.前言 AOP(Aspe ...


(適用於.NET/.NET Core/.NET Framework)

【目錄】
0.前言
1.第一個AOP程式
2.Aspect橫切麵編程
3.一個橫切麵程式攔截多個主程式
4.多個橫切麵程式攔截一個主程式
5.AOP的泛型處理(擴充)
6.AOP的非同步處理(擴充)
7.優勢總結
8.展望

0.前言

AOP(Aspect Oriented Programming)是“面向橫切麵編程”,主要是用來對程式/模塊進行解耦。怎麼理解??

我們可以把一般的編程理解為“縱向編程”(主程式),比如如下的一個示例代碼:

        public string GetInfo(int i)
        {
            string s = "";

            if (i == 1)
                s = "A";
            else if (i == 2)
                s = "B";
            else if (i == 3)
                s = "C";
            else
                s = "Z";

            return s;
        }

試想一下,上述軟體實際使用後,

  • 如果條件變數i有更多的判斷值,我們是不是要在GetInfo()方法內部修改代碼+重新編譯?
  • 如果後續需要加個日誌記錄功能,我們是不是也要在GetInfo()方法內部加上日誌函數+重新編譯?
  • 如果...
  • 更多如果...

為了避免上述的這些麻煩並增加軟體的靈活性,“橫向編程”,也就是AOP被創造了出來,它就像是“橫切一刀”,把相關功能塞進了主程式。

 

現行AOP的實現,主要是通過攔截方法(即攔截主程式),並修改其參數+返回值來完成。

網上有很多相關方案,比如:特性註釋攔截、動態代碼生成、派遣代理模式、等。但這些方案要麼實現的很複雜、要麼耦合度沒完全切斷、邏輯有變化時還是需要修改代碼重新編譯。均不夠理想。

而今天要隆重登場的主角-DeveloperSharp平臺中的AOP技術,則提供了一種簡便、快捷、徹底解耦的AOP實現。使用它,在程式邏輯有變化時,你只需要修改配置文件就行,而不再需要對主程式進行一丁丁點的代碼修改!!

1.第一個AOP程式

製作一個AOP程式需要四個步驟:

(1)製作主程式

(2)製作橫切麵程式

(3)製作配置文件,讓橫切麵程式攔截主程式

(4)調用主程式

下麵,我們一步一步來實現上述四個步驟。

【第一步】:製作主程式

我們在Visual Studio中新建一個名為“School.Logic”的類庫工程,併在該工程中新建一個名為PersonManage的類,該類中有一個名為GetInfo1的方法,代碼如下:

//從NuGet引用DeveloperSharp包
using DeveloperSharp.Structure.Model;

namespace School.Logic
{
    //主程式必須繼承自LogicLayer類
    public class PersonManage : LogicLayer
    {
        public string GetInfo1(string Name, int Num)
        {
            return $"共有{Name}{Num}人";
        }
    }
}

以上,編寫了一個非常簡單的主程式。

 

【第二步】:製作橫切麵程式

我們再在Visual Studio中新建一個名為“School.Aspect”的類庫工程,併在該工程中新建一個名為Interceptor1的類,代碼如下:

//從NuGet引用DeveloperSharp包
using DeveloperSharp.Structure.Model.Aspect;

namespace School.Aspect
{
    //橫切麵程式必須繼承自AspectModel類
    public class Interceptor1 : AspectModel
    {
        //PreProcess方法先於主程式執行
        public override void PreProcess(object sender, AspectEventArgs e)
        {
            //把主程式的兩個參數值改掉
            e.MethodInfo.ParameterValues[0] = "老師";
            e.MethodInfo.ParameterValues[1] = 20;
        }

        //PostProcess方法後於主程式執行
        public override void PostProcess(object sender, AspectEventArgs e)
        {
            
        }
    }
}

 以上,編寫了一個橫切麵程式。它的主要功能是把主程式方法的兩個參數值給改掉。
AspectModel基類中的PreProcess方法會在主程式方法執行之前被執行,而PostProcess方法會在主程式方法執行之後被執行。它兩就是AOP橫向攔截的核心要素。它兩均需要被override重寫覆蓋掉。

 

【第三步】:製作配置文件,讓橫切麵程式攔截主程式

若是在.Net Core環境下,我們創建一個名為DeveloperSharp.json的配置文件,設置讓Interceptor1攔截PersonManage中的GetInfo1方法。文件內容如下:

{
  "DeveloperSharp":
  {
     "AspectObject":
     [
         {
           "name":"School.Aspect.Interceptor1",  //橫切麵攔截器類
           "scope":"School.Logic.PersonManage",  //被攔截的主程式類
           "method":"GetInfo1"                   //被攔截的方法
         }
     ]
  }
}

若是在.Net Framework環境下,我們創建一個名為DeveloperSharp.xml的配置文件,設置讓Interceptor1攔截PersonManage中的GetInfo1方法。文件內容如下:

<?xml version="1.0" encoding="utf-8" ?>
<DeveloperSharp>
  <AspectObject>
    <Ao name="School.Aspect.Interceptor1" scope="School.Logic.PersonManage" method="GetInfo1"/>
  </AspectObject>
</DeveloperSharp>

註意:以上配置中所有的類名,都要用完全限定名。

 

【第四步】:調用主程式

最後,我們再在Visual Studio中創建一個控制台工程,讓它來調用主程式中的GetInfo1方法,代碼如下:

        //需要引用School.Aspect、School.Logic、DeveloperSharp三項
        static void Main(string[] args)
        {
            var pm = new School.Logic.PersonManage();

            //要用這種形式調用主程式中的方法,AOP功能才會生效
            var str = pm.InvokeMethod("GetInfo1", "學生", 200);
            Console.WriteLine(str);

            Console.ReadLine();
        }

附註:有人會覺得上述InvokeMethod這種調用方法不夠優雅,但事實上ASP.NET Web Api也是被類似InvokeMethod這種方式包裹調用才實現了各種Filter攔截器的攔截(本質也是AOP),只不過它的這個InvokeMethod動作是在.NET自身的CLR管道運行時中進行的。而且,那些Filter攔截器還只能用於ASP.NET Web Api環境,而不能像本方案這樣用於一般程式。

現在,為了讓前面第三步創建的配置文件生效,我們此時還需要在此主調項目中對它進行鏈接:
若是在.Net Core環境下,我們只需要把DeveloperSharp.json文件放到程式執行目錄中(即bin目錄下與dll、exe等文件的同一目錄中,放錯了位置會報錯)(註意:有些.Net Core版本在Visual Studio“調試”時,不會在bin目錄下生成全部的dll、exe,此時需要把此配置文件放在應用程式的“根目錄”下)。

若是在.Net Framework環境下,我們需要在工程配置文件App.config/Web.config中添加appSettings節點,節點內容如下:

  <appSettings>
    <add key="ConfigFile" value="D:\Test\Assist\DeveloperSharp.xml" />
  </appSettings>

此處需要設置為配置文件的“絕對路徑”(使用“絕對路徑”而不是“相對路徑”,一是有利於安全性,二是有利於分散式部署)

 

一切準備完畢,運行,結果如下:

【控制台顯示出】:共有老師20人

可見AOP已經攔截成功。

若此時,我們在配置文件DeveloperSharp.json/DeveloperSharp.xml中稍做修改,比如:把“GetInfo1”這個方法名改為“ABC”這樣一個不存在的方法名,再運行,結果如下:

【控制台顯示出】:共有學生200人

 

2.Aspect橫切麵編程

上面,第二步,製作的橫切麵程式,是通過修改主程式方法的參數值,而最終改變了主程式的返回值。

其實,我們也有辦法直接修改主程式方法的返回值,比如把上面Interceptor1類的代碼修改為如下:

//從NuGet引用DeveloperSharp包
using DeveloperSharp.Structure.Model.Aspect;

namespace School.Aspect
{
    //橫切麵程式必須繼承自AspectModel類
    public class Interceptor1 : AspectModel
    {
        //PreProcess方法先於主程式執行
        public override void PreProcess(object sender, AspectEventArgs e)
        {

        }

        //PostProcess方法後於主程式執行
        public override void PostProcess(object sender, AspectEventArgs e)
        {
            //把主程式的返回值改掉
            e.MethodInfo.ReturnValue = $"共有校長2人";
        }
    }
}

運行,結果如下:

【控制台顯示出】:共有校長2人

 

到目前為止,我們已經知道瞭如何通過“Aspect橫切麵程式”修改主程式方法的參數值、返回值。

如果我們想進一步獲取主程式的“命名空間”、“類名”、“方法名”、“參數名”、“參數類型”、“返回值類型”,則可以通過如下代碼獲取:

e.MethodInfo.NamespaceName                       //命名空間
e.MethodInfo.ClassName                           //類名
e.MethodInfo.MethodName                          //方法名
e.MethodInfo.ParameterInfos[0].Name              //參數名(第一個參數)
e.MethodInfo.ParameterInfos[0].ParameterType     //參數類型(第一個參數)
e.MethodInfo.ReturnValue.GetType()               //返回值類型

 

有時候,在某些特殊情況下,我們希望主程式方法不運行,此時則可以通過在PreProcess方法里把e.Continue設置為false來完成。

接前面的“第一個AOP程式”,比如:我們希望當人數大於10000時,主程式方法就不再運行,則可以通過把Interceptor1類的代碼修改為如下樣式來實現:

//從NuGet引用DeveloperSharp包
using DeveloperSharp.Structure.Model.Aspect;

namespace School.Aspect
{
    //橫切麵程式必須繼承自AspectModel類
    public class Interceptor1 : AspectModel
    {
        //PreProcess方法先於主程式執行
        public override void PreProcess(object sender, AspectEventArgs e)
        {
            //當人數大於10000時,主程式方法就不再運行
            if (Convert.ToInt32(e.MethodInfo.ParameterValues[1]) > 10000)
                e.Continue = false;
        }

        //PostProcess方法後於主程式執行
        public override void PostProcess(object sender, AspectEventArgs e)
        {

        }
    }
}

現在的這個示例是一個Aspect橫切麵程式攔截一個主程式。在後續將要講解的“多個Aspect橫切麵程式攔截一個主程式”的情況中,只要有一個e.Continue=false被設置,主程式方法就不會運行(在此事先提點)。

 

3.一個橫切麵程式攔截多個主程式

為了演示這部分的內容,我們首先在前面“第一個AOP程式”的基礎上,把主程式進行擴充。採取的動作是:

(1)在PersonManage類中增加一個GetInfo2方法

(2)再新增一個主程式類SystemManage,該類中有一個名為GetMessage1的方法。代碼如下:

PersonManage類:

//從NuGet引用DeveloperSharp包
using DeveloperSharp.Structure.Model;

namespace School.Logic
{
    //主程式必須繼承自LogicLayer類
    public class PersonManage : LogicLayer
    {
        public string GetInfo1(string Name, int Num)
        {
            return $"共有{Name}{Num}人";
        }

        public string GetInfo2(string Name, int Num)
        {
            return $"學校共有{Name}{Num}人";
        }
    }
}

SystemManage類:

//從NuGet引用DeveloperSharp包
using DeveloperSharp.Structure.Model;

namespace School.Logic
{
    //主程式必須繼承自LogicLayer類
    public class SystemManage : LogicLayer
    {
        public string GetMessage1(string Name1, int Num1, string Name2, int Num2)
        {
            return $"第一組共有{Name1}{Num1}人,第二組共有{Name2}{Num2}人";
        }
    }
}

如此一來,現在就有了3個主程式方法。

 

接下來,我們修改配置文件,讓Interceptor1去攔截上述的3個主程式方法。

若是在.Net Core環境下,DeveloperSharp.json文件的內容修改為如下:

{
  "DeveloperSharp":
  {
     "AspectObject":
     [
         {
           "name":"School.Aspect.Interceptor1", 
           "scope":"School.Logic.PersonManage",
           "method":"*"   //星號*代表該作用域下的全部方法
         },
         {
           "name":"School.Aspect.Interceptor1", 
           "scope":"School.Logic.SystemManage",
           "method":"GetMessage1"
         }
     ]
  }
}

若是在.Net Framework環境下,DeveloperSharp.xml文件的內容修改為如下:

<?xml version="1.0" encoding="utf-8" ?>
<DeveloperSharp>
  <AspectObject>
    <Ao name="School.Aspect.Interceptor1" scope="School.Logic.PersonManage" method="*"/>
    <Ao name="School.Aspect.Interceptor1" scope="School.Logic.SystemManage" method="GetMessage1"/>
  </AspectObject>
</DeveloperSharp>

 

 最後,我們把控制台啟動程式修改為如下:

        //需要引用School.Aspect、School.Logic、DeveloperSharp三項
        static void Main(string[] args)
        {
            var pm = new School.Logic.PersonManage();
            var sm = new School.Logic.SystemManage();

            //要用這種形式調用主程式中的方法,AOP功能才會生效
            var str1 = pm.InvokeMethod("GetInfo1", "學生", 200);
            var str2 = pm.InvokeMethod("GetInfo2", "學生", 200);
            var str3 = sm.InvokeMethod("GetMessage1", "學生", 200, "院士", 10);
            Console.WriteLine(str1);
            Console.WriteLine(str2);
            Console.WriteLine(str3);

            Console.ReadLine();
        }

運行結果如下:

【控制台顯示出】:

共有老師20人
學校共有老師20人
第一組共有老師20人,第二組共有院士10人

可見AOP所有攔截均已成功!

 

4.多個橫切麵程式攔截一個主程式

為了演示這部分的內容,我們還是要先回到前面的“第一個AOP程式”,在它的基礎上,我們新增一個名為Interceptor2的Aspect橫切麵類,代碼如下:

//從NuGet引用DeveloperSharp包
using DeveloperSharp.Structure.Model.Aspect;

namespace School.Aspect
{
    //橫切麵程式必須繼承自AspectModel類
    public class Interceptor2 : AspectModel
    {
        //PreProcess方法先於主程式執行
        public override void PreProcess(object sender, AspectEventArgs e)
        {
            //把主程式的兩個參數值改掉
            e.MethodInfo.ParameterValues[0] = "輔導員";
            e.MethodInfo.ParameterValues[1] = 40;
        }

        //PostProcess方法後於主程式執行
        public override void PostProcess(object sender, AspectEventArgs e)
        {

        }
    }
}

如此一來,我們就有了2個Aspect橫切麵程式Interceptor1與Interceptor2。

 

接下來,我們修改配置文件,讓Interceptor1、Interceptor2都去攔截主程式方法GetInfo1。

若是在.Net Core環境下,DeveloperSharp.json文件的內容修改為如下:

{
  "DeveloperSharp":
  {
     "AspectObject":
     [
         {
           "name":"School.Aspect.Interceptor1", 
           "scope":"School.Logic.PersonManage",
           "method":"GetInfo1"
         },
         {
           "name":"School.Aspect.Interceptor2", 
           "scope":"School.Logic.PersonManage",
           "method":"GetInfo1"
         }
     ]
  }
}

若是在.Net Framework環境下,DeveloperSharp.xml文件的內容修改為如下:

<?xml version="1.0" encoding="utf-8" ?>
<DeveloperSharp>
  <AspectObject>
    <Ao name="School.Aspect.Interceptor1" scope="School.Logic.PersonManage" method="GetInfo1"/>
    <Ao name="School.Aspect.Interceptor2" scope="School.Logic.PersonManage" method="GetInfo1"/>
  </AspectObject>
</DeveloperSharp>

 

上述修改完畢,運行控制台主調程式,結果如下:

【控制台顯示出】:共有輔導員40人

從上述運行結果,我們大致可以推斷出:Interceptor1、Interceptor2這兩個Aspect橫切麵攔截器是按配置順序執行的。其中,Interceptor1先把GetInfo1方法的兩個參數值改為了("老師",20),接著,Interceptor2又把GetInfo1方法的兩個參數值改為了("輔導員",40),所以最終GetInfo1方法的參數值變為了("輔導員",40)。

 

5.AOP的泛型處理
如果我們的主程式是泛型方法,則需要用InvokeMethod<T>這種方式來進行調用。

比如,現有如下的主程式代碼:

//從NuGet引用DeveloperSharp包
using DeveloperSharp.Structure.Model;

namespace Test4Logic
{
    //主程式必須繼承自LogicLayer類
    public class Calculate : LogicLayer
    {
        public int add(int i, int j)
        {
            return i + j;
        }

        public int add(int i, int j, int k)
        {
            return i + j + k;
        }

        public string add<T>(T i, T j, T k)
        {
            return "T" + i.ToString() + j.ToString() + k.ToString();
        }

        public string add<T, V>(T i, T j, V k)
        {
            return "TTV" + i.ToString() + j.ToString() + k.ToString();
        }

        public string add<T, V>(T i, V j, V k)
        {
            return "TVV" + i.ToString() + j.ToString() + k.ToString();
        }
    }
}

 

對應的Aspect橫切麵類代碼如下:

//從NuGet引用DeveloperSharp包
using DeveloperSharp.Structure.Model.Aspect;

namespace Test4Aspect
{
    //橫切麵程式必須繼承自AspectModel類
    public class Interceptor : AspectModel
    {
        //PreProcess方法先於主程式執行
        public override void PreProcess(object sender, AspectEventArgs e)
        {
            //把主程式的第一個參數值改掉
            e.MethodInfo.ParameterValues[0] = 8;

        }

        //PostProcess方法後於主程式執行
        public override void PostProcess(object sender, AspectEventArgs e)
        {

        }
    }
}

 

對應的配置文件如下:
若是在.Net Core環境下,DeveloperSharp.json文件的內容如下:

{
  "DeveloperSharp":
  {
     "AspectObject":
     [
         {
           "name":"Test4Aspect.Interceptor",  //橫切麵攔截器類
           "scope":"Test4Logic.Calculate",    //被攔截的主程式類
           "method":"add"                     //被攔截的方法
         }
     ]
  }
}

若是在.Net Framework環境下,DeveloperSharp.xml文件的內容如下:

<?xml version="1.0" encoding="utf-8" ?>
<DeveloperSharp>
  <AspectObject>
    <Ao name="Test4Aspect.Interceptor" scope="Test4Logic.Calculate" method="add"/>
  </AspectObject>
</DeveloperSharp>

 

控制台主調程式代碼如下: 

        //需要引用Test4Aspect、Test4Logic、DeveloperSharp三項
        static void Main(string[] args)
        {
              var cal = new Test4Logic.Calculate();

              //要用這種形式調用主程式中的方法,AOP功能才會生效
              var r1 = cal.InvokeMethod("add", 1, 2);
              var r2 = cal.InvokeMethod("add", 1, 2, 3);
              var r3 = cal.InvokeMethod<int>("add", 1, 2, 3);
              var r4 = cal.InvokeMethod<int, float>("add", 1, 2, (float)3);
              var r5 = cal.InvokeMethod<int, float>("add", 1, (float)2, (float)3);

              Console.WriteLine(r1);
              Console.WriteLine(r2);
              Console.WriteLine(r3);
              Console.WriteLine(r4);
              Console.WriteLine(r5);

              Console.ReadLine();
        }

 

運行上述控制台主調程式,結果如下:

【控制台顯示出】:
10
13
T823
TTV823
TVV823

主程式中每個泛型方法的對應調用一目瞭然。

 

6.AOP的非同步處理
如果我們的主程式是非同步方法,還是使用InvokeMethod來進行調用。下麵給出一個代碼樣式示例(代碼做了簡化處理):

//主程式

    //主程式必須繼承自LogicLayer類
    public class UserService : LogicLayer
    {
        public async Task<Worker> GetUser(string Id, int Age, string Name)
        {
              //...相關代碼...
        }

        public async Task<T> GetUser<T>(string Id, int Age, string Name) where T : User, new()
        {
              //...相關代碼...
        }
    }
----------------------------------------------------------------------
//主調程式

       var us = new UserService();

       //要用這種形式調用主程式中的方法,AOP功能才會生效
       var worker = await us.InvokeMethod("GetUser", "C007", 26, "alex");
       var user = await us.InvokeMethod<Manager>("GetUser", "A002", 46, "kevin");

       Console.WriteLine(worker.Name);
       Console.WriteLine(user?.Name);

 

即然主程式可以是非同步的,那Aspect橫截面攔截程式能不能也是非同步的了?答案是肯定的。你可以把PreProcess與PostProcess中的至少一個改為非同步方法,實現單個Aspect類的同步非同步混用,其代碼結構與原先的同步Aspect類一致,這點連.NET/微軟自身都還沒有實現...
下麵給出一個示例代碼:

    //橫切麵程式必須繼承自AspectModel類
    public class UserInterceptor : AspectModel
    {
        //PreProcess方法先於主程式執行
        public override void PreProcess(object sender, AspectEventArgs e)
        {

        }

        //PostProcess方法後於主程式執行
        public override async void PostProcess(object sender, AspectEventArgs e)
        {
            await Task.Run(() =>
            {
                Thread.Sleep(10000);
                File.AppendAllText("D:/zzz.txt", "耗時操作");
            });
        }
    }

  

7.優勢總結

本文所講述的,是全網唯一實現AOP徹底解耦的技術方案。使用它,當你需要給主程式增加Aspect橫切麵攔截器時,無論是增加一個還是多個,都不再需要修改&重新編譯主程式。這實現了不同功能構件之間的0依賴拼裝/拆卸開發方式,隨之而來的也會對研發團隊的管理模式產生重大影響,意義深遠...

 

8.展望

AOP對於程式代碼的解耦、業務模塊的拆分與拼裝組合,有著巨大的作用。正確的使用AOP,甚至能對傳統的軟體架構設計,產生顛覆性的影響,如超級戰士出場一般,讓所有人刮目相看,完全耳目一新!!

為了讓讀者能直觀感知AOP的上述神奇魅力,下麵給出一個業務案例:
有一批貨品要錄入資料庫,貨品包含長、寬、高、顏色、類型等屬性。現在有業務需求如下,
(1)當貨

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 強制等待 即sleep()方法,由python中的time模塊提供,強制讓代碼等待xxx時間,無論前面的代碼是否執行完成或者還未完成,都必須等待設定的時間。 示例代碼如下: # coding = utf-8 from selenium import webdriver from time impor ...
  • 拓展閱讀 MySQL View MySQL truncate table 與 delete 清空表的區別和坑 MySQL Ruler mysql 日常開發規範 MySQL datetime timestamp 以及如何自動更新,如何實現範圍查詢 MySQL 06 mysql 如何實現類似 oracl ...
  • maku-generator —— 一款低代碼生成器,可根據自定義模板內容,快速生成前後端代碼,可實現項目的快速開發、上線,減少重覆的代碼編寫,開發人員只需專註業務邏輯即可。 ...
  • 目錄簡介源碼函數說明arv_camera_newarv_camera_acquisitionarv_camera_get_model_namearv_buffer_get_image_widtharv_buffer_get_image_height 簡介 本文針對官方常式中的第一個常式:single ...
  • DDD 領域驅動設計理解(Domain Driven Design) 目錄DDD 領域驅動設計理解(Domain Driven Design)概念核心目標 概念 領域驅動設計事實上是1針對OOAD的一個擴展和延申。DDD基於面向對象分析與設計技術。 對技術架構進行了分層規劃。 對每個類進行了策略和劃 ...
  • Spring Boot 允許你將配置外部化,以便可以在不同的環境中使用相同的應用程式代碼。可以使用屬性文件、YAML文件、環境變數和命令行參數將配置外部化。屬性值可以通過使用 @Value 註解直接註入 bean,可以通過 Spring 的 Environment 抽象訪問,也可以通過 @Confi... ...
  • 永久激活支持全家桶所有軟體,包括 Pycharm、IDEA、WebStorm、Phpstorm、Datagrip、RubyMine、CLion、AppCode 下麵以 Intellij IDEA 作為演示。 準備工作:下載插件包 https://qweree.cn/index.php/259/(如果 ...
  • 本文介紹基於Python中ArcPy模塊,實現Excel數據讀取並生成矢量圖層,同時進行IDW插值與批量掩膜的方法。 1 任務需求 首先,我們來明確一下本文所需實現的需求。 現有一個記錄有北京市部分PM2.5濃度監測站點在2019年05月18日00時至23時(其中不含19時)等23個逐小時PM2.5 ...
一周排行
    -Advertisement-
    Play Games
  • .Net8.0 Blazor Hybird 桌面端 (WPF/Winform) 實測可以完整運行在 win7sp1/win10/win11. 如果用其他工具打包,還可以運行在mac/linux下, 傳送門BlazorHybrid 發佈為無依賴包方式 安裝 WebView2Runtime 1.57 M ...
  • 目錄前言PostgreSql安裝測試額外Nuget安裝Person.cs模擬運行Navicate連postgresql解決方案Garnet為什麼要選擇Garnet而不是RedisRedis不再開源Windows版的Redis是由微軟維護的Windows Redis版本老舊,後續可能不再更新Garne ...
  • C#TMS系統代碼-聯表報表學習 領導被裁了之後很快就有人上任了,幾乎是無縫銜接,很難讓我不想到這早就決定好了。我的職責沒有任何變化。感受下來這個系統封裝程度很高,我只要會調用方法就行。這個系統交付之後不會有太多問題,更多應該是做小需求,有大的開發任務應該也是第二期的事,嗯?怎麼感覺我變成運維了?而 ...
  • 我在隨筆《EAV模型(實體-屬性-值)的設計和低代碼的處理方案(1)》中介紹了一些基本的EAV模型設計知識和基於Winform場景下低代碼(或者說無代碼)的一些實現思路,在本篇隨筆中,我們來分析一下這種針對通用業務,且只需定義就能構建業務模塊存儲和界面的解決方案,其中的數據查詢處理的操作。 ...
  • 對某個遠程伺服器啟用和設置NTP服務(Windows系統) 打開註冊表 HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\W32Time\TimeProviders\NtpServer 將 Enabled 的值設置為 1,這將啟用NTP伺服器功 ...
  • title: Django信號與擴展:深入理解與實踐 date: 2024/5/15 22:40:52 updated: 2024/5/15 22:40:52 categories: 後端開發 tags: Django 信號 松耦合 觀察者 擴展 安全 性能 第一部分:Django信號基礎 Djan ...
  • 使用xadmin2遇到的問題&解決 環境配置: 使用的模塊版本: 關聯的包 Django 3.2.15 mysqlclient 2.2.4 xadmin 2.0.1 django-crispy-forms >= 1.6.0 django-import-export >= 0.5.1 django-r ...
  • 今天我打算整點兒不一樣的內容,通過之前學習的TransformerMap和LazyMap鏈,想搞點不一樣的,所以我關註了另外一條鏈DefaultedMap鏈,主要調用鏈為: 調用鏈詳細描述: ObjectInputStream.readObject() DefaultedMap.readObject ...
  • 後端應用級開發者該如何擁抱 AI GC?就是在這樣的一個大的浪潮下,我們的傳統的應用級開發者。我們該如何選擇職業或者是如何去快速轉型,跟上這樣的一個行業的一個浪潮? 0 AI金字塔模型 越往上它的整個難度就是職業機會也好,或者說是整個的這個運作也好,它的難度會越大,然後越往下機會就會越多,所以這是一 ...
  • @Autowired是Spring框架提供的註解,@Resource是Java EE 5規範提供的註解。 @Autowired預設按照類型自動裝配,而@Resource預設按照名稱自動裝配。 @Autowired支持@Qualifier註解來指定裝配哪一個具有相同類型的bean,而@Resourc... ...