有效地利用模型结构和数据进行面向模型的开发






4.85/5 (14投票s)
探索如何通过面向模型的方法,直接利用模型结构和数据来有力地增强软件开发。
引言
面向模型的开发(MOD)是一种技术,它允许你在开发过程的任何阶段利用模型进行开发。你将模型信息转换为源代码,以及/或在开发过程中直接使用模型。这并不意味着特定的过程或技术。如何表示和使用你的模型完全取决于你。
用于 MOD(或软件开发通用)的模型可以描述为具有以下特征:
- 模型结构 - 模型结构定义了相应模型可以组织和构建的“规则”或模式。例如,在通用的实体关系模型中,模型结构将包含实体、关系和属性等构造。
- 模型数据 - 模型数据定义了模型结构的特定实例,并为特定目的赋予模型具体的含义。例如,在通用的实体关系模型中,模型数据可以包含实体的实例,如客户或订单,以及属性的实例,如名字(针对客户)和订单总额(针对订单)。
任何类型的模型(UML、Microsoft Entity Framework 等),无论简单还是复杂,都可以用模型结构和模型数据来描述。
建模可以描述为根据你设计和开发特定系统的需求和需要,用模型数据填充你选择的模型结构的过程。建模工具通常通过图表等可视化构造提供创建和维护模型数据的方法。本文不讨论建模过程,只以原始格式呈现模型数据。
利用模型 - 在 MOD 中,你通常会利用模型,根据模型中概述的数据、系统需求和你最佳编码实践来创建和维护代码。为了最有效地利用模型进行面向模型的开发:
- 你需要能够完全了解模型结构
- 你需要能够以各种可想象的方式“遍历”模型数据
- 基于模型结构对模型数据进行通用浏览
- 通过浏览获取模型数据实例的父项、子项或其他相关信息
- 根据条件搜索模型数据
- 你需要能够指示并处理模型数据实例中不符合你概述的通用规则的异常情况
- 你需要能够无缝地将生成的面向模型代码与你的自定义代码(面向模型或其他)集成
在本文中,我们将介绍如何利用模型结构和数据进行面向模型的开发。我们将创建两个“虚构”的简单模型结构和数据的示例,并展示如何利用这些信息以面向模型的方式有效地生成和维护代码。
背景
本文将选择Mo+来利用自定义模型结构和数据,因为 Mo+ 专为处理和识别你选择的模型结构和数据而设计。Mo+ 面向对象的编程语言和面向模型的开发 IDEMo+ Solution Builder 在这篇Code Project 文章中已经介绍。本文的目的是阐述如何普遍地利用模型结构和数据进行 MOD。将展示 Mo+ 模型和模板,但本文不是 Mo+ 语言或工具的教程。然而,你可以不同程度地将本文中概述的技术应用于其他建模和代码生成工具。如果你正尝试使用 Mo+,上面的文章中还有一个“了解更多”部分,其中包含入门的附加信息。
模型结构将显示为UML类图,其中:
- 类代表模型结构中的对象
- 成员代表模型结构中对象的属性
- 关联代表模型结构中的父子关系
两个示例的模型数据将从 Northwind(SQL Server 或 MySQL)数据库架构填充。
生成的代码将是XML和C#。
模型示例 - 基础和扩展
我们将在这里完全虚构一个简单的模型。假设我们希望我们的模型由称为基础(foundations)的基本构建块和称为扩展(expansions)的更高级别事物组成,后者可以利用基础和其他扩展。
模型结构
考虑以下代表我们预期模型结构的图表
此模型结构的规则非常简单:
- 基础是一个基本构建块,由FoundationID标识,并由FoundationName描述。
- 扩展是一个更高级别的构建块,由ExpansionID标识,并由ExpansionName描述。扩展可以进一步由自由格式的标签描述。
- 基础层扩展一个特定的扩展,以包含一个特定的基础构建块。基础层由FoundationLayerID标识,并通过ReferencedFoundationID标识对相应基础的引用。
- 扩展层扩展一个特定的扩展,以包含另一个扩展。扩展层由ExpansionLayerID标识,并通过ReferencedExpansionID标识对相应扩展的引用。
- 一个扩展可以包含零个或多个基础层。
- 一个扩展可以包含零个或多个扩展层。
模型数据
要实际使用我们的模型结构进行 MOD,我们需要用符合上述结构的模型填充数据。
我们将使用 Northwind 数据库架构作为模型数据源,并按如下方式填充模型:
- 如果一个表没有外键,它将被添加为基础
- 表名(稍作修改)成为基础名称
- 基础 ID 已生成
- 如果一个表有外键,它将被添加为扩展
- 表名(稍作修改)成为扩展名称
- 扩展 ID 已生成
- 对于每个外键:
- 如果外键指向基础,则该键作为基础层添加到模型中
- 如果外键指向扩展,则该键作为扩展层添加到模型中
以下树状视图以原始形式说明了此模型数据
请注意,特定的基础包括类别和客户等内容,而特定的扩展包括员工和订单等内容。
如果您正在使用 Mo+,请从示例下载中加载 FoundationExpansionModel.xml 解决方案模型,这将创建此模型结构并在您连接到 Northwind(或其他 SQL Server)数据库时对其进行填充。本文中描述的代码模板也将直接可用。
MOD - 使用模型生成 XML
我们将使用填充的模型来生成一个包含基础和扩展的 XML 文件。在代码模板中,我们将以各种方式遍历模型,利用模型数据(结合原始模板文本)来生成 XML 文件内容。
模型数据通用浏览
至少,你需要能够浏览模型数据,以获取模型结构中任何给定对象的全部实例。下面,我们通过浏览模型中的每个基础和扩展来创建一些基本的 XML(在第 4 行和第 12 行的foreach语句中可见):
下面是用 Northwind 模型数据生成的 XML。生成了基础和扩展的节点,并带有相应的名称。(示例下载中的 Mo+ 模板名为 FEBrowseOnly)
<Model>
<Foundations>
<Foundation Name="CustomerDemographic"/>
<Foundation Name="Category"/>
<Foundation Name="Customer"/>
<Foundation Name="Region"/>
<Foundation Name="Shipper"/>
<Foundation Name="Supplier"/>
</Foundations>
<Expansions>
<Expansion Name="CustomerCustomerDemo"/>
<Expansion Name="Employee"/>
<Expansion Name="EmployeeTerritory"/>
<Expansion Name="OrderDetail"/>
<Expansion Name="Order"/>
<Expansion Name="Product"/>
<Expansion Name="Territory"/>
</Expansions>
</Model>
访问子模型数据
你需要能够访问任何特定模型数据实例的子信息。在这种情况下,我们希望访问特定扩展下的所有基础层和扩展层。下面,我们遍历当前扩展的子基础层和扩展层(在第 17 行和第 25 行的foreach语句中可见)。我们希望生成基础层和扩展层子节点:
下面是用 Northwind 模型数据生成的 XML,其中每个扩展下都有子层数据。(示例下载中的 Mo+ 模板名为 FEBrowseChildren)
<Model>
<Foundations>
<Foundation Name="CustomerDemographic"/>
<Foundation Name="Category"/>
<Foundation Name="Customer"/>
<Foundation Name="Region"/>
<Foundation Name="Shipper"/>
<Foundation Name="Supplier"/>
</Foundations>
<Expansions>
<Expansion Name="CustomerCustomerDemo">
<FoundationLayers>
<FoundationLayer Reference="3">
<FoundationLayer Reference="4">
</FoundationLayers>
<ExpansionLayers>
</ExpansionLayers>
</Expansion>
<Expansion Name="Employee">
<FoundationLayers>
</FoundationLayers>
<ExpansionLayers>
<ExpansionLayer Reference="5">
</ExpansionLayers>
</Expansion>
<Expansion Name="EmployeeTerritory">
<FoundationLayers>
</FoundationLayers>
<ExpansionLayers>
<ExpansionLayer Reference="5">
<ExpansionLayer Reference="13">
</ExpansionLayers>
</Expansion>
<Expansion Name="OrderDetail">
<FoundationLayers>
</FoundationLayers>
<ExpansionLayers>
<ExpansionLayer Reference="8">
<ExpansionLayer Reference="9">
</ExpansionLayers>
</Expansion>
<Expansion Name="Order">
<FoundationLayers>
<FoundationLayer Reference="4">
<FoundationLayer Reference="11">
</FoundationLayers>
<ExpansionLayers>
<ExpansionLayer Reference="5">
</ExpansionLayers>
</Expansion>
<Expansion Name="Product">
<FoundationLayers>
<FoundationLayer Reference="1">
<FoundationLayer Reference="12">
</FoundationLayers>
<ExpansionLayers>
</ExpansionLayers>
</Expansion>
<Expansion Name="Territory">
<FoundationLayers>
<FoundationLayer Reference="10">
</FoundationLayers>
<ExpansionLayers>
</ExpansionLayers>
</Expansion>
</Expansions>
</Model>
访问父模型数据
你还需要能够直接访问任何模型数据实例的任何父数据(以及该父项的父数据等)。我们的模型结构非常浅,但让我们为基础层和扩展层节点添加一个描述,其中包含父扩展的名称。因此,对于当前的基础层或扩展层实例,我们通过实例的父扩展获取名称(使用../语法在第 20 行和第 29 行可见):
下面是用 Northwind 模型数据生成的 XML,其中描述属性包含父扩展的名称。(示例下载中的 Mo+ 模板名为 FEBrowseChildrenAccessParent)
<Model>
<Foundations>
<Foundation Name="CustomerDemographic"/>
<Foundation Name="Category"/>
<Foundation Name="Customer"/>
<Foundation Name="Region"/>
<Foundation Name="Shipper"/>
<Foundation Name="Supplier"/>
</Foundations>
<Expansions>
<Expansion Name="CustomerCustomerDemo">
<FoundationLayers>
<FoundationLayer Description="Provides a layer for CustomerCustomerDemo to 3.">
<FoundationLayer Description="Provides a layer for CustomerCustomerDemo to 4.">
</FoundationLayers>
<ExpansionLayers>
</ExpansionLayers>
</Expansion>
<Expansion Name="Employee">
<FoundationLayers>
</FoundationLayers>
<ExpansionLayers>
<ExpansionLayer Description="Provides a layer for Employee to 5.">
</ExpansionLayers>
</Expansion>
<Expansion Name="EmployeeTerritory">
<FoundationLayers>
</FoundationLayers>
<ExpansionLayers>
<ExpansionLayer Description="Provides a layer for EmployeeTerritory to 5.">
<ExpansionLayer Description="Provides a layer for EmployeeTerritory to 13.">
</ExpansionLayers>
</Expansion>
<Expansion Name="OrderDetail">
<FoundationLayers>
</FoundationLayers>
<ExpansionLayers>
<ExpansionLayer Description="Provides a layer for OrderDetail to 8.">
<ExpansionLayer Description="Provides a layer for OrderDetail to 9.">
</ExpansionLayers>
</Expansion>
<Expansion Name="Order">
<FoundationLayers>
<FoundationLayer Description="Provides a layer for Order to 4.">
<FoundationLayer Description="Provides a layer for Order to 11.">
</FoundationLayers>
<ExpansionLayers>
<ExpansionLayer Description="Provides a layer for Order to 5.">
</ExpansionLayers>
</Expansion>
<Expansion Name="Product">
<FoundationLayers>
<FoundationLayer Description="Provides a layer for Product to 1.">
<FoundationLayer Description="Provides a layer for Product to 12.">
</FoundationLayers>
<ExpansionLayers>
</ExpansionLayers>
</Expansion>
<Expansion Name="Territory">
<FoundationLayers>
<FoundationLayer Description="Provides a layer for Territory to 10.">
</FoundationLayers>
<ExpansionLayers>
</ExpansionLayers>
</Expansion>
</Expansions>
</Model>
按条件搜索模型数据
我们希望将扩展层和基础层描述中的数字值替换为相应扩展和基础的实际名称。但是,我们这个简单的小模型结构并没有从层到它们的引用的直接链接。那么,我们如何获取这些数据呢?我们需要搜索它!
下面,我们通过相关 ID 搜索相应的基础和扩展以获取名称(在第 19 行和第 31 行的with语句中使用Find子句可见):
下面是用 Northwind 模型数据生成的 XML,其中包含引用的名称。(示例下载中的 Mo+ 模板名为 FEBrowseAndSearch)
<Model>
<Foundations>
<Foundation Name="CustomerDemographic"/>
<Foundation Name="Category"/>
<Foundation Name="Customer"/>
<Foundation Name="Region"/>
<Foundation Name="Shipper"/>
<Foundation Name="Supplier"/>
</Foundations>
<Expansions>
<Expansion Name="CustomerCustomerDemo">
<FoundationLayers>
<FoundationLayer Name="CustomerDemographic" Description="Provides a layer for CustomerCustomerDemo to CustomerDemographic.">
<FoundationLayer Name="Customer" Description="Provides a layer for CustomerCustomerDemo to Customer.">
</FoundationLayers>
<ExpansionLayers>
</ExpansionLayers>
</Expansion>
<Expansion Name="Employee">
<FoundationLayers>
</FoundationLayers>
<ExpansionLayers>
<ExpansionLayer Name="Employee" Description="Provides a layer for Employee to Employee.">
</ExpansionLayers>
</Expansion>
<Expansion Name="EmployeeTerritory">
<FoundationLayers>
</FoundationLayers>
<ExpansionLayers>
<ExpansionLayer Name="Employee" Description="Provides a layer for EmployeeTerritory to Employee.">
<ExpansionLayer Name="Territory" Description="Provides a layer for EmployeeTerritory to Territory.">
</ExpansionLayers>
</Expansion>
<Expansion Name="OrderDetail">
<FoundationLayers>
</FoundationLayers>
<ExpansionLayers>
<ExpansionLayer Name="Order" Description="Provides a layer for OrderDetail to Order.">
<ExpansionLayer Name="Product" Description="Provides a layer for OrderDetail to Product.">
</ExpansionLayers>
</Expansion>
<Expansion Name="Order">
<FoundationLayers>
<FoundationLayer Name="Customer" Description="Provides a layer for Order to Customer.">
<FoundationLayer Name="Shipper" Description="Provides a layer for Order to Shipper.">
</FoundationLayers>
<ExpansionLayers>
<ExpansionLayer Name="Employee" Description="Provides a layer for Order to Employee.">
</ExpansionLayers>
</Expansion>
<Expansion Name="Product">
<FoundationLayers>
<FoundationLayer Name="Category" Description="Provides a layer for Product to Category.">
<FoundationLayer Name="Supplier" Description="Provides a layer for Product to Supplier.">
</FoundationLayers>
<ExpansionLayers>
</ExpansionLayers>
</Expansion>
<Expansion Name="Territory">
<FoundationLayers>
<FoundationLayer Name="Region" Description="Provides a layer for Territory to Region.">
</FoundationLayers>
<ExpansionLayers>
</ExpansionLayers>
</Expansion>
</Expansions>
</Model>
处理异常
好的,以各种方式遍历和搜索模型数据以生成所需的代码都是很有益的。但是,不可避免地,你想要的 कोड 并不总是能整齐一致地映射到你的模型数据,总会有异常,你需要管理这些异常。
处理异常的一种方法是将你的模型数据的各种特定实例“标记”上任意数量的术语,并根据这些标签的存在(或不存在)执行其他操作。我们将客户人口统计基础实例和客户客户演示扩展实例标记为“忽略”,因为我们决定不生成这些节点到我们的 XML 中。下面,我们跳过被忽略的基础和扩展(如第 4 行和第 12 行的where子句所示):
下面是用 Northwind 模型数据生成的 XML,其中移除了被忽略的客户人口统计和客户客户演示节点。(示例下载中的 Mo+ 模板名为 FEExceptions)
<Model>
<Foundations>
<Foundation Name="Category"/>
<Foundation Name="Customer"/>
<Foundation Name="Region"/>
<Foundation Name="Shipper"/>
<Foundation Name="Supplier"/>
</Foundations>
<Expansions>
<Expansion Name="Employee">
<FoundationLayers>
</FoundationLayers>
<ExpansionLayers>
<ExpansionLayer Name="Employee" Description="Provides a layer for Employee to Employee.">
</ExpansionLayers>
</Expansion>
<Expansion Name="EmployeeTerritory">
<FoundationLayers>
</FoundationLayers>
<ExpansionLayers>
<ExpansionLayer Name="Employee" Description="Provides a layer for EmployeeTerritory to Employee.">
<ExpansionLayer Name="Territory" Description="Provides a layer for EmployeeTerritory to Territory.">
</ExpansionLayers>
</Expansion>
<Expansion Name="OrderDetail">
<FoundationLayers>
</FoundationLayers>
<ExpansionLayers>
<ExpansionLayer Name="Order" Description="Provides a layer for OrderDetail to Order.">
<ExpansionLayer Name="Product" Description="Provides a layer for OrderDetail to Product.">
</ExpansionLayers>
</Expansion>
<Expansion Name="Order">
<FoundationLayers>
<FoundationLayer Name="Customer" Description="Provides a layer for Order to Customer.">
<FoundationLayer Name="Shipper" Description="Provides a layer for Order to Shipper.">
</FoundationLayers>
<ExpansionLayers>
<ExpansionLayer Name="Employee" Description="Provides a layer for Order to Employee.">
</ExpansionLayers>
</Expansion>
<Expansion Name="Product">
<FoundationLayers>
<FoundationLayer Name="Category" Description="Provides a layer for Product to Category.">
<FoundationLayer Name="Supplier" Description="Provides a layer for Product to Supplier.">
</FoundationLayers>
<ExpansionLayers>
</ExpansionLayers>
</Expansion>
<Expansion Name="Territory">
<FoundationLayers>
<FoundationLayer Name="Region" Description="Provides a layer for Territory to Region.">
</FoundationLayers>
<ExpansionLayers>
</ExpansionLayers>
</Expansion>
</Expansions>
</Model>
与自定义集成
最后,能够将生成的面向模型代码与你的自定义代码无缝集成至关重要。通过 MOD,你可能在开发生命周期的后期引入了模型,并可能随着时间的推移从模型中集成零散的部分。无论哪种情况,你总是希望保持高质量和必需的自定义代码。
如果你的生成代码和自定义代码位于不同的文件中,并且彼此了解集成点(这通常是理想的),那么集成通常不是什么大问题。但是,如果自定义代码和生成代码需要位于同一个文件中怎么办?在我们的例子中,我们希望将自定义代码集成到我们生成的 XML 文件中。
管理此问题的一种方法是能够定义自定义开始和结束点。在生成代码的这些区域内,即使生成代码更新后,也可以维护自定义代码。
我们希望为我们的员工扩展实例插入一些自定义节点。因此,我们将此实例标记为“CustomNodes”,并修改我们的模板以创建带有这样标记的扩展的自定义点(作为注释)(参见第 16-21 行):
下面是用 Northwind 模型数据生成的 XML,其中包含员工的自定义点。请注意,名为 CustomNode 的节点是一段插入的自定义代码。 此自定义会在文件重新生成时得到维护。 (示例下载中的 Mo+ 模板名为 FEIntegrateCustomizations,请参阅该模板的输出区域,其中定义了自定义的开始和结束点以及更新文件的规则。当您单击此解决方案的“更新输出解决方案”时,会调用此模板。)
<Model>
<Foundations>
<Foundation Name="Category"/>
<Foundation Name="Customer"/>
<Foundation Name="Region"/>
<Foundation Name="Shipper"/>
<Foundation Name="Supplier"/>
</Foundations>
<Expansions>
<Expansion Name="Employee">
<!-- region protected -->
<CustomNode Name="This is a test!" />
<!-- endregion protected -->
<FoundationLayers>
</FoundationLayers>
<ExpansionLayers>
<ExpansionLayer Name="Employee" Description="Provides a layer for Employee to Employee.">
</ExpansionLayers>
</Expansion>
<Expansion Name="EmployeeTerritory">
<FoundationLayers>
</FoundationLayers>
<ExpansionLayers>
<ExpansionLayer Name="Employee" Description="Provides a layer for EmployeeTerritory to Employee.">
<ExpansionLayer Name="Territory" Description="Provides a layer for EmployeeTerritory to Territory.">
</ExpansionLayers>
</Expansion>
<Expansion Name="OrderDetail">
<FoundationLayers>
</FoundationLayers>
<ExpansionLayers>
<ExpansionLayer Name="Order" Description="Provides a layer for OrderDetail to Order.">
<ExpansionLayer Name="Product" Description="Provides a layer for OrderDetail to Product.">
</ExpansionLayers>
</Expansion>
<Expansion Name="Order">
<FoundationLayers>
<FoundationLayer Name="Customer" Description="Provides a layer for Order to Customer.">
<FoundationLayer Name="Shipper" Description="Provides a layer for Order to Shipper.">
</FoundationLayers>
<ExpansionLayers>
<ExpansionLayer Name="Employee" Description="Provides a layer for Order to Employee.">
</ExpansionLayers>
</Expansion>
<Expansion Name="Product">
<FoundationLayers>
<FoundationLayer Name="Category" Description="Provides a layer for Product to Category.">
<FoundationLayer Name="Supplier" Description="Provides a layer for Product to Supplier.">
</FoundationLayers>
<ExpansionLayers>
</ExpansionLayers>
</Expansion>
<Expansion Name="Territory">
<FoundationLayers>
<FoundationLayer Name="Region" Description="Provides a layer for Territory to Region.">
</FoundationLayers>
<ExpansionLayers>
</ExpansionLayers>
</Expansion>
</Expansions>
</Model>
另一个示例 - 基于类的模型
我们将通过一个完全不同的模型执行相同的步骤,以展示过程是相同的。我们经常使用 MOD 来生成面向模型的软件(通常是面向对象的语言),所以让我们创建一个可以用来建模类的简单模型。
模型结构
考虑以下代表我们预期类模型结构的图表
这个简单的类模型结构的规则也相当简单:
- 类代表面向对象语言中的一个类。类由ClassID标识,并由ClassName命名。类可以进一步由自由格式的标签描述。
- 属性代表类的简单类型成员,例如字符串或数字字段或属性。属性由AttributeID标识,并由AttributeName命名。属性以ValueType枚举(我们仅使用 2 种数据类型,保持简单)的形式保存字符串或数字值。
- 引用代表类的成员,该成员引用另一个类的实例。引用由ReferenceID标识,并由ReferenceName命名。引用通过ReferencedClassID引用一个类的实例。
- 一个类可以包含零个或多个属性。
- 一个类可以包含零个或多个引用。
模型数据
同样,我们将使用 Northwind 数据库架构作为模型数据源。这次,我们将按如下方式填充模型:
- 每个表成为一个类
- 表名(稍作修改)成为类名
- 类 ID 已生成
- 每列成为相应类中的一个属性
- 列名(稍作修改)成为属性名
- 列数据类型成为值类型(简化为字符串或数字)
- 属性 ID 已生成
- 每个外键成为一个引用
- 引用名基于被引用类的名称
- 被引用类 ID 是基于被引用表创建的类的 ID
- 引用 ID 已生成
以下树状视图以原始形式说明了此模型数据
请注意,客户、员工和订单等是类。员工区域类被扩展以显示属性(EmployeeID和TerritoryID)以及引用(到Employee和Territory)。
如果您正在使用 Mo+,请从示例下载中加载 ClassModel.xml 解决方案模型,这将创建此模型结构并在您连接到 Northwind(或其他 SQL Server)数据库时对其进行填充。本文中描述的代码模板也将直接可用。
MOD - 使用模型生成 C#
我们将使用填充的模型来生成类和类中的成员。在代码模板中,我们将以各种方式遍历模型,利用模型数据(结合原始模板文本)来生成 C# 类及其详细信息。
模型数据通用浏览
同样,你需要能够浏览模型数据,以获取模型结构中任何给定对象的全部实例。下面,我们通过浏览模型中的每个类来创建类(在第 2 行的foreach语句中可见):
下面是用 Northwind 模型数据生成的 C# 类集。类是按其名称生成的。(示例下载中的 Mo+ 模板名为 ClassBrowseOnly)
public class CustomerCustomerDemo
{
}
public class CustomerDemographic
{
}
public class Category
{
}
public class Customer
{
}
public class Employee
{
}
public class EmployeeTerritory
{
}
public class OrderDetail
{
}
public class Order
{
}
public class Product
{
}
public class Region
{
}
public class Shipper
{
}
public class Supplier
{
}
public class Territory
{
}
访问子模型数据
作为访问子数据的一个例子,我们希望访问特定类的所有属性。下面,我们遍历当前类的子属性(在第 7 行的foreach语句中可见)。我们希望将属性生成为 get/set 属性。
下面是用 Northwind 模型数据生成的 C# 类集,包含基本的 get/set 属性(从这里开始的生成代码中的一些细节被省略号遮挡)。(示例下载中的 Mo+ 模板名为 ClassBrowseChildren)
public class CustomerCustomerDemo
{
public string CustomerID { get; set; }
public string CustomerTypeID { get; set; }
}
public class CustomerDemographic
{
public string CustomerTypeID { get; set; }
public string CustomerDesc { get; set; }
}
public class Category
{
public string Picture { get; set; }
public int CategoryID { get; set; }
public string CategoryName { get; set; }
public string Description { get; set; }
}
public class Customer
{
public string CustomerID { get; set; }
public string CompanyName { get; set; }
public string ContactName { get; set; }
.
.
.
}
.
.
.
public class Order
{
public int OrderID { get; set; }
public string CustomerID { get; set; }
public int EmployeeID { get; set; }
.
.
.
}
public class Product
{
public int ProductID { get; set; }
public string ProductName { get; set; }
.
.
.
}
.
.
.
访问父模型数据
对于访问父模型数据,同样,我们的模型结构非常浅,但让我们添加一些注释,并让属性的注释包含父类的名称(在第 14 行使用Class.表示法可见):
下面是用 Northwind 模型数据生成的更新后的 C# 类集,包含注释。(示例下载中的 Mo+ 模板名为 ClassBrowseChildrenAccessParent)
/// <summary>
/// This class holds data for CustomerCustomerDemo elements.
/// </summary>
public class CustomerCustomerDemo
{
// Provide access to CustomerID data for the corresponding CustomerCustomerDemo instance.
public string CustomerID { get; set; }
// Provide access to CustomerTypeID data for the corresponding CustomerCustomerDemo instance.
public string CustomerTypeID { get; set; }
}
/// <summary>
/// This class holds data for CustomerDemographic elements.
/// </summary>
public class CustomerDemographic
{
// Provide access to CustomerTypeID data for the corresponding CustomerDemographic instance.
public string CustomerTypeID { get; set; }
// Provide access to CustomerDesc data for the corresponding CustomerDemographic instance.
public string CustomerDesc { get; set; }
}
/// <summary>
/// This class holds data for Category elements.
/// </summary>
public class Category
{
// Provide access to Picture data for the corresponding Category instance.
public string Picture { get; set; }
// Provide access to CategoryID data for the corresponding Category instance.
public int CategoryID { get; set; }
// Provide access to CategoryName data for the corresponding Category instance.
public string CategoryName { get; set; }
// Provide access to Description data for the corresponding Category instance.
public string Description { get; set; }
}
/// <summary>
/// This class holds data for Customer elements.
/// </summary>
public class Customer
{
// Provide access to CustomerID data for the corresponding Customer instance.
public string CustomerID { get; set; }
// Provide access to CompanyName data for the corresponding Customer instance.
public string CompanyName { get; set; }
// Provide access to ContactName data for the corresponding Customer instance.
public string ContactName { get; set; }
.
.
.
}
.
.
.
/// <summary>
/// This class holds data for Order elements.
/// </summary>
public class Order
{
// Provide access to OrderID data for the corresponding Order instance.
public int OrderID { get; set; }
// Provide access to CustomerID data for the corresponding Order instance.
public string CustomerID { get; set; }
// Provide access to EmployeeID data for the corresponding Order instance.
public int EmployeeID { get; set; }
.
.
.
}
/// <summary>
/// This class holds data for Product elements.
/// </summary>
public class Product
{
// Provide access to ProductID data for the corresponding Product instance.
public int ProductID { get; set; }
// Provide access to ProductName data for the corresponding Product instance.
public string ProductName { get; set; }
.
.
.
}
.
.
.
按条件搜索模型数据
现在我们要将引用添加为类的 get/set 属性。同样,我们这个简单的小模型结构并没有从引用到它们的类的直接链接。所以,我们必须搜索这些引用。
我们添加了浏览引用的功能来添加引用 get/set 属性(如第 19-28 行所示),并且我们通过 ID 搜索相应的类以获取数据类型和注释的正确名称(如第 21 行的with语句中使用Find子句可见)。
下面是用 Northwind 模型数据生成的更新后的 C# 类集,包含引用的 get/set 属性。(示例下载中的 Mo+ 模板名为 ClassBrowseAndSearch)
/// <summary>
/// This class holds data for CustomerCustomerDemo elements.
/// </summary>
public class CustomerCustomerDemo
{
// Provide access to CustomerID data for the corresponding CustomerCustomerDemo instance.
public string CustomerID { get; set; }
// Provide access to CustomerTypeID data for the corresponding CustomerCustomerDemo instance.
public string CustomerTypeID { get; set; }
// Provide reference to corresponding CustomerDemographic instance.
public CustomerDemographic CustomerDemographic { get; set; }
// Provide reference to corresponding Customer instance.
public Customer Customer { get; set; }
}
/// <summary>
/// This class holds data for CustomerDemographic elements.
/// </summary>
public class CustomerDemographic
{
// Provide access to CustomerTypeID data for the corresponding CustomerDemographic instance.
public string CustomerTypeID { get; set; }
// Provide access to CustomerDesc data for the corresponding CustomerDemographic instance.
public string CustomerDesc { get; set; }
}
/// <summary>
/// This class holds data for Category elements.
/// </summary>
public class Category
{
// Provide access to Picture data for the corresponding Category instance.
public string Picture { get; set; }
// Provide access to CategoryID data for the corresponding Category instance.
public int CategoryID { get; set; }
// Provide access to CategoryName data for the corresponding Category instance.
public string CategoryName { get; set; }
// Provide access to Description data for the corresponding Category instance.
public string Description { get; set; }
}
/// <summary>
/// This class holds data for Customer elements.
/// </summary>
public class Customer
{
// Provide access to CustomerID data for the corresponding Customer instance.
public string CustomerID { get; set; }
// Provide access to CompanyName data for the corresponding Customer instance.
public string CompanyName { get; set; }
// Provide access to ContactName data for the corresponding Customer instance.
public string ContactName { get; set; }
.
.
.
}
.
.
.
/// <summary>
/// This class holds data for EmployeeTerritory elements.
/// </summary>
public class EmployeeTerritory
{
// Provide access to EmployeeID data for the corresponding EmployeeTerritory instance.
public int EmployeeID { get; set; }
// Provide access to TerritoryID data for the corresponding EmployeeTerritory instance.
public string TerritoryID { get; set; }
// Provide reference to corresponding Employee instance.
public Employee Employee { get; set; }
// Provide reference to corresponding Territory instance.
public Territory Territory { get; set; }
}
.
.
.
处理异常
同样,我们选择通过标记来处理异常。我们将客户人口统计和客户客户演示类实例标记为“忽略”,因为我们决定不生成这些类。下面,我们跳过被忽略的类(如第 2 行的 where 子句所示):
下面是用 Northwind 模型数据生成的更新后的 C# 类集,其中移除了被忽略的客户人口统计和客户客户演示类。(示例下载中的 Mo+ 模板名为 ClassExceptions)
/// <summary>
/// This class holds data for Category elements.
/// </summary>
public class Category
{
// Provide access to Picture data for the corresponding Category instance.
public string Picture { get; set; }
// Provide access to CategoryID data for the corresponding Category instance.
public int CategoryID { get; set; }
// Provide access to CategoryName data for the corresponding Category instance.
public string CategoryName { get; set; }
// Provide access to Description data for the corresponding Category instance.
public string Description { get; set; }
}
/// <summary>
/// This class holds data for Customer elements.
/// </summary>
public class Customer
{
// Provide access to CustomerID data for the corresponding Customer instance.
public string CustomerID { get; set; }
// Provide access to CompanyName data for the corresponding Customer instance.
public string CompanyName { get; set; }
// Provide access to ContactName data for the corresponding Customer instance.
public string ContactName { get; set; }
.
.
.
}
.
.
.
/// <summary>
/// This class holds data for EmployeeTerritory elements.
/// </summary>
public class EmployeeTerritory
{
// Provide access to EmployeeID data for the corresponding EmployeeTerritory instance.
public int EmployeeID { get; set; }
// Provide access to TerritoryID data for the corresponding EmployeeTerritory instance.
public string TerritoryID { get; set; }
// Provide reference to corresponding Employee instance.
public Employee Employee { get; set; }
// Provide reference to corresponding Territory instance.
public Territory Territory { get; set; }
}
.
.
.
与自定义集成
同样,我们将定义自定义的开始和结束点,以将一些复杂的自定义集成到我们生成的类中。在这些自定义点(或受保护区域)内,我们可以插入和维护自定义代码。
我们希望能够为一些 get/set 属性添加自定义代码。因此,我们将(类别类)图片属性实例标记为“CustomGetSet”,并修改我们的模板以创建带有这样标记的属性的自定义点(作为 C# 区域)(参见第 15-37 行):
下面是用 Northwind 模型数据生成的更新后的 C# 类集,其中包含图片 get/set 属性的自定义点。请注意,图片的 getter 和 setter 在受保护区域内部包含一些自定义注释。这些自定义会在文件重新生成时得到维护。(示例下载中的 Mo+ 模板名为 ClassIntegrateCustomizations,请参阅该模板的输出区域,其中定义了自定义的开始和结束点以及更新文件的规则。当您单击此解决方案的“更新输出解决方案”时,会调用此模板。)
/// <summary>
/// This class holds data for Category elements.
/// </summary>
public class Category
{
// Provide access to Picture data for the corresponding Category instance.
private string picture
public string Picture
{
get
{
#region protected
// this is a customization
#endregion protected
return picture;
}
set
{
picture = value;
#region protected
// this is another customization
#endregion protected
}
}
// Provide access to CategoryID data for the corresponding Category instance.
public int CategoryID { get; set; }
// Provide access to CategoryName data for the corresponding Category instance.
public string CategoryName { get; set; }
// Provide access to Description data for the corresponding Category instance.
public string Description { get; set; }
}
/// <summary>
/// This class holds data for Customer elements.
/// </summary>
public class Customer
{
// Provide access to CustomerID data for the corresponding Customer instance.
public string CustomerID { get; set; }
// Provide access to CompanyName data for the corresponding Customer instance.
public string CompanyName { get; set; }
.
.
.
}
.
.
.
/// <summary>
/// This class holds data for EmployeeTerritory elements.
/// </summary>
public class EmployeeTerritory
{
// Provide access to EmployeeID data for the corresponding EmployeeTerritory instance.
public int EmployeeID { get; set; }
// Provide access to TerritoryID data for the corresponding EmployeeTerritory instance.
public string TerritoryID { get; set; }
// Provide reference to corresponding Employee instance.
public Employee Employee { get; set; }
// Provide reference to corresponding Territory instance.
public Territory Territory { get; set; }
}
.
.
.
总结
总之,为了实现有效的面向模型的开发(MOD),你需要完全轻松地访问模型的结构和数据,你需要能够管理异常,并且你需要将你的自定义代码与你的面向模型代码无缝集成。
希望本文能让你对从结构和数据的角度更深入地研究模型有所启发。无论你选择创建自己的模型结构还是使用现有的标准化结构,我都希望这里的示例能让你了解如何有效地利用你的模型结构和数据来实现有效的面向模型的开发。
此外,我希望你能尝试Mo+和Mo+ Solution Builder,以充分利用面向模型的开发方法。Mo+ 内置了一个灵活的通用实体关系模型结构,但它也允许你创建和使用自己的模型结构(或者根据你的选择重新创建其他标准化结构)。这个免费的开源产品可在moplus.codeplex.com获得。除了产品本身,该网站还包含用于构建更完整的模型和生成完整工作应用程序的示例包。此外,该网站还提供视频教程和其他材料。Mo+ Solution Builder还包含广泛的内置帮助。
成为会员!
Mo+ 社区通过https://modelorientedplus.com网站获得额外的支持,并为 Mo+ 的发展做出贡献。成为会员可以为 Mo+ 用户带来额外的好处,例如额外的论坛支持、会员贡献的工具,以及对 Mo+ 方向进行投票和/或贡献的能力。此外,我们将每月为会员举办竞赛,让他们可以通过使用 Mo+ 开发解决方案来赢取奖金。