In order to return useful information in SoapException.Detail
for an asmx web service, I took an idea from WCF and created a fault class to contain said useful information. That fault object is then serialised to the required XmlNode
of a thrown SoapException
.
I'm wondering whether I have the best code to create the XmlDocument
- here is my take on it:
var xmlDocument = new XmlDocument();
var serializer = new XmlSerializer(typeof(T));
using (var stream = new MemoryStream())
{
serializer.Serialize(stream, theObjectContainingUsefulInformation);
stream.Flush();
stream.Seek(0, SeekOrigin.Begin);
xmlDocument.Load(stream);
}
Is there a better way of doing this?
UPDATE: I actually ended up doing the following, because unless you wrap the XML in a <detail>
xml element, you get a SoapHeaderException
at the client end:
var serialiseToDocument = new XmlDocument();
var serializer = new XmlSerializer(typeof(T));
using (var stream = new MemoryStream())
{
serializer.Serialize(stream, e.ExceptionContext);
stream.Flush();
stream.Seek(0, SeekOrigin.Begin);
serialiseToDocument.Load(stream);
}
// Remove the xml declaration
serialiseToDocument.RemoveChild(serialiseToDocument.FirstChild);
// Memorise the node we want
var serialisedNode = serialiseToDocument.FirstChild;
// and wrap it in a <detail> element
var rootNode = serialiseToDocument.CreateNode(XmlNodeType.Element, "detail", "");
rootNode.AppendChild(serialisedNode);
UPDATE 2: Given John Saunders excellent answer, I've now started using the following:
private static void SerialiseFaultDetail()
{
var fault = new ServiceFault
{
Message = "Exception occurred",
ErrorCode = 1010
};
// Serialise to the XML document
var detailDocument = new XmlDocument();
var nav = detailDocument.CreateNavigator();
if (nav != null)
{
using (XmlWriter writer = nav.AppendChild())
{
var ser = new XmlSerializer(fault.GetType());
ser.Serialize(writer, fault);
}
}
// Memorise and remove the element we want
XmlNode infoNode = detailDocument.FirstChild;
detailDocument.RemoveChild(infoNode);
// Move into a root <detail> element
var rootNode = detailDocument.AppendChild(detailDocument.CreateNode(XmlNodeType.Element, "detail", ""));
rootNode.AppendChild(infoNode);
Console.WriteLine(detailDocument.OuterXml);
Console.ReadKey();
}
-
overall it looks good to me, though I think I'd use strongly typed objects rather than vars in this case.
Also, I don't know that the
stream.Seak(0, SeekOrigin.Begin)
is really necessary.Steven : var is still strongly typed, just saves having to write out the full type.Neil Barnwell : Yes - "var" is *implicit typing*, not *weak typing*. The seek is necessary I found, because the steam is at the end and so not ready for the XmlDocument to start reading.tjmoore : Whilst it may save writing out the full type, it makes readability a potential nightmare. I wouldn't want to maintain someone else's code that is full of vars. It's also a problem when posting code on the net as you don't have intellisense to help you. Besides intellisense makes typing the full type barely any more than typing var. -
EDIT: Creates output inside of detail element
public class MyFault { public int ErrorCode { get; set; } public string ErrorMessage { get; set; } } public static XmlDocument SerializeFault() { var fault = new MyFault { ErrorCode = 1, ErrorMessage = "This is an error" }; var faultDocument = new XmlDocument(); var nav = faultDocument.CreateNavigator(); using (var writer = nav.AppendChild()) { var ser = new XmlSerializer(fault.GetType()); ser.Serialize(writer, fault); } var detailDocument = new XmlDocument(); var detailElement = detailDocument.CreateElement( "exc", SoapException.DetailElementName.Name, SoapException.DetailElementName.Namespace); detailDocument.AppendChild(detailElement); detailElement.AppendChild( detailDocument.ImportNode( faultDocument.DocumentElement, true)); return detailDocument; }
Neil Barnwell : +1 for being a really neat solution. However, given that I need to wrap the serialised object's XML in a root "" element, what's the best approach? I tried creating a node, then using the new node's navigator but got an InvalidOperationException with "WriteStartDocument cannot be called on writers created with ConformanceLevel.Fragment." Neil Barnwell : Ahh, now I understand. Using the return value of ImportNode for the AppendChild call was the link in the chain I was missing. I really hate these messy XML classes in .net, I must say. Thanks for your help.
0 comments:
Post a Comment