此文章由人工翻译。 将光标移到文章的句子上,以查看原文。 |
译文
原文
|
向层关系图添加自定义体系结构验证
更新:2010 年 12 月
在 Visual Studio 2010 旗舰版和 Visual Studio 2010 高级专业版中,用户可以参照层模型验证 Visual Studio 项目中的源代码,以便可以验证源代码是否符合层关系图上的依赖项。 有一种标准验证算法,但利用此 Visual Studio 2010 功能包,您可以对 Visual Studio 旗舰版和 Visual Studio 高级专业版定义自己的验证扩展。 有关更多信息,请参见 Visual Studio 功能包。
当用户在层关系图上选择“验证体系结构”命令时,将调用标准验证方法,并后跟已安装的任何验证扩展。
注意 |
|---|
层关系图中的验证与 UML 关系图中的验证不相同。 在层关系图中,主要目的是将关系图与解决方案其他部分中的程序代码进行比较。 |
您可以将层验证扩展打包到一个 Visual Studio 集成扩展 (VSIX) 中,并可以将其分发给其他 Visual Studio 旗舰版用户。 您可以将验证程序单独放入一个 VSIX 中,也可以将其合并到其他扩展所在的同一个 VSIX 中。 您应在验证程序自己的 Visual Studio 项目中编写验证程序代码,而不是在其他扩展所在的同一项目中编写验证程序代码。
创建验证程序的最快速方法是使用项目模板。 这会将代码和 VSIX 清单放入同一个项目中。
使用项目模板定义扩展
-
通过使用“文件”菜单上的“新建项目”命令,在新解决方案中创建项目。
-
在“新建项目”对话框中的“建模项目”下,选择“层设计器验证扩展”[Layer Designer Validation Extension]。
此模板将创建一个包含小示例的项目。
-
编辑代码以定义验证。 有关更多信息,请参见编程验证。
-
若要测试扩展,请参见调试层验证。
注意
仅在特定情形下才调用您的方法,而且断点不会自动工作。 有关更多信息,请参见调试层验证。
-
若要在 Visual Studio 的主实例中或在其他计算机上安装扩展,请在 bin\* 中找到 .vsix 文件。 将该文件复制到要安装它的计算机中,然后双击它。 若要卸载该文件,请使用“工具”菜单上的“扩展管理器”。
若要创建一个包含层验证程序、命令和其他扩展的 VSIX,建议您创建一个项目来定义该 VSIX,并为处理程序创建单独的项目。 有关其他类型的建模扩展的信息,请参见扩展 UML 模型和关系图。
将层验证添加到单独的 VSIX 中
-
在新的或现有的 Visual Studio 旗舰版解决方案中创建类库项目。 在“新建项目”对话框中,单击“Visual C#”,再单击“类库”。 此项目将包含层验证类。
-
在您的解决方案中标识或创建 VSIX 项目。 VSIX 项目包含一个名为 source.extension.vsixmanifest 的文件。 如果必须添加一个 VSIX 项目,请按照以下步骤操作:
-
在“新建项目”对话框中,展开“Visual C#”,单击“扩展性”,再单击“VSIX 项目”。
-
在解决方案资源管理器中,右击该 VSIX 项目,然后单击“设为启动项目”。
-
单击“选择版本”[Select Editions],并确保选中 Visual Studio 旗舰版。
-
-
在 source.extension.vsixmanifest 中的“内容”下,将层验证项目添加为 MEF 组件:
-
单击“添加内容”。
-
在“选择内容类型”处,选择“MEF 组件”。
-
在“选择源”[Select a source]处,单击“项目”,并选择命令或笔势处理程序项目的名称。
-
保存该文件。
-
-
将层验证项目添加为自定义扩展:
-
单击“添加内容”。
-
在“选择内容类型”处,选择“自定义扩展类型”。
-
在“类型”处,输入 Microsoft.VisualStudio.ArchitectureTools.Layer.Validator。
-
在“选择源”处,单击“项目”,并选择验证类库项目的名称。
-
-
在“引用”下,单击“添加引用”,并选择此功能包的运行时。
-
返回到层验证项目,并添加以下项目引用:
引用
可完成的操作
如果您安装了 Visual Studio 2010 可视化和建模功能包:
%LocalAppData%\Microsoft\VisualStudio\10.0\Extensions\Microsoft\Visualization and Modeling Feature Pack Runtime\1.0\Microsoft.VisualStudio.GraphModel.dll
如果您安装了 Visual Studio 2010 功能包 2:
…\Microsoft Visual Studio 10.0\Common7\IDE\Extensions\Microsoft\Visualization and Modeling Feature Pack Runtime\1.0\Microsoft.VisualStudio.GraphModel.dll
读取体系结构关系图
如果您安装了 Visual Studio 2010 可视化和建模功能包:
%LocalAppData%\Microsoft\VisualStudio\10.0\Extensions\Microsoft\Visualization and Modeling Feature Pack Runtime\1.0\Microsoft.VisualStudio.ArchitectureTools.Extensibility.CodeSchema.dll
如果您安装了 Visual Studio 2010 功能包 2:
…\Microsoft Visual Studio 10.0\Common7\IDE\Extensions\Microsoft\ Visualization and Modeling Feature Pack Runtime\1.0\Microsoft.VisualStudio.ArchitectureTools.Extensibility.CodeSchema.dll
读取与层关联的代码 DOM
如果您安装了 Visual Studio 2010 可视化和建模功能包:
%LocalAppData%\Microsoft\VisualStudio\10.0\Extensions\Microsoft\Visualization and Modeling Feature Pack Runtime\1.0\Microsoft.VisualStudio.ArchitectureTools.Extensibility.Layer.dll
如果您安装了 Visual Studio 2010 功能包 2:
…\Microsoft Visual Studio 10.0\Common7\IDE\Extensions\Microsoft\Visualization and Modeling Feature Pack Runtime\1.0\Microsoft.VisualStudio.ArchitectureTools.Extensibility.Layer.dll
读取层模型
Microsoft.VisualStudio.Uml.Interfaces
读取层模型
Microsoft.VisualStudio.ArchitectureTools.Extensibility
读取和更新形状和关系图。
System.ComponentModel.Composition
使用 Managed Extensibility Framework (MEF) 定义验证组件
Microsoft.VisualStudio.Modeling.Sdk.10.0
定义建模扩展
注意
%LocalAppData% 通常为“驱动器名称:\Users\用户名\AppData\Local”。 在 Windows XP 或 Windows 2003 上,使用 %AppData% 而不使用 %LocalAppData%。
-
编辑 C# 类库项目中的类文件以包含验证的代码。 有关更多信息,请参见编程验证。
-
若要测试扩展,请参见调试层验证。
注意
仅在特定情形下才调用您的方法,而且断点不会自动工作。 有关更多信息,请参见调试层验证。
-
若要在 Visual Studio 的主实例中或其他计算机上安装 VSIX,请在 VSIX 项目的 bin 目录中找到 .vsix 文件。 将该文件复制到要安装 VSIX 的计算机中。 在 Windows 资源管理器中双击 VSIX 文件。
若要卸载该文件,请使用“工具”菜单上的“扩展管理器”。
若要定义层验证扩展,请定义一个具有以下特征的类:
-
声明的总体形式如下所示:
[Export(typeof(IValidateArchitectureExtension))] public partial class Validator1Extension : IValidateArchitectureExtension { public void ValidateArchitecture(Graph graph) { var typeCategory = graph.DocumentSchema .Categories.Get("CodeSchema_Type"); var allTypes = graph.Nodes.GetByCategory(typeCategory); ... this.LogValidationError(graph, "SampleErrorId", "Sample Validation Error", GraphErrorLevel.Error, allTypes); } -
当您发现错误时,可使用 LogValidationError() 报告错误。
当用户调用“验证体系结构”菜单命令时,层运行时系统对层及其项目进行分析来生成关系图。 此关系图包括四部分:
-
Visual Studio 解决方案的层模型,表示为关系图中的节点和链接。
-
解决方案中定义的代码、项目项和其他项目,表示为表示分析过程发现的依赖项的节点和链接。
-
从层节点到代码项目节点的链接。
-
表示验证程序发现的错误的节点。
关系图构造完成后,调用标准验证方法。 此过程完成后,将按未指定的顺序调用任意已安装的扩展验证方法。 将关系图传递给每个 ValidateArchitecture 方法,然后该方法可以扫描关系图并报告它发现的任何错误。
注意
|
|---|
|
这与应用到 UML 关系图的验证过程不相同,而且与可通过域特定语言使用的验证过程也不相同。 |
验证方法不应更改正在验证的层模型或代码。
关系图模型在 Microsoft.VisualStudio.GraphModel 中定义, 其主体类为 Node 和 Link。
每个节点和每个链接都有一个或多个类别,用来指定它表示的元素或关系的类型。 典型关系图的节点具有以下类别:
-
Dsl.LayerModel
-
Dsl.Layer
-
Dsl.Reference
-
CodeSchema_Type
-
CodeSchema_Namespace
-
CodeSchema_Type
-
CodeSchema_Method
-
CodeSchema_Field
-
CodeSchema_Property
代码中从层到元素的链接具有“Represents”类别。
下面的代码是体系结构验证扩展的典型示例。 它验证层模型是否至少引用一次在解决方案代码中声明的每个类型。 用户可以通过设置模型的 Boolean 自定义属性,控制是否应对每个层模型执行此验证。
如果发现错误,则调用 LogValidationError。
若要调试层验证扩展,请按 Ctrl+F5。 Visual Studio 的实验实例将打开。 在此实例中打开或创建一个层模型。 此模型必须与代码关联,且必须具有至少一个依赖项。
测试包含依赖项的解决方案
除非具有以下特征,否则不执行验证:
-
在层关系图上存在至少一个依赖项链接。
-
模型中存在一些与代码元素关联的层。
首次启动 Visual Studio 的实验实例来测试您的验证扩展时,打开或创建一个具有这些特征的解决方案。
运行“验证体系结构”命令之前运行“清理解决方案”命令
每当更新验证代码时,请首先在实验解决方案中使用“生成”菜单上的“清理解决方案”命令,然后再测试“验证”命令。 这是必需的,原因是验证结果会被缓存。 如果您尚未更新测试层关系图或其代码,则将不会执行验证方法。
显式启动调试器
验证将在单独的进程中运行。 因此,不会触发验证方法中的断点。 验证启动后,必须显式地将调试器附加到进程。
若要将调试器附加到验证进程,请在验证方法的开头插入对 System.Diagnostics.Debugger.Launch() 的调用。 当调试对话框出现时,选择 Visual Studio 的主实例。
或者,也可以插入对 System.Windows.Forms.MessageBox.Show() 的调用。 当消息框出现时,转到 Visual Studio 的主实例,并在“调试”菜单上单击“附加到进程”。选择名为 Graphcmd.exe 的进程。
始终通过按 Ctrl+F5(“开始执行(不调试)”)来启动此实验实例。
部署验证扩展
若要在安装了 Visual Studio 旗舰版或 Visual Studio 高级专业版的计算机上安装您的验证扩展,请在目标计算机上打开 VSIX 文件。 若要在安装了 Team Foundation Build 的计算机上进行安装,必须将 VSIX 内容手动提取到 Extensions 文件夹中。 有关更多信息,请参见部署层建模扩展。
下面的示例阐释了层验证扩展。
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Linq;
using Microsoft.VisualStudio.ArchitectureTools.Extensibility.CodeSchema;
using Microsoft.VisualStudio.GraphModel;
using Microsoft.VisualStudio.ArchitectureTools.Extensibility.Layer;
namespace MyValidationExtensions
{ // This attribute identifies a layer validator:
[Export(typeof(IValidateArchitectureExtension))]
public partial class UnreferencedTypeValidatorExtension
: IValidateArchitectureExtension
{
private GraphCategory typeCategory = null;
/// <summary>
/// Validate the architecture
/// </summary>
/// <param name="graph">The graph</param>
public void ValidateArchitecture(Graph graph)
{
// A good place to attach a debugger
// System.Windows.Forms.MessageBox.Show("Attach - Unreferenced Type Validator");
// To find the nodes that represent the code and the layers,
// we need to filter by category.
// Categories are identified by specific strings:
if (typeCategory == null)
{
typeCategory =
graph.DocumentSchema.Categories.Get("CodeSchema_Type");
}
var layerModelCategory =
graph.DocumentSchema.Categories.Get("Dsl.LayerModel");
var allLayerModels =
graph.Nodes.GetByCategory(layerModelCategory);
foreach (var layerModel in allLayerModels)
{
var allTypesMustBeReferencedProperty =
ExtractProperty(layerModel,
AllTypesMustBeReferencedProperty.FullName);
bool shouldAllTypesBeReferenced =
allTypesMustBeReferencedProperty == null
? false
: Convert.ToBoolean(allTypesMustBeReferencedProperty);
if (shouldAllTypesBeReferenced)
{
// Find all types referenced by layers:
var referencedTypes = new HashSet<Node>();
GetReferencedTypes(referencedTypes, layerModel);
var allTypes = graph.Nodes.GetByCategory(typeCategory);
foreach (var type in allTypes)
{
if (!referencedTypes.Contains(type))
{
// Filter out types that are not part of any
// assembly (for example referenced external types).
if (type.GetContainmentSources(graph)
.Where(n => n.HasCategory(graph.DocumentSchema
.Categories.Get("CodeSchema_Assembly"))).Any())
{
// type is not referenced in the layer diagram
this.LogValidationError(graph,
string.Format("{0}_UnreferencedTypeError_{1}",
GetFullyQualifiedTypeName(type), Guid.NewGuid()),
string.Format("AV1002 : Unreferenced type :"
+ " {0}{2}Layer Diagram: {1}.layerdiagram.",
GetFullyQualifiedTypeName(type),
layerModel.Label,
Environment.NewLine),
GraphErrorLevel.Error,
new Node[] { type });
}
}
}
}
}
}
private void GetReferencedTypes(HashSet<Node> referencedTypes, Node node)
{
foreach (Node containedNode in node.GetContainmentTargets(node.Owner))
{
if (referencedTypes.Contains(containedNode))
{
// We've seen this node before
continue;
}
if (containedNode.HasCategory(typeCategory))
{
referencedTypes.Add(containedNode);
}
else
{
GetReferencedTypes(referencedTypes, containedNode);
}
}
}
public static string GetFullyQualifiedTypeName(Node typeNode)
{
try
{
string returnValue;
CodeQualifiedIdentifierBuilder id =
new CodeQualifiedIdentifierBuilder(
typeNode.Id, typeNode.Owner);
returnValue = id.GetFullyQualifiedLabel(
CodeQualifiedIdentifierBuilder.LabelFormat
.NoAssemblyPrefix);
return returnValue;
}
catch { }
return typeNode.Label;
}
public static object ExtractProperty
(Node layerModel, string propertyName)
{
object propertyValue = null;
var propertyCategory =
layerModel.Owner.DocumentSchema.GetProperty(propertyName);
if (propertyCategory != null)
{
propertyValue = layerModel[propertyCategory];
}
return propertyValue;
}
}
