在ASP.NET MVC應用中開發插件框架(中英對照)

来源:https://www.cnblogs.com/mikecheers/archive/2019/01/17/10283803.html
-Advertisement-
Play Games

[原文] Developing a plugin framework in ASP.NET MVC with medium trust [譯文] 在ASP.NET MVC應用中開發一個插件框架 I’ve recently spent quite a lot of time researching a ...


[原文] Developing a plugin framework in ASP.NET MVC with medium trust

[譯文] 在ASP.NET MVC應用中開發一個插件框架

 

I’ve recently spent quite a lot of time researching and prototyping different ways to create a plugin engine in ASP.NET MVC3 and primarily finding a nice way to load plugins (DLLs) in from outside of the ‘bin’ folder. Although this post focuses on MVC3, I am sure that the same principles will apply for other MVC versions.

我最近花了很多時間研究和原形設計在ASP.NET MVC3應用中創建插件引擎的不同方法,主要是為了找到一個能夠載入"bin"目錄之外的插件(DLLs)的好的方法。雖然這篇文章聚焦在MVC3上,不過我相信這些相同的原則也適用於其他MVC版本。

 

The Issues

Loading DLLs from outside of the ‘bin’ folder isn’t really anything new or cutting edge, however when working with MVC this becomes more difficult. This is primarily due to how MVC loads/finds types that it needs to process including controllers, view models (more precisely the generic argument passed to a ViewPage or used with the @model declaration in Razor), model binders, etc… MVC is very tied to the BuildManager which is the mechanism for compiling views, and locating other services such as controllers. By default the BuildManager is only familiar with assembies in the ‘bin’ folder and in the GAC, so if you start putting DLLs in folders outside of the ‘bin’ then it won’t be able to locate the MVC services and objects that you might want it to be referencing.

Another issue that needs to be dealt with is DLL file locking. When a plugin DLL is loaded and is in use the CLR will lock the file. This becomes an issue if developers want to update the plugin DLL while the website is running since they won’t be able to unless they bump the web.config or take the site down. This holds true for Managed Extensibility Framework (MEF) and how it loads DLLs as well.

 

問題

"bin"目錄以外載入DLLs已不是什麼新鮮、前沿的事兒,然而使用MVC時,這將變得更加困難。這主要是由於在處理控制器、模型視圖(更準確的說是給ViewPage傳遞參數或者在Razor中使用@model定義)、模型綁定等等的過程中載入/查找類型的方式引起的。與BuildManager的機制息息相關,BuildManager是編譯視圖和定位諸如控制器等其他服務的機制。預設情況下,BuildManager只對"bin"目錄和全局程式集中的程式集感冒,所以如果一開始就將需要引用的服務和對象的DLLs放置在"bin"目錄之外,MVC將不能定位到它們。

另外一個需要處理的問題就是DLL文件所定。當一個插件DLL被載入和使用時,CLR就會將這個DLL文件鎖定。這造成一個問題,就是開發人員不能夠在應用運行中更新這些DLLs,除非他們修改web.config(將引發應用重新啟動)或乾脆將應用停止。這適用於MEF以及它如何載入DLL。

 

.Net 4 to the rescue… almost

One of the new features in .Net 4 is the ability to execute code before the app initializes which compliments another new feature of the BuildManager that lets you add assembly references to it at runtime (which must be done on application pre-init). Here’s a nice little reference to these new features from Phil Haack: http://haacked.com/archive/2010/05/16/three-hidden-extensibility-gems-in-asp-net-4.aspx.  This is essential to making a plugin framework work with MVC so that the BuildManager knows where to reference your plugin DLLs outside of the ‘bin’. However, this isn’t the end of the story.

 

.Net 4的救援措施

.Net 4中的一個新功能是在應用程式初始化之前執行代碼的能力,它與BuildManager的另一個新功能相輔相成,它允許您在運行時向其添加程式集引用(必須在應用程式預啟動時完成)。 以下是Phil Haack對這些新功能的一個很好的參考:http://haacked.com/archive/2010/05/16/three-hidden-extensibility-gems-in-asp-net-4.aspx。 這對於使插件框架與MVC一起工作至關重要,以便BuildManager知道在"bin"目錄之外引用插件DLLs的位置。 然而,故事到這裡還沒完。

 

Strongly typed Views with model Types located in plugin DLLs

Unfortunately if you have a view that is strongly typed to a model that exists outside of the ‘bin’, then you’ll find out very quickly that it doesn’t work and it won’t actually tell you why. This is because the RazorViewEngine  uses the BuildManager to compile the view into a dynamic assembly but then uses Activator.CreateInstance to instantiate the newly compiled object. This is where the problem lies, the current AppDomain doesn’t know how to resolve the model Type for the strongly typed view since it doesn’t exist in the ‘bin’ or GAC.  An even worse part about this scenario is that you don’t get any error message telling you why this isn’t working, or where the problem is. Instead you get the nice MVC view not found error: “…or its master was not found or no view engine supports the searched locations. The following locations were searched: ….” telling you that it has searched for views in all of the ViewEngine locations and couldn’t find it… which is actually not the error at all.  Deep in the MVC3 source, it tries to instantiate the view object from the dynamic assembly and it fails so it just keeps looking for that view in the rest of the ViewEngine paths.

NOTE: Even though in MVC3 there’s a new IViewPageActivator which should be responsible for instantiating the views that have been compiled with the BuildManager, implementing a custom IViewPageActivator to handle this still does not work because somewhere in the MVC3 codebase fails before the call to the IViewPageActivator which has to do with resolving an Assembly that is not in the ‘bin’.

 

模型類型定義在插件程式集中的強類型視圖

不幸的是,如果你有一個強類型視圖引用的模型存在於'bin'目錄之外,那麼你會很快發現它不起作用,它實際上不會告訴你原因。這是因為RazorViewEngine使用BuildManager將視圖編譯為動態程式集,然後使用Activator.CreateInstance來實例化新編譯的對象。這就是問題所在,當前AppDomain不知道如何解析強類型視圖的模型類型,因為它在“bin”目錄或GAC中找不到。關於這種情況更糟糕的部分是你沒有得到任何錯誤消息來告訴你為什麼這不起作用,或問題出在哪裡。相反,你得到了“友好”的MVC視圖未找到錯誤:“...或者找不到它的主人或沒有視圖引擎支持搜索的位置。搜索了以下位置:...。“告訴您它已在所有ViewEngine位置搜索了視圖,但找不到它......實際上根本的錯誤並不是它。在MVC3源代碼深處,它嘗試從動態程式集中實例化視圖對象,但它失敗了,所以它只是在其餘的ViewEngine路徑中繼續查找該視圖。

註:即使在MVC3有一個新的IViewPageActivator這應該是負責實例化已編譯的BuildManager的,實現自定義IViewPageActivator來處理這個問題仍然沒有效果的,因為調用IViewPageActivator之前,MVC3代碼庫就已經因為不能解析不在'bin'中的程式集而失敗。

 

Full trust

When working in Full Trust we have a few options for dealing with the above scenario:

  •  Use the AppDomain’s ResolveAssembly event
    •  By subscribing to this event, you are able to instruct the AppDomain where to look when it can’t find a reference to a Type.
    •  This is easily done by checking if your plugin assemblies match the assembly being searched for, and then returning the Assembly object if found:
static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
    var pluginsFolder = new DirectoryInfo(HostingEnvironment.MapPath("~/Plugins"));
    return (from f in pluginsFolder.GetFiles("*.dll", SearchOption.AllDirectories)
            let assemblyName = AssemblyName.GetAssemblyName(f.FullName)
            where assemblyName.FullName == args.Name || assemblyName.FullName.Split(',')[0] == args.Name
            select Assembly.LoadFile(f.FullName)).FirstOrDefault();
}
  •  Shadow copy your plugin DLLs into the AppDomain’s DynamicDirectory.
    •  This is the directory that the BuildManager compiles it’s dynamic assemblies into and is also a directory that the AppDomain looks to when resolving Type’s from Assemblies.
    •  You can shadow copy your plugin DLLs to this folder on app pre-init and everything ‘should just work’
  •  Replace the RazorViewEngine with a custom razor view engine that compiles views manually but makes references to the appropriate plugin DLLs
    •  I actually had this working in an Umbraco v5 prototype but it is hugely overkill and unnecessary plus you actually would have to replace the RazorViewEngine which is pretty absurd.

 

完全信任

Full Trust中工作時,我們有幾個選項來處理上述場景:

  •  使用AppDomain的ResolveAssembly事件
    •  通過訂閱此事件,您可以指示AppDomain在無法找到對Type的引用時查找的位置。
    •  通過檢查插件程式集是否與要搜索的程式集匹配,然後返回Assembly對象(如果找到),可以輕鬆完成此操作: 
static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
    var pluginsFolder = new DirectoryInfo(HostingEnvironment.MapPath("~/Plugins"));
    return (from f in pluginsFolder.GetFiles("*.dll", SearchOption.AllDirectories)
            let assemblyName = AssemblyName.GetAssemblyName(f.FullName)
            where assemblyName.FullName == args.Name || assemblyName.FullName.Split(',')[0] == args.Name
            select Assembly.LoadFile(f.FullName)).FirstOrDefault();
}
  •  將插件DLLs副本複製到AppDomain的DynamicDirectory中。
    •  這是BuildManager編譯動態程式集的目錄,也是AppDomain在從程式集中解析Type時所查找的目錄。
    •  你可以在應用pre-init上將你的插件DLLs副本複製到這個文件夾,一切都應該“正常”
  •  用自定義的Razor視圖引擎替換RazorViewEngine,該引擎手動編譯視圖但引用相應的插件DLLs
    •  我實際上是在Umbraco v5原型中使用它,但它殺傷力過大也其實沒那麼必要,再加上你實際上必須非常荒謬的更換RazorViewEngine。

 

The burden of Medium Trust

In the MVC world there’s only a couple hurdles to jump when loading in plugins from outside of the ‘bin’ folder in Full Trust. In Medium Trust however, things get interesting. Unfortunately in Medium Trust it is not possible to handle the AssemblyResolve event and it’s also not possible to access the DynamicDirectory of the AppDomain so the above two solutions get thrown out the window. Further to this it seems as though you can’t use CodeDom in Medium Trust to custom compile views.

 

中級信任的負擔

MVC世界中,當從Full Trust中的'bin'文件夾外部載入插件時只需要跳過幾個障礙就可以了。然而,在Medium Trust中,事情變得有趣。不幸的是,在Medium Trust中,無法處理AssemblyResolve事件,也無法訪問AppDomain的DynamicDirectory,因此上述兩個解決方案都會被拋出窗外。除此之外,似乎您無法在Medium Trust中使用CodeDom來自定義編譯視圖。

 

Previous attempts

For a while I began to think that this wasn’t possible and I thought I tried everything:

  •  Shadow copying DLLs from the plugins folder into the ‘bin’ folder on application pre-init
    •  This fails because even during app pre-init, the application pool will still recycle. Well, it doesn’t actually ‘fail’ unless you keep re-copying the DLL into the bin. If you check if it already exists and don’t copy into the bin than this solution will work for you but it’s hardly a ‘solution’ since you might as well just put all your DLLs into the ‘bin’ in the first place.
  •  Trying to use sub folders of the ‘bin’ folder to load plugins.
    •  Turns out that ASP.Net doesn’t by default load in DLLs that exist in sub folders of the bin, though from research it looks like standard .Net apps actually do.
    •  Another interesting point was that if you try to copy a DLL into a sub folder of the bin during application pre-init you get a funky error:  “Storage scopes cannot be created when _AppStart is executing”. It seems that ASP.Net is monitoring all changes in the bin folder regardless of whether or not they are in sub folders but still doesn’t load or reference those assemblies.

 

以前的嘗試

有一段時間我開始認為這是不可能的,我以為我嘗試了一切:

  •  DLL從插件文件夾中複製到應用程式pre-init上的“bin”文件夾中
    •  這失敗了,因為即使在app pre-init期間,應用程式池仍將回收迴圈使用。好吧,除非你不斷將DLL重新複製到bin中,否則它也不能說是徹底“失敗”。如果你檢查它是否已經存在,如果它已經存在並且不需要再複製到bin中,那麼這個解決方案對你有用,但它不是一個“解決方案”,因為你可能只是把所有的DLLs放在'bin'中。
  •  嘗試使用'bin'文件夾的子文件夾來載入插件。
    •  事實證明,ASP.Net預設情況下不會載入存儲在bin的子文件夾中的DLLs,儘管從研究看起來它看起來像標準.Net應用程式的做法。
    •  另一個有趣的觀點是,如果您嘗試在應用程式預初始化期間將DLL複製到bin的子文件夾中,則會出現一個時髦的錯誤:“_AppStart執行時無法創建存儲範圍”。似乎ASP.Net正在監視bin文件夾中的所有更改,無論它們是否在子文件夾中,但仍然不載入或引用這些程式集。

 

An easy solution

So, the easy solution is to just set a ‘privatePath’ on the ‘probing’ element in your web.config to tell the AppDomain to also look for Assemblies/Types in the specified folders. I did try this before when trying to load plugins from sub folders in the bin and couldn’t get it to work. I’m not sure if I was ‘doing it wrong’ but it definitely wasn’t working then, either that or attempting to set this in sub folders of the bin just doesn’t work.

<runtime>
  <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">       
    <probing privatePath="Plugins/temp" />

 

一個簡單的解決方案

因此,簡單的解決方案是在web.config中的'probing'配置節上設置'privatePath'屬性,以告知AppDomain還可以在指定的文件夾中查找Assemblies/Types。我嘗試從bin中的子文件夾載入插件,之前嘗試過這個,但無法使其工作。我不確定是否“做錯了”但它肯定不起作用,或者試圖在bin的子文件夾中設置它只是不起作用。

<runtime>
  <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">       
    <probing privatePath="Plugins/temp" />

 

DLL file locking

Since plugin DLLs get locked by the CLR when they are loaded, we need to work around this. The solution is to shadow copy the DLLs to another folder on application pre-init. As mentioned previously, this is one of the ways to get plugins loaded in Full Trust and in my opinion is the nicest way to do it since it kills 2 birds with one stone. In Medium Trust however, we’ll have to jump through some hoops and shadow copy the DLLs to a temp folder that exists within the web application. IMPORTANT: When you’re copying DLLs you might be tempted to modify the name of the DLL by adding a version number or similar, but this will NOT work and you’ll get a “The located assembly's manifest definition … does not match the assembly reference.” exception.

 

DLL文件鎖的問題

由於插件DLLs在載入時被CLR鎖定,我們需要解決這個問題。解決方案是在應用程式pre-init上將DLLs副本複製到另一個文件夾。如前所述,這是在Full Trust中載入插件的方法之一,在我看來,這是最好的方法,因為它可以一石二鳥。但是,在Medium Trust中,我們必須跳過一些環節並將DLLs複製到Web應用程式中存在的臨時文件夾。重要提示:當您複製DLL時,您可能想通過添加版本號或類似名稱來修改DLL的名稱,但這不起作用,您將獲得“找到的程式集的清單定義...與程式集引用不匹配。”異常。

 

Solution

UPDATE: The latest version of this code can be found in the Umbraco v5 source code. The following code does work but there’s been a lot of enhancements to it in the Umbraco core. Here’s the latest changeset as of 16/16/2012 Umbraco v5 PluginManager.cs

Working in Full Trust, the simplest solution is to shadow copy your plugin DLLs into your AppDomain DynamicDirectory. Working in Medium Trust you’ll need to do the following:

  •  On application pre-init:
    •  Shadow copy all of your plugin DLLs to a temporary folder in your web application (not in the ‘bin’)
    •  Add all of the copied DLLs to be referenced by the BuildManager
  •  Add all folder paths to the privatePath attribute of the probing element in your web.config to point to where you will be copying your DLLs
    •  If you have more than one, you need to semi-colon separate them

Thanks to Glenn Block @ Microsoft who gave me a few suggestions regarding DLL file locking with MEF, Assembly load contexts and probing paths! You put me back on track after I had pretty much given up.

Here’s the code to do the shadow copying and providing the Assemblies to the BuildManager on application pre-init (make sure you set the privatePath on the probing element in your web.config first!!)

 

解決方案

更新:此代碼的最新版本可以在Umbraco v5源代碼中找到。以下代碼確實有效,但在Umbraco核心中有很多增強功能。這是截至16/16/2012 Umbraco v5 PluginManager.cs的最新變更集

使用Full Trust,最簡單的解決方案是將插件DLLs副本拷貝到AppDomain DynamicDirectory中。

使用Medium Trust,您需要執行以下操作:

  •  在應用程式pre-init上:
    •  將所有插件DLLs副本拷貝到Web應用程式中的臨時文件夾(不要在“bin”目錄中)
    •  BuildManager引用所有插件DLLs的副本
  •  將所有文件夾路徑添加到web.config中probing配置節的privatePath屬性,以指向插件DLLs副本的存儲位置
    •  如果你有多個,需要用分號分開它們

感謝Glenn Block @ Microsoft,他給了我一些關於使用MEF,裝配載入上下文和probing路徑鎖定DLL文件的建議!在我幾乎放棄之後,你讓我重回正軌。

這是在應用程式pre-init上進行副本拷貝並將程式集提供給BuildManager的代碼(確保首先在web.config中的probing配置節上設置privatePath !!)

 

using System.Linq;
using System.Web;
using System.IO;
using System.Web.Hosting;
using System.Web.Compilation;
using System.Reflection;

[assembly: PreApplicationStartMethod(typeof(PluginFramework.Plugins.PreApplicationInit), "Initialize")]

namespace PluginFramework.Plugins
{
    public class PreApplicationInit
    {

        static PreApplicationInit()
        {
            PluginFolder = new DirectoryInfo(HostingEnvironment.MapPath("~/plugins"));
            ShadowCopyFolder = new DirectoryInfo(HostingEnvironment.MapPath("~/plugins/temp"));
        }

        /// <summary>
        /// The source plugin folder from which to shadow copy from
        /// </summary>
        /// <remarks>
        /// This folder can contain sub folderst to organize plugin types
        /// </remarks>
        private static readonly DirectoryInfo PluginFolder;

        /// <summary>
        /// The folder to shadow copy the plugin DLLs to use for running the app
        /// </summary>
        private static readonly DirectoryInfo ShadowCopyFolder;

        public static void Initialize()
        {            
            Directory.CreateDirectory(ShadowCopyFolder.FullName);

            //clear out plugins)
            foreach (var f in ShadowCopyFolder.GetFiles("*.dll", SearchOption.AllDirectories))
            {
                f.Delete();
            }            

            //shadow copy files
            foreach (var plug in PluginFolder.GetFiles("*.dll", SearchOption.AllDirectories))
            {
                var di = Directory.CreateDirectory(Path.Combine(ShadowCopyFolder.FullName, plug.Directory.Name));
                // NOTE: You cannot rename the plugin DLL to a different name, it will fail because the assembly name is part if it's manifest
                // (a reference to how assemblies are loaded: http://msdn.microsoft.com/en-us/library/yx7xezcf )
                File.Copy(plug.FullName, Path.Combine(di.FullName, plug.Name), true);
            }
            
            // Now, we need to tell the BuildManager that our plugin DLLs exists and to reference them.
            // There are different Assembly Load Contexts that we need to take into account which 
            // are defined in this article here:
            // http://blogs.msdn.com/b/suzcook/archive/2003/05/29/57143.aspx

            // * This will put the plugin assemblies in the 'Load' context
            // This works but requires a 'probing' folder be defined in the web.config
            foreach (var a in
                ShadowCopyFolder
                .GetFiles("*.dll", SearchOption.AllDirectories)
                .Select(x => AssemblyName.GetAssemblyName(x.FullName))
                .Select(x => Assembly.Load(x.FullName)))
            {
                BuildManager.AddReferencedAssembly(a);
            }

            // * This will put the plugin assemblies in the 'LoadFrom' context
            // This works but requires a 'probing' folder be defined in the web.config
            // This is the slowest and most error prone version of the Load contexts.            
            //foreach (var a in
            //    ShadowCopyFolder
            //    .GetFiles("*.dll", SearchOption.AllDirectories)
            //    .Select(plug => Assembly.LoadFrom(plug.FullName)))
            //{
            //    BuildManager.AddReferencedAssembly(a);
            //}

            // * This will put the plugin assemblies in the 'Neither' context ( i think )
            // This nearly works but fails during view compilation.
            // This DOES work for resolving controllers but during view compilation which is done with the RazorViewEngine, 
            // the CodeDom building doesn't reference the plugin assemblies directly.
            //foreach (var a in
            //    ShadowCopyFolder
            //    .GetFiles("*.dll", SearchOption.AllDirectories)
            //    .Select(plug => Assembly.Load(File.ReadAllBytes(plug.FullName))))
            //{
            //    BuildManager.AddReferencedAssembly(a);
            //}

        }
    }
}

 


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

-Advertisement-
Play Games
更多相關文章
  • 一、HTTP路由 所有路由都定義在 App\Providers\RouteServiceProvider 類載入的 app/Http/routes.php文件中。 1. 基本路由 簡單的 Laravel 路由只接受一個 URI 和一個閉包 1 2 3 Route::get('foo', functi ...
  • 一、關鍵字 1.final關鍵字 用final修飾的類:不能被繼承 用final修飾的方法:不能被重寫 註意:(1)在Scala中變數不需要用final修飾,因為val與var已經限制了變數是否可變 (2)final不能修飾特質,但是可以修飾抽象類 如圖代碼,Pig繼承特質Animal後使用“ctr ...
  • Django 系列博客(十二) 前言 本篇博客繼續介紹 Django 中的查詢,分別為聚合查詢和分組查詢,以及 F 和 Q 查詢。 聚合查詢 語法: aggregate()是 QuerySet 的一個終止子句,意思是說,它返回一個包含一些鍵值對的字典。鍵的名稱是聚合值得標識符,值是計算出來的聚合值。 ...
  • 前提:1.maven工程一個 2.avi視頻一個 第一步: 通過pom.xml導入jave相關包 第二步: 示例: ...
  • 1,從要破解的程式中拷貝.jar包文件,運行1_jar.sh將其解壓。以jira7.9.2為例: 2,運行2_jad.sh將.class文件反編譯為.java文件。 3,運行3_hack.sh將需要破解的.java文件拷貝到工程中,註意package路徑。jira需要破解的文件為: 4,修改控制li ...
  • 給 寫一個 擴展方法 Intro 在 .net core 中,微軟已經預設使用 appsettings.json 來代替 app.config,並重新設計了一套完整的配置系統,可以支持 json/xml/ini/環境變數等。 在 .net core 中有一個 的擴展方法用來比較方便的獲取鏈接字元串, ...
  • Java環境準備 可以下載oracle最新的JDK,作為C 程式員,支持一下微軟的Mobile OpenJDK,構建一下Java環境。 微軟的OpenJDK是針對Xamarin.Android的SDK,在我嘗試之前不確定是否適用ElasticSearch。 結論是:沒問題。 Microsoft 分發 ...
  • <?xml version="1.0" encoding="utf-8" ?> <configuration> <connectionStrings> <add name="strCon" connectionString="server=127.0.0.1;user id=sa; password ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...