从.ASMX Web服务返回XElement

时间:2013-07-05 08:28:05

标签: c# asp.net web-services

我最近更新了一个返回XElement的.ASMX Web服务,但遇到了以下错误消息:

Cannot use wildcards at the top level of a schema

现在,此错误由以下代码生成;

public class FooBarService : System.Web.Services.WebService
{            
    [WebMethod]
    public XElement Foo(string Bar)
    {
        return null;
    }    
}

但是,如果我将代码更改为接受XElement而不是String;

public class FooBarService : System.Web.Services.WebService
{        
    [WebMethod]
    public XElement Foo(XElement Bar)
    {
        return null;
    }
}

然后Web服务不会抛出错误。

那么为什么接受XElement并返回XElement的方法工作,而不是其他方法?

1 个答案:

答案 0 :(得分:3)

开始的地方是堆栈跟踪,它指示发生异常的地方:

at System.Xml.Serialization.XmlSchemaExporter.ExportElement(ElementAccessor accessor)
at System.Xml.Serialization.XmlSchemaExporter.ExportTypeMapping(XmlTypeMapping xmlTypeMapping)
at System.Web.Services.Description.MimeXmlReflector.ReflectReturn()
at System.Web.Services.Description.HttpProtocolReflector.ReflectMimeReturn()
at System.Web.Services.Description.HttpPostProtocolReflector.ReflectMethod()

通过使用ILSpy,我们可以观察触发异常的条件:

// System.Xml.Serialization.XmlSchemaExporter
private XmlSchemaElement ExportElement(ElementAccessor accessor)
{
    if (!accessor.Mapping.IncludeInSchema && !accessor.Mapping.TypeDesc.IsRoot)
    {
        return null;
    }
    if (accessor.Any && accessor.Name.Length == 0)
    {
        throw new InvalidOperationException(Res.GetString("XmlIllegalWildcard"));
    }
    // truncated method body
}

进一步浏览代码:

// System.Web.Services.Description.MimeXmlReflector
internal override bool ReflectReturn()

// System.Xml.Serialization.XmlReflectionImporter
private ElementAccessor 
    ImportElement(TypeModel model, 
        XmlRootAttribute root, 
        string defaultNamespace, 
        RecursionLimiter limiter)

等等,我们采用这种方法:

// System.Xml.Serialization.XmlReflectionImporter
private static ElementAccessor 
    CreateElementAccessor(TypeMapping mapping, string ns)
{
    ElementAccessor elementAccessor = new ElementAccessor();
    bool flag = mapping.TypeDesc.Kind == TypeKind.Node;
    if (!flag && mapping is SerializableMapping)
    {
        flag = ((SerializableMapping)mapping).IsAny;
    }
    if (flag)
    {
        elementAccessor.Any = true;
    }
    else
    {
        elementAccessor.Name = mapping.DefaultElementName;
        elementAccessor.Namespace = ns;
    }
    // truncated
}

XElement类型映射似乎将Any属性值设置为true,但未获得DefaultElementName

该问题的一个简单解决方法是创建派生类:

public class FooBarService : System.Web.Services.WebService
{
    [WebMethod]
    public MyXElement Foo(string bar)
    {
        return null;
    }
}
public class MyXElement : XElement
{
    public MyXElement()
        : base(XName.Get("default")) { }
}

将在堆栈中调用:

System.Web.Services.Description.SoapProtocolReflector.ReflectMethod()

代替HttpPostProtocolReflector.ReflectMethod()方法,并正确分配名称:

messagePart.Name = members[0].MemberName;

要回答您的问题,在将XElement指定为参数时,方法调用的工作原因是因为类型映射是通过其他方法创建的,而name成员不为空。因此,不会发生引发异常的条件。