深入解析ASP.NET Core MVC的模块化设计[下篇]
ASP.NET Core MVC的“模块化”设计使我们可以构成应用的基本单元Controller定义在任意的模块(程序集)中,并在运行时动态加载和卸载。《设计篇》介绍了这种为“飞行中的飞机加油”的方案的实现原理?本篇我们将演示将介绍“分散定义Controller”的N种实现方案。源代码从这里下载。
一、标注ApplicationPartAttribute特性
二、标注RelatedAssemblyAttribute特性
三、注册ApplicationPartManager
四、添加ApplicationPart到现有ApplicationPartManager
一、标注ApplicationPartAttribute特性
接下来我们就通过几个简单的实例来演示如何将Controller类型定义在非入口应用所在的项目中。我们创建如图1所示的解决方案,其中App是一个MVC应用类型的项目,而Foo则是一个普通的类库项目,App具有针对Foo的项目引用。我们希望将部分Controller类型定义在Foo这个类库项目中。

图1 将部分Controller类型定义在Foo项目中
我们在App项目中定义了如下这个HomeController。如代码片段所示,我们在构造函数中注入了ApplicationPartManager对象,并利用它得到当前应用范围内所有有效Controller类型。在执行应用根路径的Action方法Index中,我们将得到的有效Controller类型名称呈现出来。如下所示的FooController类型是我们在Foo项目中定义的Controller类型。
public class HomeController : Controller
{
private readonly IEnumerable<Type> _controllerTypes;
public HomeController(ApplicationPartManager manager)
{
var feature = new ControllerFeature();
manager.PopulateFeature(feature);
_controllerTypes = feature.Controllers;
} [HttpGet("/")]
public string Index()
{
var lines = _controllerTypes.Select(it => it.Name);
return string.Join(Environment.NewLine, lines.ToArray());
}
}
public class FooController
{
public void Index() => throw new NotImplementedException();
}
在启动这个演示程序后,如果利用浏览器通过根路径访问定义在HomeController类型中的Action方法Index,我们会得到如图2所示的输出结果。从输出结果可以看出,定义在非MVC应用项目Foo中的Controller类型在默认情况下是不会被解析的。

图2 默认只解析MVC应用所在项目定义的Controller
如果希望MVC应用在进行Controller类型解析的时候将项目Foo编译后的程序集(默认为Foo.dll)包括进来,我们可以在应用所在项目中标注ApplicationPartAttribute特性将程序集Foo作为应用的组成部分。所以我们在Program.cs中针对ApplicationPartAttribute特性进行了如下的标记。
[assembly:ApplicationPart("Foo")]
修改后的程序集启动之后,再次利用浏览器按照按照相同的路径对它发起请求,我们将得到如图3所示的输出结果。由于程序集Foo成为了当前应用的有效组成部分,定义在它里面的BarController自然也成为了当前应用有效的Controller类型。

图3 解析ApplicationPartAttribute特性指向程序集中的Controller类型
二、标注RelatedAssemblyAttribute特性
除了在入口程序集上标注ApplicationPartAttribute特性将某个程序集作为当前应用的有效组成部分之外,我们也可以通过标注RelatedAssemblyAttribute达到相同的目的。根据前面的介绍,我们知道RelatedAssemblyAttribute特性只能标注到入口程序集或者ApplicationPartAttribute特性指向的程序集中,所以我们可以将RelatedAssemblyAttribute特性标注到Foo项目中将另一个程序集包含进行。为此我们在解决方案中添加了另一个类库项目Bar(如图4所示),并为App添加针对Bar的项目引用,然后在Bar项目中定义一个类似于FooController的BarController类型。

图4 将部分Controller类型定义在Foo和Bar项目中
为了将项目Bar编译后生成的程序集(默认为Bar.dll)作为当前应用的组成部分,我们可以选择在App或者Foo项目中标注一个指向它的RelatedAssemblyAttribute特性。对于我们演示的实例来说,我们选择在FooController.cs中以如下形式标注一个指向程序集Bar的RelatedAssemblyAttribute特性。
[assembly: RelatedAssembly("Bar")]
修改后的程序集启动之后,再次利用浏览器按照按照相同的路径对它发起请求,我们将得到如图5所示的输出结果。由于程序集Bar成为了当前应用的有效组成部分,定义在它里面的BazController自然也成为了当前应用有效的Controller类型。

图5 解析RelatedAssemblyAttribute特性指向程序集中的Controller类型
三、注册ApplicationPartManager
由于针对有效Controller类型的解析是利用注册的ApplicationPartManager对象实现的,所以我们完全可以通过注册一个ApplicationPartManager对象的方式达到相同的目的。接下来我们将上一个演示实例中标注的ApplicationPartAttribute和RelatedAssemblyAttribute特性删除,并将承载程序修改为如下的形式。
var manager = new ApplicationPartManager();
var entry = Assembly.GetEntryAssembly()!;
var foo = Assembly.Load(new AssemblyName("Foo"));
var bar = Assembly.Load(new AssemblyName("Bar")); manager.ApplicationParts.Add(new AssemblyPart(entry));
manager.ApplicationParts.Add(new AssemblyPart(foo));
manager.ApplicationParts.Add(new AssemblyPart(bar));
manager.FeatureProviders.Add(new ControllerFeatureProvider()); var builder = WebApplication.CreateBuilder(args);
builder.Services
.AddSingleton(manager)
.AddControllers();
var app = builder.Build();
app.MapControllers();
app.Run();
如上面的代码片段所示,我们创建了一个ApplicationPartManager对象,并在其ApplicationParts属性中显式添加了指向入口程序集以及Foo和Bar程序集的AssemblyPart对象。为了能够让这个ApplicationPartManager对象具有解析Controller类型的能力,我们在其FeatureProviders中添加了一个ControllerFeatureProvider对象。在后续的应用承载程序中,我们将这个ApplicationPartManager对象作为服务注册到依赖注入框架中。修改后的程序集启动之后,再次利用浏览器按照按照相同的路径对它发起请求,我们依然会得到如图5所示的输出结果。
四、添加ApplicationPart到现有ApplicationPartManager
其实我们没有必要注册一个新的,按照如下的方式将Foo、Bar程序集转换成AssemblyPart并将其添加到现有的ApplicationPartManager之中也可以达到相同的目的。
var builder = WebApplication.CreateBuilder(args);
builder.Services
.AddControllers()
.AddApplicationPart(Assembly.Load(new AssemblyName("Foo")))
.AddApplicationPart(Assembly.Load(new AssemblyName("Bar")));
var app = builder.Build();
app.MapControllers();
app.Run();
深入解析ASP.NET Core MVC的模块化设计[下篇]的更多相关文章
- 通过极简模拟框架让你了解ASP.NET Core MVC框架的设计与实现[上篇]
<200行代码,7个对象--让你了解ASP.NET Core框架的本质>让很多读者对ASP.NET Core管道有了真实的了解.在过去很长一段时间中,有很多人私信给我:能否按照相同的方式分 ...
- [ASP.NET Core MVC] 如何实现运行时动态定义Controller类型?
昨天有个朋友在微信上问我一个问题:他希望通过动态脚本的形式实现对ASP.NET Core MVC应用的扩展,比如在程序运行过程中上传一段C#脚本将其中定义的Controller类型注册到应用中,问我是 ...
- asp.net core mvc权限控制:分配权限
前面的文章介绍了如何进行权限控制,即访问控制器或者方法的时候,要求当前用户必须具备特定的权限,但是如何在程序中进行权限的分配呢?下面就介绍下如何利用Microsoft.AspNetCore.Ident ...
- ASP.NET Core MVC之Serilog日志处理,你了解多少?
前言 本节我们来看看ASP.NET Core MVC中比较常用的功能,对于导入和导出目前仍在探索中,项目需要自定义列合并,所以事先探索了如何在ASP.NET Core MVC进行导入.导出,更高级的内 ...
- ASP.NET Core MVC之ViewComponents(视图组件)
前言 大概一个来星期未更新博客了,久违了各位,关于SQL Server性能优化会和ASP.NET Core MVC穿插来讲,如果你希望我分享哪些内容可以在评论下方提出来,我会筛选并看看技术文档来对你的 ...
- ASP.NET Core MVC 源码学习:MVC 启动流程详解
前言 在 上一篇 文章中,我们学习了 ASP.NET Core MVC 的路由模块,那么在本篇文章中,主要是对 ASP.NET Core MVC 启动流程的一个学习. ASP.NET Core 是新一 ...
- 你想要的都在这里,ASP.NET Core MVC四种枚举绑定方式
前言 本节我们来讲讲在ASP.NET Core MVC又为我们提供了哪些方便,之前我们探讨过在ASP.NET MVC中下拉框绑定方式,这节我们来再来重点看看枚举绑定的方式,充分实现你所能想到的场景,满 ...
- ASP.NET Core MVC 模型绑定用法及原理
前言 查询了一下关于 MVC 中的模型绑定,大部分都是关于如何使用的,以及模型绑定过程中的一些用法和概念,很少有关于模型绑定的内部机制实现的文章,本文就来讲解一下在 ASP.NET Core MVC ...
- ASP.NET Core MVC 控制器创建与依赖注入
本文翻译自<Controller activation and dependency injection in ASP.NET Core MVC>,由于水平有限,故无法保证翻译完全准确,欢 ...
- 008.Adding a model to an ASP.NET Core MVC app --【在 asp.net core mvc 中添加一个model (模型)】
Adding a model to an ASP.NET Core MVC app在 asp.net core mvc 中添加一个model (模型)2017-3-30 8 分钟阅读时长 本文内容1. ...
随机推荐
- vm-storage在全部都是旧metric情况下的写入性能测试
作者:张富春(ahfuzhang),转载时请注明作者和引用链接,谢谢! cnblogs博客 zhihu Github 公众号:一本正经的瞎扯 接上篇:测试所有metric都是存在过的metric的情况 ...
- Go实现网络代理
使用 Go 语言开发网络代理服务可以通过以下步骤完成.这里,我们将使用 golang.org/x/net/proxy 包来创建一个简单的 SOCKS5 代理服务作为示例. 步骤 1. 安装 golan ...
- MySQL【三】---数据库查询详细教程{分页、连接查询、自关联、子查询、数据库设计规范}
1.分页 limit start count limit限制查询出来的数据个数,limit在语句最后 查找两个女性 select * from student where gender=1 limit ...
- Swift中发布-订阅框架Combine的使用
Combine简介 Combine是一个苹果用来处理事件的新的响应式框架,支持iOS 13及以上版本. 你可以使用Combine去统一和简化在处理类似于target-action,delegate,k ...
- 戴尔全球首款6K IPS Black显示器上市:配4K摄像头
戴尔的全球首款6K IPS Black显示器--U3224KB,目前已经上架,价格为3199.99美元(约合人民币22186元). 据介绍,这款显示器采用IPS Black面板,刷新率为60Hz,对比 ...
- 使用DoraCloud免费版搭建办公桌面云
DoraCloud是一款多平台的桌面虚拟化管理软件,支持Hyper-V.VMware.Proxmox.XenServer等多种虚拟化平台.DoraCloud在虚拟化平台上具有极大的灵活性,允许您的组织 ...
- electron 安装 base64
1.安装这个:https://www.npmjs.com/package/js-base64 2.安装ts:https://www.npmjs.com/package/@types/js-base64
- 从零开始的react入门教程(五),了解react中的表单,何为受控组件与非受控组件
壹 ❀ 引 我们在从零开始的react入门教程(四),了解常用的条件渲染.列表渲染与独一无二的key一文中介绍了react中常用的条件渲染操作,比如三元运算符,逻辑运算符等,结合react组件或者re ...
- low-code 低代码平台 java 代码自动一键生成工具
low-code low-code 是一款为 java 打造的低代码平台. 开源地址:https://github.com/houbb/low-code 特性 支持基本的增删改查 支持枚举值处理 支持 ...
- 【Unity3D】Unity与Android交互
1 前言 本文主要介绍 Unity 打包发布 Android apk 流程.基于 AndroidJavaObject(或 AndroidJavaClass)实现 Unity 调用 Java 代码. ...