目前在學習ASP.NET CORE MVC中,今天看到微軟在ASP.NET CORE MVC 2.0中又恢復了允許開發人員引用第三方DLL程序集的功能,感到甚是高興!於是我急忙寫了個Demo想試試,我的項目結構如下:
可以看到解決方案中就兩個項目,AspNetCoreWebApp就是一個ASP.NET CORE MVC 2.0的項目,而MyNetCoreLib是一個.Net Core 2.0的類庫項目,為了體現AspNetCoreWebApp是通過程序集來引用MyNetCoreLib的,我還在解決方案中創建了一個文件夾叫Reference,將類庫項目MyNetCoreLib編譯后生成的DLL文件放到了Reference文件夾中,然后在AspNetCoreWebApp中通過添加引用程序集的方式引用了MyNetCoreLib.dll,如下圖所示:
然后編譯整個解決方案,調試AspNetCoreWebApp這個項目,運行立馬報錯。。。錯誤如下:
這明顯是一個運行時錯誤,因我在編譯整個解決方案的時候是成功的,沒有報任何錯誤。后來去網上查了查資料,發現雖然我們在項目AspNetCoreWebApp中引用了MyNetCoreLib.dll,而且項目AspNetCoreWebApp編譯后也在其Bin目錄下輸出了MyNetCoreLib.dll這個文件,如下圖所示:
但是ASP.NET CORE MVC的依賴注入環境其實並不知道該到哪里去找MyNetCoreLib.dll這個文件,所以才會在運行時報出InvalidOperationException: Cannot find compilation library location for package 'MyNetCoreLib'這種錯誤。。。開發人員需要用代碼去告訴ASP.NET CORE MVC應該到哪里去找到MyNetCoreLib.dll這個文件。
因此首先我們需要定義一個叫MetadataReferenceFeatureProvider的類,代碼如下,其關鍵代碼就是告訴ASP.NET CORE MVC的依賴注入環境去AppDomain.CurrentDomain.BaseDirectory(也就是Bin目錄)下找我們在項目中引用的程序集文件
using Microsoft.AspNetCore.Mvc.ApplicationParts; using Microsoft.AspNetCore.Mvc.Razor.Compilation; using Microsoft.CodeAnalysis; using Microsoft.Extensions.DependencyModel; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection.PortableExecutable; using System.Threading.Tasks; namespace AspNetCoreWebApp.Utils { public class ReferencesMetadataReferenceFeatureProvider : IApplicationFeatureProvider<MetadataReferenceFeature> { public void PopulateFeature(IEnumerable<ApplicationPart> parts, MetadataReferenceFeature feature) { var libraryPaths = new HashSet<string>(StringComparer.OrdinalIgnoreCase); foreach (var assemblyPart in parts.OfType<AssemblyPart>()) { var dependencyContext = DependencyContext.Load(assemblyPart.Assembly); if (dependencyContext != null) { foreach (var library in dependencyContext.CompileLibraries) { if (string.Equals("reference", library.Type, StringComparison.OrdinalIgnoreCase)) { foreach (var libraryAssembly in library.Assemblies) { //告訴ASP.NET CORE MVC如果現在項目中有引用第三方程序集,要到AppDomain.CurrentDomain.BaseDirectory這個文件夾(就是Bin目錄)下去尋找該程序集的dll文件 libraryPaths.Add(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, libraryAssembly)); } } else { foreach (var path in library.ResolveReferencePaths()) { libraryPaths.Add(path); } } } } else { libraryPaths.Add(assemblyPart.Assembly.Location); } } foreach (var path in libraryPaths) { feature.MetadataReferences.Add(CreateMetadataReference(path)); } } private static MetadataReference CreateMetadataReference(string path) { using (var stream = File.OpenRead(path)) { var moduleMetadata = ModuleMetadata.CreateFromStream(stream, PEStreamOptions.PrefetchMetadata); var assemblyMetadata = AssemblyMetadata.Create(moduleMetadata); return assemblyMetadata.GetReference(filePath: path); } } } }
其次我們還要在項目AspNetCoreWebApp的Startup.cs文件中的services.AddMvc()方法上注冊我們定義的這個Provider,代碼如下(注意ConfigureServices方法中的代碼):
using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.AspNetCore.Mvc.Razor.Compilation; using AspNetCoreWebApp.Utils; namespace AspNetCoreWebApp { public class Startup { public Startup(IConfiguration configuration) { Configuration = configuration; } public IConfiguration Configuration { get; } // This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { services.AddMvc().ConfigureApplicationPartManager(manager => { //移除ASP.NET CORE MVC管理器中默認內置的MetadataReferenceFeatureProvider,該Provider如果不移除,還是會引發InvalidOperationException: Cannot find compilation library location for package 'MyNetCoreLib'這個錯誤 manager.FeatureProviders.Remove(manager.FeatureProviders.First(f => f is MetadataReferenceFeatureProvider)); //注冊我們定義的ReferencesMetadataReferenceFeatureProvider到ASP.NET CORE MVC管理器來代替上面移除的MetadataReferenceFeatureProvider manager.FeatureProviders.Add(new ReferencesMetadataReferenceFeatureProvider()); }); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IHostingEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); app.UseBrowserLink(); } else { app.UseExceptionHandler("/Home/Error"); } app.UseStaticFiles(); app.UseMvc(routes => { routes.MapRoute( name: "default", template: "{controller=Home}/{action=Index}/{id?}"); }); } } }
然后重新編譯代碼,調試項目AspNetCoreWebApp,好了這下項目成功運行了,沒有報任何錯誤。
也不知道本文討論的這個問題是ASP.NET CORE MVC 2.0的一個缺陷,會在以后版本中修復,還是微軟故意為之?因為我試了下在.NET CORE 2.0的控制台項目中,直接引用第三方程序集DLL文件是完全沒問題的,不需要寫任何額外的代碼就可以使用。既然微軟在ASP.NET CORE MVC中也開放了引用第三方程序集這個功能,其實就完全可以把它做的和老.Net Framework一樣,自動去Bin目錄下面尋找DLL文件即可,希望ASP.NET CORE MVC以后的版本能夠完善這個功能,不再需要開發人員在引用DLL文件后,還要添加額外的代碼。