使用代码生成

Codegeneration with usings

我正在使用 CodeDOM 从多个 xsd 文件构建代码。因此,假设我们有一些从 xsd 中定义的名称空间到已创建的程序集中的名称空间的映射:

<?xml version="1.0" encoding="UTF-8"?>
<schema xmlns="http://www.w3.org/2001/XMLSchema" xmlns:abw="MyNamespace" xmlns:bfm="BaseNamespace" 
        xmlns:gml="http://www.opengis.net/gml/3.2" elementFormDefault="qualified" targetNamespace="MyNamespace" 
        version="1.0.1.0">
  <import namespace="BaseNamespace" schemaLocation="Base.xsd"/>
  <import namespace="http://www.opengis.net/gml/3.2" schemaLocation="http://schemas.opengis.net/gml/3.2.1/gml.xsd"/>

  <element name="MyClass" substitutionGroup="bfm:MyBaseClass" type="abw:MyClassType"/>
  <complexType name="MyClassType">
    <complexContent>
      <extension base="bfm:MyBaseClassType">
        <sequence>
          <element maxOccurs="unbounded" minOccurs="0" name="Property1" type="gml:ReferenceType">
            <annotation>
              <appinfo>
                <targetElement xmlns="http://www.opengis.net/gml/3.2">abw:MyClass2</targetElement>
                <reversePropertyName xmlns="http://www.opengis.net/gml/3.2">abw:MyClassAlias</reversePropertyName>
              </appinfo>
            </annotation>
          </element>
        </sequence>
      </extension>
    </complexContent>
  </complexType>
</schema>

现在 - 由于命名空间 BaseNamespacegml 的程序集已经存在 - 我只想构建 MyClass 的源代码,它应该自动引用 base-class bfm:MyBaseClassgml:ReferenceType 通过添加 using 指令。

所以我为 targetNamespace 创建了一个 CodeNamespace,它应用 xsd 中的命名空间 MyNamespace。现在我循环 CodeNamespace 中的类型并过滤那些存在于我的 xsd 中的类型,因为引用的 xsd 文件中的所有其他类型已经 分配给他们的程序集。如果在我的 xsd 中没有定义类型,它应该 添加到代码中,但是应该添加 using

var codeNamepscace = new CodeNamespace("targetNamepscae");
var tmp = code.Types.Cast<CodeTypeDeclaration>().ToArray();
foreach (var type in tmp)
{
    var typeFromSchema = schema.SchemaTypes.Names.Cast<XmlQualifiedName>().FirstOrDefault(x => x.Name == type.Name);
    if (typeFromSchema == null)
    {
        string xsdNamespace = ???; // how to get the xsd-namespace for the current type here?
        Console.WriteLine(String.Format("Found referenced type {0} which is not declared in current schema", type.Name));
        // omit the type from the current namespace and add a using-directive to the generated source-file if not yet done
        code.Types.Remove(type);
        // add a using-directive for the type if not already done
        if (!((IList)code.Imports).Contains(typeFromSchema.Namespace)) code.Imports.Add(new CodeNamespaceImport(GetDotNetNSFromXsdNS(xsdNamespace)));
    }
}

方法 GetDotNetNSFromXsdNS 定义了从 xsd 文件中定义的名称空间到程序集中的名称空间的映射。

我现在的问题是:如何从 xsd 中为我的 CodeNamepspace 中的类型获取命名空间?特别是:如何从生成中排除 gml:ReferenceTypebfm:MyBaseClass 并通过 using?

添加它们

以下解决方案适用于我 - 虽然我认为它更像是一种解决方法:

var tmp = code.Types.Cast<CodeTypeDeclaration>().ToArray();     // temp. copy of our types-list to avoid ModifiedException during iteration
foreach (var type in tmp)
{
    var attributes = type.CustomAttributes.Cast<CodeAttributeDeclaration>().Where(x => x.Name.StartsWith("System.Xml.Serialization"));
    var namespaceAttribute = attributes.SelectMany(x => x.Arguments.Cast<CodeAttributeArgument>()).FirstOrDefault(x => x.Name == "Namespace");
    string xsdNamespace = ((CodePrimitiveExpression)namespaceAttribute.Value).Value as string;
    if (xsdNamespace != schema.TargetNamespace)
    {
        Console.WriteLine("INFO: Found referenced type {0} which is not declared in current schema", type.Name);

        // omit the type from the current namespace and add a using-directive to the generated source-file if not yet done
        code.Types.Remove(type);

        var nameWithinAssembly = this.m_typeMapper.GetDotNetNamespaceFromXsdNamespace(xsdNamespace);
        // add a using-directive for the type if not already done
        if (!(code.Imports.Cast<CodeNamespaceImport>().Any(x => x.Namespace == nameWithinAssembly))) code.Imports.Add(new CodeNamespaceImport(nameWithinAssembly));
    }
}

这里我们假设来自 xsd 的命名空间在序列化属性中有镜像。因此,我们为 ozur CodeNameapace 中的每个类型获取 CustomAttributes,并检查它是否在 System.Xml.Serialization 的任何属性上定义了 Namespace-属性。最后我们要做的就是检查这个用于序列化的命名空间是否等于模式目标命名空间。如果此条件未通过,则应使用 using 指令推断类型,该指令在最后一行完成。