(四)学习了解OrchardCore笔记——将模块的名字添加到程序集的ModuleName
关于如何将模块名添加到程序集的ModuleName说简单吧也简单,说不简单吧也不简单。
简单的原因是代码只有几行,不简单的原因是这些都不是c#,都是MSbuild的代码。这可真难为我了,所以这个地方我卡了两个星期。
首先我们来看下解决方案的目录:
都知道这些文件夹都是解决方案文件夹,但是build解决方案文件夹里放的是什么,居然不是项目?不是项目如何调用呢,这可触及我的知识盲点。经过多方查阅资料和博问上提问,我重于搞清楚了。
首先,这两个.yml文件删了也无所谓,我第一次见到yml文件还是在docker的配置上,也就是一个跟我们常用的json一样的配置文件(用缩进代替json里面那堆括号,有人觉得这样比较方便)。开始我还以为是通过其它程序靠yml配置文件注入模块,被误导了好久,这两个配置文件就是持续集成的配置文件(没进过大厂不懂这些被浪费好久),也就是项目发布到github会自动帮你build等等。
其次,就是剩下的文件是靠Directory.Build.props和Directory.Build.targets调用到项目里的。这就是我的第二个知识盲点了。我们打开文件夹(直接打开资源管理器的,不是visual studio的),可以发现src源码文件夹下的OrchardCore、OrchardCore.Modules和OrchardCore.Themes三个文件夹都有Directory.Build.props和Directory.Build.targets这两个文件。这意味着还有文件不在项目里?这又怎么用呢?我真是快崩溃了!经过查找资料,重要明白:在MSBuild15以后新增一个功能就是让开发者可以自己定义项目信息放在一个文件,这个文件会在Microsoft.Common.props和Microsoft.Common.target引用,而且会在csproj项目文件所在的文件夹开始寻找,只要找到存在Directory.Build.props和Directory.Build.target文件就会自动导入里面的内容。也就是从项目的csproj当前项目开始自动向上寻找Directory.Build.props和Directory.Build.target,直到解决方法根目录,只要找到立即停止。这就是意味着每个项目编译的时候会把这些文件给包括进来。
好了,明白了这些后,开始按顺序了解模块的加载过程。
我是从启动项目OrchardCore.Cms.Web开始看,它项目引用了OrchardCore.Application.Cms.Core.Targets,而这个项目又引用了OrchardCore.Application.Targets,这下Targets项目都在OrchardCore文件夹下,也就是编译的时候会包含上面说的Directory.Build.props和Directory.Build.targets文件。
OrchardCore.Application.Targets里面只有一个OrchardCore.Application.Targets.targets文件,打开如下:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <!-- This file is packaged with "OrchardCore.Application.Targets.nupkg" in "./build" such that any Application that references it will embed in its assembly a list of the referenced modules. --> <Target Name="ResolveModuleProjectReferences" AfterTargets="AfterResolveReferences"> <MSBuild Targets="GetModuleProjectName" BuildInParallel="$(BuildInParallel)" Projects="@(_MSBuildProjectReferenceExistent)" Condition="'@(_MSBuildProjectReferenceExistent)' != ''" SkipNonexistentTargets="true" ContinueOnError="true"> <Output ItemName="ModuleProjectNames" TaskParameter="TargetOutputs" /> </MSBuild> <ItemGroup> <ModuleNames Include="@(ModulePackageNames);@(ModuleProjectNames)" /> </ItemGroup> <ItemGroup> <AssemblyAttribute Include="OrchardCore.Modules.Manifest.ModuleNameAttribute" Condition="'@(ModuleNames)' != ''"> <_Parameter1>%(ModuleNames.Identity)</_Parameter1> </AssemblyAttribute> </ItemGroup> </Target> <Target Name="NoWarnOnRazorViewImportedTypeConflicts" BeforeTargets="RazorCoreCompile"> <PropertyGroup> <NoWarn>$(NoWarn);0436</NoWarn> </PropertyGroup> </Target> </Project>
看到AssemblyAttribute元素了吧,没错,这就是程序集的属性啊,在看看它里面有啥?OrchardCore.Modules.Manifest.ModuleNameAttribute,没错,这个就是上篇反射找到的ModuleName,那么怎么来的呢,有个Condition=”‘@(ModuleNames)’ != ””,明显这个条件就是ModuleNames元素不为空,然后获取ModuleNames元素的Identity。那么我们顺着往上看就可以看到上个ItemGroup元素里面就包含ModuleNames,但是没有Identity属性啊,只能查阅资料了,谁叫我MSbuild两眼一抹黑呢,根据msdn介绍明白Identity就是Include属性中指定的项,大家也可以看看msdn。那么我们接着看INclude,@(ModulePackageNames);@(ModuleProjectNames),分号间隔符,间隔而已,继续看看第二个ModuleProjectNames,上面的MSBuid的Oupt就是了,而任务呢,就是MSBuild的GetModuleProjectName。这个任务在哪呢,感觉很无厘头是不是,不对,前面不是介绍了Directory.Build.props和Directory.Build.targets吗,没错,往里面找,打开Directory.Build.targets看看
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <Import Project="..\OrchardCore.Build\OrchardCore.Commons.targets" /> <Import Project="..\OrchardCore\OrchardCore.Module.Targets\OrchardCore.Module.Targets.targets" /> <PropertyGroup> <AllowedOutputExtensionsInPackageBuildOutputFolder>$(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb</AllowedOutputExtensionsInPackageBuildOutputFolder> </PropertyGroup> </Project>
看到里面包含了的OrchardCore.Module.Targets.targets文件没有,打开接着看(这个有点长直接看最后部分):
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> ... <Target Name="GetModuleProjectName" Returns="@(ModuleProjectName)"> <ItemGroup> <ModuleProjectName Include="$(AssemblyName)" /> </ItemGroup> </Target> </Project>
看到Name为GetModuleProjectName的Target任务了吧,请注意这个文件的位置,在..\OrchardCore\OrchardCore.Module.Targets\OrchardCore.Module.Targets.targets,没错就在OrchardCore.Module.Targets项目里,而所有模块都是以用这个项目,也就是说所有应用这个项目的项目名都会加到程序集的ModuleName上!