SqlMetalPlus - 用于管理 DBML 自定义更改的 VS 插件






4.90/5 (33投票s)
此插件可轻松访问 DBML 文件的上下文菜单命令,用于应用自定义更改或使用数据库的最新更改刷新整个 DBML 文件。

引言
在我们的一个项目中 O(n) 使用 LINQ to SQL 时,我们遇到了一个需要对 SqlMetal
生成的 DBML 文件进行大量更改的情况,例如:
- 将所有查找值更改为
enum
,以使我们的代码看起来更优雅 - 将生成的关联成员名称修改为我们自己的名称,以提高可读性
- 删除不需要的表/存储过程
- 将 DBML 导出为图像
- 等等。
在尝试进行上述所有更改时,每次重新生成 DBML 时重复相同的更改都变得很麻烦。因此,我开始编写一个小脚本,最终将其转换为 VS 插件。
安装插件
我已将 Wix 项目与插件源代码一起包含在内,该项目生成 MSI。所以,只需安装即可开始使用。
工作原理
插件会在与 DBML 文件相同的文件夹中查找一个 XML 文件,其名称为 DBML 文件名 + "custom.xml"。因此,如果您的 DBML 文件名为 Northwind.dbml,那么您的自定义映射文件应命名为 "northwind.dbml.custom.xml"。
该插件为解决方案中的所有 DBML 文件添加了两个上下文菜单选项。
- 创建映射 XML
- 此命令仅为您创建初始映射文件以进行自定义。它只是复制 DBML 文件并更改文件名。
- 应用自定义
- 此命令将自定义更改应用于现有 DBML 文件。
- 创建/刷新
- 此命令要么创建初始 DBML(或)使用数据库的最新更改刷新整个 DBML,然后应用自定义更改。
这两个命令都会自动重新生成 designer.cs 和布局文件,以便您在任务完成后在设计器中查看修改后的 DBML。
如何创建自定义映射 XML
只需复制 DBML 文件,然后根据命名约定重命名,并在末尾添加 "custom.xml"。此方法的优点之一是 Visual Studio 为所有可能的属性和节点名称提供自动完成功能。您可以添加一个名为 "CustomizationType
" 的自定义属性,它接受 "Add
"、"Update
"、"Delete
",用于指定您正在寻找的特定节点的自定义。
例如,如果您想使数据库中的特定列在代码中不可见,出于任何原因,您可以如下更新映射文件中的列节点:
<Column CustomizationType="Delete" Name="Description"
Type="System.String" DbType="VarChar(50) NOT NULL"
CanBeNull="false" />
要将查找列类型更改为 enum
,您可以如下修改自定义映射文件中的节点:
初始版本
<Column Name="ProductStatusCd" Type="System.Int16"
DbType="SmallInt NOT NULL" CanBeNull="false" />
在自定义 XML 中
<Column Name="ProductStatusCd" Member="ProductStatus"
Type="global::SampleApplication.ProductStatus"
DbType="SmallInt NOT NULL" CanBeNull="false" />
如果您注意到上面的自定义更改,我还将 Member
属性更改为 "ProductStatus
",因此在我的代码中,我可以将此列称为 "ProductStatus
" 而不是 "ProductStatusCd
"。
现在,我可以像下面这样编写我的 LINQ 查询,这更优雅:
var activeProducts = from p in dbContext.Products
where p.ProductStatus == ProductStatus.Active
select p;
同样,您可以进行 VS 设计器允许的任何自定义更改,并保存自定义 XML 文件,该插件将负责将这些更改应用于最终的 DBML 文件。
截图展示用法
- 使用 Visual Studio 添加新对话框将一个新的 DBML 文件添加到项目中。这将向项目添加一个空的 DBML 文件。
- 选择插件的 "创建/刷新" 命令,从给定的数据库连接生成 DBML。
- 提供连接详细信息以及
sqlMetal
特定属性,如序列化类型等。 - 下面的屏幕显示了初始生成的 DBML。现在我们需要为此创建自定义映射文件。
- 选择 "创建映射 XML" 命令,根据初始 DBML 文件创建基本映射 XML。我们使用此文件来指定我们的自定义。如果您不需要任何自定义,可以从该映射文件中删除这些表/列/函数。但保留它们会使插件认为您也需要对这些实体进行一些自定义。但只要我们不指定任何自定义,插件就会忽略它们。
- 这是我在示例中使用的自定义映射文件(现已包含在源代码中)。我已尽力解释,但如果有什么不清楚的地方,请告诉我。
<?xml version="1.0" encoding="utf-8"?> <!--At the root level, you can change attributes like Serialization, Class,EntityNamespace,ContextNamespace. But you can always customize the code if you have any need to change AccessModifier,BaseType etc. You can include any extra namespaces you want to add, using "CustomNapespaces" node. --> <Database Name="TestDB" Serialization="Unidirectional" xmlns="http://schemas.microsoft.com/linqtosql/dbml/2007"> <CustomNamespaces> <Namespace Name="System.IO" Alias="MyAlias"/> <Namespace Name="System.Text"/> </CustomNamespaces> <!-- Possible changes implemented are "Member" and you can add custom attributes (xml serialization,propertygrid related etc) to the generated class. --> <Table Name="dbo.ProductCategories" Member="ProductCategories"> <!-- Possible changes implemented are "Name" and "Id". for example, if your table name in db is "product_category", you can change to "ProductCategory" if you follow specific naming conventions or to satisfy FxCop:) --> <CustomAttributes> <Attribute Name="YourOwnCustomAttribute">"Parameters if any"</Attribute> </CustomAttributes> <Type Name="ProductCategory" > <Column Name="ProductCategoryID" Type="System.Int32" DbType="Int NOT NULL IDENTITY" IsPrimaryKey="true" IsDbGenerated="true" CanBeNull="false" /> <Column Name="Description" Type="System.String" DbType="VarChar(50) NOT NULL" CanBeNull="false" /> <Column Name="ParentCategoryID" Type="System.Int32" DbType="Int" CanBeNull="true" > <CustomAttributes> <Attribute Name="global::System.ComponentModel.Description"> "Category Name"</Attribute> <Attribute Name="DisplayName">"Category"</Attribute> <Attribute Name="Category">"Main Properties"</Attribute> </CustomAttributes> <Column> <Association Name="FK_ProductCategories_ProductCategories" Member="ParentCategory" ThisKey="ParentCategoryID" OtherKey="ProductCategoryID" Type="ProductCategory" IsForeignKey="true" /> <Association Name="FK_ProductCategories_ProductCategories" Member="ChildCategories" ThisKey="ProductCategoryID" OtherKey="ParentCategoryID" Type="ProductCategory" DeleteRule="NO ACTION" /> <Association Name="FK_Products_ProductCategories" Member="Products" ThisKey="ProductCategoryID" OtherKey="ProductCategoryID" Type="Product" DeleteRule="NO ACTION" /> </Type> </Table> <Table Name="dbo.Products" Member="Products"> <Type Name="Product"> <Column Name="ProductID" Type="System.Int32" DbType="Int NOT NULL IDENTITY" IsPrimaryKey="true" IsDbGenerated="true" CanBeNull="false" /> <Column Name="ProductName" Type="System.String" DbType="VarChar(50) NOT NULL" CanBeNull="false" /> <!--Example to add a custom property to the Product--> <Column Name="NewCustomProperty" Type="System.String" CustomizationType="Add"/> <Column Name="ProductCategoryID" Type="System.Int32" DbType="Int NOT NULL" CanBeNull="false" /> <!--Example to change the datatype to enum and change the Member attribute value such that we can refer in our code as "ProductStatus" which is more meaningful than "ProductStatusCd". Also here i have n't specified any CustomizationType.By default its assumed to be "Update".This is just to save some typing as most of the changes we do ,are updates. :) --> <Column Name="ProductStatusCd" Member="ProductStatus" Type="global::SampleApplication.ProductStatus" DbType="SmallInt NOT NULL" CanBeNull="false" /> <!--For Associations, you can mention the Cardinality="One" if you know the relation is always one to one. Other customizations possible are 1.Change the Member attribute to your custom name --> <Association Name="FK_Products_ProductCategories" Member="ProductCategory" ThisKey="ProductCategoryID" OtherKey="ProductCategoryID" Type="ProductCategory" IsForeignKey="true" /> </Type> </Table> <!--For Functions, you can change the Method name to a more meaningful name than the default generated one. Sp_getproducts->GetProducts --> <Function Name="dbo.sp_getproducts" Method="GetProducts"> <!--For Parameter, you can change the "Parameter" to a more meaningful name than the default generated one. category_id->CategoryID --> <Parameter Name="category_id" Parameter="CategoryID" Type="System.Int32" DbType="Int" /> <!--For ElementType, you can change the Name to a more meaningful name than the default generated one. Sp_getproductsResult->ProductDetails --> <ElementType Name="Sp_getproductsResult"> <!--For ElementType Columns, you can change the Member and Type--> <Column Name="ProductID" Type="System.Int32" DbType="Int" CanBeNull="true" /> <Column Name="ProductName" Type="System.String" DbType="VarChar(50)" CanBeNull="true" /> </ElementType> </Function> <Function CustomizationType="Delete" Name="dbo.sp_alterdiagram" Method="Sp_alterdiagram"> <Parameter Name="diagramname" Type="System.String" DbType="NVarChar(128)" /> <Parameter Name="owner_id" Type="System.Int32" DbType="Int" /> <Parameter Name="version" Type="System.Int32" DbType="Int" /> <Parameter Name="definition" Type="System.Data.Linq.Binary" DbType="VarBinary(MAX)" /> <Return Type="System.Int32" DbType="Int" /> </Function> <Function CustomizationType="Delete" Name="dbo.sp_creatediagram" Method="Sp_creatediagram"> <Parameter Name="diagramname" Type="System.String" DbType="NVarChar(128)" /> <Parameter Name="owner_id" Type="System.Int32" DbType="Int" /> <Parameter Name="version" Type="System.Int32" DbType="Int" /> <Parameter Name="definition" Type="System.Data.Linq.Binary" DbType="VarBinary(MAX)" /> <Return Type="System.Int32" DbType="Int" /> </Function> <Function CustomizationType="Delete" Name="dbo.sp_dropdiagram" Method="Sp_dropdiagram"> <Parameter Name="diagramname" Type="System.String" DbType="NVarChar(128)" /> <Parameter Name="owner_id" Type="System.Int32" DbType="Int" /> <Return Type="System.Int32" DbType="Int" /> </Function> <Function CustomizationType="Delete" Name="dbo.sp_helpdiagramdefinition" Method="Sp_helpdiagramdefinition"> <Parameter Name="diagramname" Type="System.String" DbType="NVarChar(128)" /> <Parameter Name="owner_id" Type="System.Int32" DbType="Int" /> <ElementType Name="Sp_helpdiagramdefinitionResult"> <Column Name="version" Member="Version" Type="System.Int32" DbType="Int" CanBeNull="true" /> <Column Name="definition" Member="Definition" Type="System.Data.Linq.Binary" DbType="VarBinary(MAX)" CanBeNull="true" /> </ElementType> </Function> <Function CustomizationType="Delete" Name="dbo.sp_helpdiagrams" Method="Sp_helpdiagrams"> <Parameter Name="diagramname" Type="System.String" DbType="NVarChar(128)" /> <Parameter Name="owner_id" Type="System.Int32" DbType="Int" /> <ElementType Name="Sp_helpdiagramsResult"> <Column Name="Database" Type="System.String" DbType="NVarChar(128)" CanBeNull="true" /> <Column Name="Name" Type="System.String" DbType="NVarChar(128)" CanBeNull="true" /> <Column Name="ID" Type="System.Int32" DbType="Int" CanBeNull="true" /> <Column Name="Owner" Type="System.String" DbType="NVarChar(128)" CanBeNull="true" /> <Column Name="OwnerID" Type="System.Int32" DbType="Int" CanBeNull="true" /> </ElementType> </Function> <Function CustomizationType="Delete" Name="dbo.sp_renamediagram" Method="Sp_renamediagram"> <Parameter Name="diagramname" Type="System.String" DbType="NVarChar(128)" /> <Parameter Name="owner_id" Type="System.Int32" DbType="Int" /> <Parameter Name="new_diagramname" Type="System.String" DbType="NVarChar(128)" /> <Return Type="System.Int32" DbType="Int" /> </Function> </Database>
- 最后选择 "应用自定义" 命令将自定义映射应用于初始 DBML。
- 这是最终的 DBML。
- 一旦您准备好了一个版本的 DBML,您就可以继续更改您的映射文件,并使用 "创建/刷新" 命令从数据库更新 DBML 并自动应用您的自定义更改。
关注点
如果您想使用此插件,请不要使用 VS Designer 创建 DBML,因为出于任何原因,VS Designer 生成的关联键名与 SqlMetal
工具生成的不同。SqlMetal
将关联键命名为与数据库中的外键名称相同,但 VS Designer 将它们更改为 sourcetable_targettable 格式。由于此插件依赖于 sqlmetal.exe 来生成初始 DBML,因此请不要使用 VS Designer 对 DBML 进行任何更改。当然,您可以打开最终的 DBML 并在设计器中查看它,以便清楚地了解已更改的内容以及所有更改是否已正确应用。
结论
我还包含了 Wix 项目的源代码,它将为您提供有关如何使用 Wix (Windows Installer XML) 创建简单安装项目的基础知识。除了解决使用 LINQ to SQL 时遇到的常见问题外,我希望它能帮助某人编写她/他自己的 VS 插件。
欢迎提出任何反馈/建议。
参考文献
我使用了这篇文章来显示 SQL 连接字符串属性(但将代码转换为 C#):https://codeproject.org.cn/KB/vb/SQL_Connection_Dialog.aspx。
历史
- 2009 年 6 月 12 日:发布初始版本
- 2009 年 6 月 14 日:发布更新版本
- 2010 年 2 月 23 日:更新了设置和源文件
- 2010 年 3 月 1 日:更新以支持每个节点(列、表、数据库等)的所有可能属性。自定义 XML 中指定的任何有效属性都将应用于 DBML 文件。
- 2011 年 7 月 14 日:更新了下载文件 - 修复了一些错误
- 2011 年 7 月 24 日:更新以支持包含添加到生成代码的附加命名空间,并支持添加到生成的实体类和属性的自定义属性
- 2012 年 2 月 15 日:为 VB.NET 添加自定义属性的支持
- 2013 年 12 月 6 日:增加了对 VS2012/2013 的支持,并提供了将 DBML 导出为图像的选项