【问题标题】:Returning an XElement from a .ASMX Web Service从 .ASMX Web 服务返回 XElement
【发布时间】:2013-10-01 07:43:08
【问题描述】:

我最近更新了一个 .ASMX Web 服务,在该服务中它返回了一个 XElement 并遇到了以下错误消息:

现在这个错误是由下面的代码产生的;

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 Service就不会抛出错误了。

那么为什么接受 XElement 并返回 XElement 的方法有效,而其他方法无效?

【问题讨论】:

  • 相关问题在这里; stackoverflow.com/questions/349769/… - 看起来可能是一个错误?
  • @ChrisMcAtackney 是的,我已经阅读了那个问题,其中一个答案的链接断开了,其余的都没有说明问题。
  • 为什么不返回一个字符串呢?
  • 因为我不想 :P 我想知道为什么这不起作用。

标签: c# asp.net web-services


【解决方案1】:

从堆栈跟踪开始,它指示异常发生的位置:

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成员不为空。所以引发异常的条件不会发生。

【讨论】:

  • 这是一个很好的答案,谢谢!也感谢您对此问题进行调查!
  • 当我尝试使用 VS2010 中的 Web Reference 使用接受并返回 MyXElement 的服务时,它会创建 DataSet 类型化参数和返回类型。如何让它使用 XElement?
猜你喜欢
  • 1970-01-01
  • 2010-09-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-05-20
  • 1970-01-01
  • 2013-03-27
  • 1970-01-01
相关资源
最近更新 更多