nHydrate 生成器模板






4.27/5 (5投票s)
使用您自己的自定义生成器模板扩展 nHydrate。
概述
在开始此演练之前,您应该花一些时间了解 nHydrate。一个好的起点是这篇文章:NHydrate 逐步指南。
nHydrate 是一个通用生成平台,它提供了许多开箱即用的模板,用于创建简单和复杂的 .NET 应用程序。开箱即用,它可以创建数据访问层 (DAL)、数据传输层 (DTO),并实现控制反转模式 (IoC)。此功能可以帮助您非常快速地启动并运行应用程序。但是,您可能希望扩展标准模板并创建自己的扩展。这是一项简单的任务,将在本文中进行说明。
本文将讨论如何扩展 nHydrate 模板中现有的 DTO 层;但是,您也可以创建自己的项目。DTO 项目已经存在。我们将只添加另一个生成器来创建额外的文件类型。这些新生成的文件将添加到 nHydrate 在用户请求生成此项目类型时创建的 DTO 项目中。本文未涵盖但仍然有用的是,创建在 VS.NET 解决方案资源管理器中创建全新项目的生成器。
在本文中,我们将扩展 DTO 生成器。该扩展将包括创建两个模板,它们将新类生成到 DTO 项目中。这些模板将协同工作,为模型中定义的每个表生成一个类。我们使用两个模板来演示一种常见模式,即我们使用部分类方法将一个类生成到两个文件中。一个文件用于处理自定义类扩展,而另一个文件是生成的,不应进行自定义。
创建项目
首先,我们将创建一个新的类库项目,即 Widgetsphere.GeneratorWalkthrough。该项目将需要引用
- Widgetsphere.Generator.Common - 这包含构成核心生成的接口和属性。
- Widgetsphere.Generator - 包含模型类以及使创建新模板更容易的基类。
- Widgetsphere.Generator.DataTransfer - 由于在此示例中我们正在扩展 DTO 生成器,因此我们将在 DTO 项目中创建新类。我们将使用此对 DTO 对象的引用来完成此操作。
单击项目属性并突出显示构建事件。为了方便部署,将后期构建事件设置如下。请记住,这是 32 位机器的默认安装位置。如果您使用的是 64 位机器,或者它安装在自定义文件夹中,您将需要使用正确的路径。
copy $(TargetDir)$(TargetName).* "C:\Program Files\Widgetsphere\CodeTools\*.*"
创建生成器和模板类
按照惯例,我们将文件创建与文件内容分离到两个类中。文件创建在生成器类中指定。此有两个主要要求。从 BaseGenerator
继承并向类应用 GeneratorItemAttribute
。
/// <summary>
/// This generator will generate the [TableName]MetaData.cs file. This file
/// contains a public partial class [TableName]MetaData. For the generated
/// file the class will be empty.
/// This file is provided as a way to allow programmers to
/// extend the generated class. It will be generated
/// once and never over written after that point.
/// </summary>
[GeneratorItemAttribute("MetaData Class Extension",
typeof(DataTransferProjectGenerator))]
public class MetaDataInfoClassExtensionGenerator : BaseGenerator
/// <summary>
/// This generator will generate
/// the [TableName]MetaData.generated.cs file. This file
/// contains a public partial class [TableName]MetaData. This generated
/// file will contain the core implementation
/// of the [TableName]MetaData. It will be
/// overwritten each time a generation occurs.
/// It should not be extended by programmers.
/// </summary>
[GeneratorItemAttribute("MetaData Class",
typeof(MetaDataInfoClassExtensionGenerator))]
public class MetaDataInfoClassGenerator : BaseGenerator
我们首先讨论 GeneratorItemAttribute
。生成器引擎会搜索所有公开此属性的类。一旦找到此,它就会将其加载到在生成过程中应调用的类列表中。属性中的第一个参数只是应应用于此生成器的名称。第二个参数稍微复杂一些。它用于标识依赖生成器。生成架构使用此信息以适当的顺序调用生成器。在此示例中,您会注意到 DataTransferProjectGenerator
是 MetaDataInfoClassExtensionGenerator
的前身,而 MetaDataInfoClassExtensionGenerator
是 MetaDataInfoClassGenerator
的前身。当您看到生成的结果时,原因就非常明显了。您必须在拥有 CustomerMetaData.cs 等关联项之前拥有 Visual Studio 项目 Acme.DataTransfer,并且您必须在创建子项 CustomerMetaData.Generated.cs 之前拥有项 CustomerMetaData.cs。
BaseGenerator
是接口 IProjectItemGenerator
的抽象实现。此接口由核心生成器引擎通过 VSCodeTools 插件添加和覆盖文件。创建 BaseGenerator
是为了隐藏实现此类的复杂性。重写此类时,有两个抽象属性:FileCount
和 ProjectName
,以及一个必须实现的抽象方法 Generate
。这些属性非常直接。第一个 FileCount
用于告知生成器预计将创建多少项目项。此信息在生成过程中的各个点提供给用户界面进行展示。第二个是 ProjectName
,用于标识应将文件放置在其中的项目。最后,是生成引擎调用的 Generate
方法。作为此方法实现的一部分,用户应引发 ProjectItemGenerated
和 ProjectItemGenerationComplete
事件。生成引擎监视这些事件并使用事件参数来识别在适当的 Visual Studio 项目中创建什么。
/// <summary>
/// Method called by the core generator. This method is used to
/// manage what items are to be generated by the system.
/// </summary>
public override void Generate()
{
//Walk through all the tables in the model.
//Generate classes for those that are specified to be generated.
foreach (Table table in _model.Database.Tables.Where(
x => x.Generated).OrderBy(x => x.Name))
{
//Construct the template class passing
//it the current model file and table
MetadataInfoClassExtensionTemplate template =
new MetadataInfoClassExtensionTemplate(_model, table);
//Create a string that represents the project relative
//output location of the file (Ex: \MetaData\Customer.cs)
string projectRelativeFileName =
RELATIVE_OUTPUT_LOCATION + template.FileName;
//Get the generated file content.
string fileContent = template.FileContent;
//Publish the Project Item Generated event
//for the generation engine to handle
ProjectItemGeneratedEventArgs eventArgs =
new ProjectItemGeneratedEventArgs(projectRelativeFileName,
fileContent, ProjectName, this, false);
OnProjectItemGenerated(this, eventArgs);
}
//Publish the generation completed event to identify to the
//engine all files have been generated.
ProjectItemGenerationCompleteEventArgs gcEventArgs =
new ProjectItemGenerationCompleteEventArgs(this);
OnGenerationComplete(this, gcEventArgs);
}
创建模板类
文件内容是从模板类生成的。此将继承自 BaseDataTransferTemplate
。按照惯例,生成项目将包含一个模板基类,该基类通过使创建新模板更容易的方法来帮助其他开发人员。
/// <summary>
/// This template will generate the content
/// for the [TableName]MetaData.cs file. This file
/// will contain a public partial class [TableName]MetaData. This file is
/// provided as a way to allow programmers to extend the generated class.
/// It will be generated once and never over written after that point.
/// </summary>
public class MetadataInfoClassExtensionTemplate : BaseDataTransferTemplate
/// <summary>
/// This template will generate the content
/// for the[EntityName]MetaData.generated.cs file.
/// This file contains a public partial class
/// [EntityName]MetaData. The file will contain
/// the core implementation of the [EntityName]MetaData.
/// It will be overwritten each time
/// a generation occurs. It should not be extended by programmers.
/// </summary>
public class MetaDataInfoClassTemplate : BaseDataTransferTemplate
我们生成此,以生成所有需要添加到项目的文件的 FileName
、FileContent
以及必要的 ParentItemName
。
/// <summary>
/// Returns the name of the file. That is to be added to the project
/// </summary>
public override string FileName
{
get { return string.Format("{0}MetaData.Generated.cs",
_currentTable.PascalName); }
}
/// <summary>
/// Returns the a parent item name. This is only necessary when the
/// parent item is another file in the project
/// </summary>
public string ParentItemName
{
get { return string.Format("{0}MetaData.cs",
_currentTable.PascalName); }
}
/// <summary>
/// Returns the generated contents of the file as a string
/// </summary>
public override string FileContent
{
get
{
GenerateContent();
return sb.ToString();
}
}
让我们测试一下
为了测试,我们将设置 Widgetsphere.GeneratorWalkthrough 项目以启动 Visual Studio 的另一个实例。在项目属性中,突出显示“调试”选项卡。接下来,选择“启动外部程序”选项并输入 Visual Studio 应用程序的路径。
重要:非常重要的是要意识到,一旦打开模型,Visual Studio 将锁定 nHydrate 安装文件夹下的文件。一旦这些文件被锁定,您将无法构建此项目。因此,建议只运行一个已打开生成扩展项目的 Visual Studio 实例。一旦开始调试,将启动第二个实例。此实例将用于打开模型文件并启动生成过程。
现在调试信息已设置好,我们可以开始调试了。按 F5,Visual Studio 的新实例将启动。在示例中,还有另一个解决方案 TestCustomTemplate.sln;此解决方案包含一个非常简单的模型文件,您可以从中生成。打开模型文件并开始生成。在 MetaDataInfoClassExtensionGenerator
类的 Generate
上设置一个断点。运行“生成所有”命令,确保选择了 DTO 项目进行生成。一旦生成开始,将命中断点,您可以逐步调试代码。
生成后,您会看到一个 DTO 项目。这是由 nHydrate 附带的标准 DTO 项目生成器创建的。但是,您现在会看到项目中有一个 MetaData 文件夹。在该文件夹中,您会看到模型中每个数据库表都有一组文件。有一个用户可修改的类名为 [Table]MetaData.cs,以及一个依赖的生成文件名为 [Table]MetaData.Generated.cs。用户可以在前者中进行修改,并且更改不会被覆盖。但是,后者由生成器维护,并且每次生成时都会被覆盖。用户不应修改此文件,因为更改不会被保留。您还会注意到,在生成的文件中,有一个方法返回所有数据库字段名称,以及一个用于表中每个属性的属性。
这项技术确实强大,您可以扩展现有生成器并自定义 nHydrate 框架,以创建您自定义应用程序所需的任何内容。