问题描述
在 MVC 6 RC1 中,我们使用 IAssemlbyProvider
接口来注册在运行时发现的程序集并在类似的 这篇文章的时尚.. 现在随着 RC2 版本的发布,IAssemblyProvider
已被删除并更改为(参见参考资料).
我们的框架版本目前是net46
.
自升级以来,我们在外部程序集中(未引用)的控制器正在返回 404
状态.
我们已经尝试通过 ApplicationPartManager
手动将控制器添加到已注册的控制器中.
var mvcBuilder = services.AddMvc();var controllerFeature = new ControllerFeature();mvcBuilder.PartManager.PopulateFeature(controllerFeature);var moduleControllers = ModulesManager.GetControllers();foreach(模块控制器中的 var c)controllerFeature.Controllers.Add(c);mvcBuilder.PartManager.PopulateFeature(controllerFeature);
还有……
services.AddMvc().ConfigureApplicationPartManager(app =>{var controllerFeature = new ControllerFeature();app.PopulateFeature(controllerFeature);var moduleControllers = ModulesManager.GetControllers();foreach(模块控制器中的 var c)controllerFeature.Controllers.Add(c);app.PopulateFeature(controllerFeature);});
现在程序集肯定会加载到 AppDomain
中,因为我们的依赖注入系统正在为外部程序集中的其他项查找和填充服务.
在我们之前的实现中,使用 IAssemblyProvider
效果很好.
公共类 ModuleAwareAssemblyProvider : IAssemblyProvider{私有只读 DefaultAssemblyProvider _defaultProvider;公共 ModuleAwareAssemblyProvider(DefaultAssemblyProvider defaultProvider){_defaultProvider = defaultProvider;}public IEnumerable<Assembly>候选人大会{得到{返回 _defaultProvider.CandidateAssemblies.Concat(ModulesManager.Assemblies).Distinct();}}}
我知道 RC2 仍然相对较新,但如果有人有在启动时注册额外控制器的经验会有所帮助.
干杯,尼科
花了一些时间直接使用 ControllerFeature
没有结果,是时候回到基础了.
基本上在应用程序启动时,控制器被注册到控制器功能容器不是来自控制器功能.这是关键,因为您需要注册控制器.
我正在浏览 RC2 的 GitHub 存储库,发现了 ControllerFeatureProvider
.如前所述.
从 <see cref="ApplicationPart"/> 列表中发现控制器
然后有一个方法进一步向下到 PopulateFeature
,我们可以看到它抓取注册到应用程序的所有部分并提取控制器接口(IsController()
方法值得一看).
///<inheritdoc/>公共无效填充特征(IEnumerable<ApplicationPart>部分,控制器功能特性){foreach (var part in parts.OfType<IApplicationPartTypeProvider>()){foreach(part.Types 中的 var 类型){if (IsController(type) && !feature.Controllers.Contains(type)){特征.Controllers.Add(type);}}}}
所以现在我们知道控制器是如何找到的,它们来自 ApplicationPart
注册到应用程序.下一个问题是我们如何创建应用程序部分.
经过一些审查并尝试使用依赖注入,手动将部件添加到应用程序以注册我的部件后,我遇到了另一个概念.
接口IMvcBuilder
具有扩展方法 AddApplicationPart
将 Assembly
添加到应用程序部分.这是通过将程序集包装在 中来完成的AssemblyPart
应用程序部分.在查看 AssemblyPart
时,此部分将在程序集中找到的所有类型返回给调用部件系统(在我们的例子中为 ControllerFeatureProvider
).
///<inheritdoc/>公共 IEnumerable<TypeInfo>类型=>Assembly.DefinedTypes;
现在 AssemblyPart
有趣的是方法 GetReferencePaths()
///<inheritdoc/>公共 IEnumerable<string>获取参考路径(){var dependencyContext = DependencyContext.Load(Assembly);如果(依赖上下文!= null){return dependencyContext.CompileLibraries.SelectMany(library => library.ResolveReferencePaths());}//如果应用程序在没有preserveCompilationContext的情况下被编译,则返回程序集的路径//作为参考.对于运行时编译,这将允许编译成功,只要它至少//一个应用程序部分已使用preserveCompilationContext 编译并包含一组超类型//编译成功所必需的.返回新的[] {组装.位置};}
看来难题的最后一块是在模块(或外部程序集)的 project.json 文件中启用 preserveCompilationContext
.
"preserveCompilationContext": {类型":布尔","description": "设置此选项以保留引用程序集和其他上下文数据以允许运行时编译.",默认":假}
最后,实现和解决这个问题变得非常简单.我们的每个外部程序集(或模块)都是通过我们的 ModuleManager
类加载的.这有一个所有引用的模块程序集的列表.因此,在注册 MVC 的 Startup.cs
文件中的 ConfigureServices
方法中,我们只需为每个模块程序集调用扩展方法 AddApplicationPart
.
var mvcBuilder = services.AddMvc();foreach(ModulesManager.ReferencedModules 中的 var 模块){mvcBuilder.AddApplicationPart(module.ReferencedAssembly);}
完成这些小改动后,我的外部控制器停止返回 404
.
In MVC 6 RC1 we used the IAssemlbyProvider
interface to register assemblies that were discovered at runtime and inject additional controller types, in a similar fashion to this post.. Now with RC2 release the IAssemblyProvider
has been removed and has changed to (see reference).
Our framework version is currently net46
.
Since the upgrade our controllers in external assemblies (not referenced) are returning a 404
status.
We have tried manually add the controller to the registered controllers via the ApplicationPartManager
.
var mvcBuilder = services.AddMvc();
var controllerFeature = new ControllerFeature();
mvcBuilder.PartManager.PopulateFeature(controllerFeature);
var moduleControllers = ModulesManager.GetControllers();
foreach (var c in moduleControllers)
controllerFeature.Controllers.Add(c);
mvcBuilder.PartManager.PopulateFeature(controllerFeature);
and...
services.AddMvc().ConfigureApplicationPartManager(app =>
{
var controllerFeature = new ControllerFeature();
app.PopulateFeature(controllerFeature);
var moduleControllers = ModulesManager.GetControllers();
foreach (var c in moduleControllers)
controllerFeature.Controllers.Add(c);
app.PopulateFeature(controllerFeature);
});
Now the assemblies are definitely loaded into the AppDomain
as our dependency injection system is finding and populating services for other items in the external assemblies.
With our previous implementations this worked nicely using the IAssemblyProvider
.
public class ModuleAwareAssemblyProvider : IAssemblyProvider
{
private readonly DefaultAssemblyProvider _defaultProvider;
public ModuleAwareAssemblyProvider(DefaultAssemblyProvider defaultProvider)
{
_defaultProvider = defaultProvider;
}
public IEnumerable<Assembly> CandidateAssemblies
{
get
{
return _defaultProvider.CandidateAssemblies.Concat(ModulesManager.Assemblies).Distinct();
}
}
}
I understand RC2 is still relatively new but if anyone has any experience registering additional controllers at start-up would be helpful.
Cheers, Nico
After some time spending working with the ControllerFeature
directly with no result it was time to go back to basics.
Basically at start up of the application controllers are registered into the controller feature container not from the controller feature. This is key, as you need to get the controllers registered.
I was browsing the GitHub repository for RC2 and came across the ControllerFeatureProvider
. As stated.
And then has a method further down to PopulateFeature
where we can see it grabs all the parts registered to the application and extracts the controller interfaces (the IsController()
method is worth a review).
/// <inheritdoc />
public void PopulateFeature(
IEnumerable<ApplicationPart> parts,
ControllerFeature feature)
{
foreach (var part in parts.OfType<IApplicationPartTypeProvider>())
{
foreach (var type in part.Types)
{
if (IsController(type) && !feature.Controllers.Contains(type))
{
feature.Controllers.Add(type);
}
}
}
}
So now we know how the controllers are found, they come from an ApplicationPart
registered to the application. Next question was how do we create an application part.
After some review and trying to use dependency injection, manually adding the part to the application to get my parts registered I came across another concept.
The interface IMvcBuilder
has the extension method AddApplicationPart
which adds an Assembly
to the application parts. This is done by wrapping the assembly in an AssemblyPart
application part. On review of the AssemblyPart
this part returns all of the types found in the assembly to the calling part system (in our case the ControllerFeatureProvider
).
/// <inheritdoc />
public IEnumerable<TypeInfo> Types => Assembly.DefinedTypes;
Now something interesting with the AssemblyPart
is the method GetReferencePaths()
/// <inheritdoc />
public IEnumerable<string> GetReferencePaths()
{
var dependencyContext = DependencyContext.Load(Assembly);
if (dependencyContext != null)
{
return dependencyContext.CompileLibraries.SelectMany(library => library.ResolveReferencePaths());
}
// If an application has been compiled without preserveCompilationContext, return the path to the assembly
// as a reference. For runtime compilation, this will allow the compilation to succeed as long as it least
// one application part has been compiled with preserveCompilationContext and contains a super set of types
// required for the compilation to succeed.
return new[] { Assembly.Location };
}
It appears that the final piece of the puzzle is to enable preserveCompilationContext
within the modules (or external assembly's) project.json file.
"preserveCompilationContext": {
"type": "boolean",
"description": "Set this option to preserve reference assemblies and other context data to allow for runtime compilation.",
"default": false
}
Finally the implementation and resolution for this became quite simple. Each of our external assemblies (or modules) are loaded through our ModuleManager
class. This has a list of all referenced module assemblies. So in the ConfigureServices
method in the Startup.cs
file where the MVC is registered we simply call the extension method AddApplicationPart
for each module assembly as.
var mvcBuilder = services.AddMvc();
foreach(var module in ModulesManager.ReferencedModules)
{
mvcBuilder.AddApplicationPart(module.ReferencedAssembly);
}
Once making these small changes my external controllers stopped returning a 404
.
这篇关于另一个组件中的 MVC 6 RC2 控制器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!