field.type.arrayelementtype );
// change field type to collection.
field.type = new codetypereference( col.name );
code.types.add( col );
}
}
}
}
}
正像我在前面所做的,我复制了需要修改的集合;在此例中,是 codenamespace.types。
进一步的自定义可以包括:向生成的类中添加 [serializable],添加 dal 方法(即 loadbyid、findbykey、save、delete 等),生成被序列化操作忽略但由您的代码使用的成员(应用 xmlignoreattribute),省略属于外部导入架构的类的生成,等等。
返回页首
映射技巧
如果您要更深入地研究代码生成工具本身,或者希望更进一步地自定义架构处理,那么您或许会对下列与 codegen 类有关的高级问题感兴趣。如果您只是要开发扩展和操纵 codedom,则它们对您不会有多大价值,您可以跳过本部分而不会有任何问题。
我已经通过检索元素的 xmltypemapping 来处理这些元素;我尚未使用其任何属性,但如果您必须要找到与元素对应的 codetypedeclaration,则可能需要使用这些属性。有关 xmltypemapping 属性及其含义的简短说明,请参阅 msdn 文档。但是,该类用在许多方案中,如该文档中所示的 soapreflectionimporter 映射导入。至于我所使用的 xmlschemaimporter,我已经发现 xmltypemapping.typefullname 和 xmltypemapping.typename 对一个特定架构元素的设计具有不正确的行为:如果该元素在某个序列内部包含单个未绑定的子元素,则两者都将错误地假定子属性的类型。
因此,对于以下架构元素:
<xs:element name="pubs"> <xs:complextype> <xs:sequence> <xs:element name="publishers" type="publisher" maxoccurs="unbounded" /> </xs:sequence> </xs:complextype> </xs:element>
xmltypemapping.typefullname 和 xmltypemapping.typename 都没有“pubs”值(这是将要生成的类型),而是具有值“publisher[]”,这是其唯一属性的类型。如果该序列具有一个以上的元素,则一切都可以按预期方式工作。请注意,无论元素的类型是否为命名的全局类型,或者无论该元素本身是否为引用,这一(明显的)错误都适用。
除了类型映射以外,xmlschemaimporter 还可以检索将应用于其成员(字段)的映射。这很有用,因为 xsd/clr 类型映射(包括 xsd 自定义派生类型)将被解析,并且您可以确信它就是由 xmlserializer 使用的那个映射。您可以按如下方式获得成员映射:
xmlmembersmapping mmap = importer.importmembersmapping(
element.qualifiedname );
int count = mmap.count;
for (int i = 0; i < count; i++)
{
xmlmembermapping map = mmap[i];
//you have now:
// map.elementname
// map.membername
// map.typefullname
// map.typename
}
xmlmembermapping.typefullname 容纳命名空间限定的 clr 类型,尽管 xmlmembermapping.typename 具有 xsd 类型名。例如,对于 xsd 类型“xs:positiveinteger”的成员,前者将是“system.string”,而后者将是“positiveinteger”。如果您没有访问该成员映射检索的权限,则必须知道 xmlserializer 所使用的所有 xsd 到 clr 类型转换规则。请注意,这些规则不必与用于 xsd 验证和 dom psvi 的规则相同。
对于成员导入,有一个重要的警告(同样,明显是一个错误)。您不能重用 xmlschemaimporter,否则将得到由导入代码在 xmlmembersmapping 构建时引发的 invalidcastexception。这可以通过每次使用导入程序的新实例来加以解决。
有了这些信息,您可以彻底更改类的外观,例如,重命名属性以使首字母变成大写,而不会对序列化基础结构产生危害。
当我讨论 codegen 类的基本原理时,我说过您只能为全局定义的元素检索(导入)映射;如果您创建自己的自定义特性以修改得到的类,则将只能够针对顶级元素检索和分析它们,因为您将只具有这些元素的映射。例如,假设您添加了一个 code:classname 特性,该特性被某个扩展用来更改生成的类名:
<xs:schema xmlns:code="http://weblogs.asp.net/cazzu" ...> <xs:element name="pubs" code:classname="publishersdata"> <xs:complextype> <xs:sequence> <xs:element name="publishers" code:classname="publishers"> <xs:complextype>
您将能够为 pubs 元素检索这些映射,但无法为 publishers 子元素检索这些映射。因此,对其进行处理将是不安全的,因为 codegen 类将来可能发生更改。如果不能控制映射,您就不能简单地假设相应的 codetypedeclaration 将具有与该元素相同的名称(以便找到和更改它)。当然,您可以自行决定是否可以接受这种危险。
返回页首
小结
通过重用为 xmlserializer 创建的内置代码生成功能,可以确保对所生成的代码进行少量更改不会破坏 xml 序列化。通过 codedom 直接操纵其输出也可以增加灵活性。我在本文中说明了如何通过灵活处理 xml 架构来实现任意扩展以改变输出,并且开发了一些有用的示例。
在奠定这一牢固的基础之后,您可以继续研究更高级的方案,例如:外部(导入的/包含的)xsd 架构及其与代码生成的关系,操纵代码输出以重新使用应用程序或企业范围的储备库中的类型定义(包括 xsd 和相应的生成 .net 类型),等等。