ExtCore框架:ASP.NET 5的模块化CMS解决方案

在开发基于ASP.NET 5的跨平台CMS系统时,一个挑战是如何实现项目的模块化结构。希望项目能够通过简单地复制文件到扩展文件夹来添加或移除扩展。扩展可能包含控制器、视图模型、视图、存储模型和仓库等。为了解决这个问题,决定将这个功能提取到一个单独的ExtCore框架项目中,以便在其他项目中重用。

什么是ExtCore框架

ExtCore框架由两个必需的NuGet包组成:

  • ExtCore.WebApplication
  • ExtCore.Infrastructure

ExtCore.WebApplication包含用于发现和加载扩展、控制器、视图、资源等的类。ExtCore.Infrastructure定义了一个扩展,所有扩展必须实现IExtension接口才能被发现。此外,ExtCore还有一个可选的扩展用于存储(目前只支持SQLite和MS SQL Server,但添加其他存储支持非常简单)。

还有五个更多的NuGet包:

  • ExtCore.Data
  • ExtCore.Data.Models.Abstractions
  • ExtCore.Data.Abstractions
  • ExtCore.Data.EntityFramework.Sqlite
  • ExtCore.Data.EntityFramework.SqlServer

ExtCore.Data.Models.Abstractions定义了一个存储实体,所有实体必须实现IEntity接口。ExtCore.Data.Abstractions描述了基本的存储上下文、存储和仓库,以及IStorageContext、IStorage和IRepository接口。ExtCore.Data.EntityFramework.Sqlite和ExtCore.Data.EntityFramework.SqlServer实现了IStorageContext、IStorage和IRepository接口,适用于相应的存储。IStorage接口的实现将搜索所有程序集以找到给定的IRepository实现。如果找到了实现,它将实例化一个仓库实例并返回。

ExtCore.Data是ExtCore.Data扩展的主要部分。它包含了IExtension的实现,自动搜索可用的IStorage实现,并使用内置的ASP.NET 5DI注入。因此,每个控制器都能够获取IStorage实现的实例来与存储交互。

扩展结构的更多信息

当创建自己的扩展时,可能(而且应该,如果想拥有统一的架构),遵循下一个扩展结构(其中X是扩展的名称):

YourApplication.X YourApplication.X.Data.Models YourApplication.X.Data.Abstractions YourApplication.X.Data.SpecificStorageA YourApplication.X.Data.SpecificStorageB YourApplication.X.Data.SpecificStorageC YourApplication.X.Frontend YourApplication.X.Backend 等等。

正如看到的,这个结构与ExtCore.Data扩展的结构非常相似,但也可以在这里看到一些YourApplication.X.Frontend和YourApplication.X.Backend。YourApplication.X扩展的这些部分包含了它的UI(控制器、视图、js、CSS等)用于前端和后端(但可以在应用程序中有不同层次或层次)。

重要的是要知道如何存储视图和静态资源(如js、CSS、图片等),以及如何稍后使用它们。

有两种存储视图的选项:

  • 可以将视图存储为编译后的资源。在这种情况下,不能有强类型的视图,如果类型在主Web应用程序没有依赖的任何东西中定义。换句话说,只能使用标准类型如string或IEnumerable作为视图模型,否则在运行时编译视图将不可能。
  • 可以将视图存储为预编译的。在这种情况下,可以使用任何现有的类型作为视图模型。此外,它不会在运行时编译视图。这是首选选项。

存储静态资源只有一个选项。它是通过添加类似下面的内容到project.json中,将静态资源编译为程序集资源:

"resource": "Your/Static/Content/Path/**"

之后,可以使用像这样的URL通过HTTP使用ExtCore获取资源:

/resource?name=your.static.content.path.someimagename.png

(在未来的版本中,将使其能够通过名称像常规文件一样获取资源)。

它是如何工作的

请查看ExtCore.WebApplication.Startup类。首先,在ConfigureServices方法中,从应用程序的扩展文件夹加载所有程序集:

IEnumerable assemblies = AssemblyManager.LoadAssemblies( this.applicationBasePath.Substring( 0, this.applicationBasePath.LastIndexOf("src")) + "artifacts\\bin\\Extensions", this.assemblyLoaderContainer, this.assemblyLoadContextAccessor );

程序集加载后,将它们存储到全局缓存中:

ExtensionManager.SetAssemblies(assemblies);

现在使用ExtensionManager类,可以从任何地方访问可用程序集和扩展的数组,所以所有扩展都可以彼此了解。

接下来,必须允许Razor解析存储在扩展中的视图。正如上面描述的,有两种存储扩展中视图的选项。ExtCore支持它们两者。

这是包括编译为资源的视图:

.AddPrecompiledRazorViews(ExtensionManager.Assemblies.ToArray());

这是包括预编译视图:

services.Configure(options => { options.FileProvider = this.GetFileProvider(this.applicationBasePath); });

之后,必须调用所有扩展的SetConfigurationRoot和ConfigureServices方法:

foreach (IExtension extension in ExtensionManager.Extensions) { extension.SetConfigurationRoot(this.configurationRoot); extension.ConfigureServices(services); }

最后,应该告诉MVC如何在扩展中发现控制器:

services.AddTransient(); services.AddTransient(); ExtensionAssemblyProvider将复制DefaultAssemblyProvider找到的所有程序集,并添加存储在ExtensionManager中的程序集。

在Configure方法中,调用所有扩展的Configure和RegisterRoutes方法:

foreach (IExtension extension in ExtensionManager.Extensions) extension.Configure(applicationBuilder); applicationBuilder.UseMvc(routeBuilder => { routeBuilder.MapRoute(name: "Resource", template: "resource", defaults: new { controller = "Resource", action = "Index" }); foreach (IExtension extension in ExtensionManager.Extensions) extension.RegisterRoutes(routeBuilder); });

如何使用

要使用ExtCore,只需将ExtCore.WebApplication的引用添加到主应用程序的project.json中,使主应用程序的Startup类继承自ExtCore.WebApplication.Startup,并将ExtCore.Infrastructure的引用添加到扩展的project.json中。之后,可以将扩展的DLL文件放到/artifacts/bin/extensions文件夹中,它将在下次应用程序启动时自动被发现。

准备了一个示例应用程序,可以使用:

。它包含2个扩展:

  • 扩展A。它展示了如何将视图添加到程序集作为资源(并且,它还展示了主Web应用程序将找到这些视图)。此外,它还展示了如何获取并显示所有可用扩展的名称。
  • 扩展B。它展示了如何使用预编译的强类型视图与扩展内定义的自定义视图模型类。此外,它还展示了如何与存储(在这种情况下是Sqlite)一起工作。

还可以下载准备好的示例项目。它包含从Visual Studio 2015运行ExtCore-based Web应用程序所需的一切,包括带有测试数据的SQLite数据库。

已知问题

不同的AspNet5项目使用不同的System.Xxx版本,决定在所有项目中将Microsoft.AspNet.Mvc作为依赖项,以便在所有项目中拥有相同的依赖集,但这是错误的。所以Microsoft.AspNet.Mvc应该被替换为,例如,System.Linq等的某个版本,但在这种情况下,因为不同项目中System.Xxx的不同版本而得到了编译错误。稍后会修复这个问题,并感谢任何帮助。

因为主Web应用程序没有一些模块需要的依赖项,不得不将System.Reflection.dll和System.Reflection.TypeExtensions.dll放到扩展文件夹中。真的不喜欢它,必须解决它。

沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485