Получение XML из хранимой процедуры

Я получаю вывод XML из хранимой процедуры. То, что я пытаюсь сделать, это получить этот XML и передать его через ASP.NET:

public XmlDocument GetPunchListXml(string communityDesc)
{
    try
    {
        using (connection = new SqlConnection(connectionString))
        {
             connection.Open();

             using (SqlCommand command = new SqlCommand("GetPunchList", connection))
             {
                   command.CommandType = CommandType.StoredProcedure;

                   SqlParameter parameter1 = new SqlParameter("@communityDesc", SqlDbType.VarChar);
                   parameter1.Value = communityDesc;
                   parameter1.Direction = ParameterDirection.Input;
                   command.Parameters.Add(parameter1);

                   var doc = new XmlDocument();
                   var reader = command.ExecuteXmlReader();
                   if (reader.Read())
                   {
                       doc.Load(reader);
                   }

                   return doc;
               }
           }
      }
      finally
      {
         connection.Close();
      }
}

но я продолжаю получать эти ошибки:

<Error>
    <Message>An error has occurred.</Message>
    <ExceptionMessage>
        The 'ObjectContent`1' type failed to serialize the response body for content type 'application/xml; charset=utf-8'.
    </ExceptionMessage>
    <ExceptionType>System.InvalidOperationException</ExceptionType>
    <StackTrace/>
    <InnerException>
        <Message>An error has occurred.</Message>
        <ExceptionMessage>
            Type 'System.Xml.XmlDocument' is an invalid collection type since it does not have a valid Add method with parameter of type 'System.Object'.
        </ExceptionMessage>
        <ExceptionType>
            System.Runtime.Serialization.InvalidDataContractException
        </ExceptionType>
        <StackTrace>
            at System.Runtime.Serialization.DataContract.DataContractCriticalHelper.ThrowInvalidDataContractException(String message, Type type)
            at WriteArrayOfanyTypeToXml(XmlWriterDelegator , Object , XmlObjectSerializerWriteContext , CollectionDataContract )
            at System.Runtime.Serialization.CollectionDataContract.WriteXmlValue(XmlWriterDelegator xmlWriter, Object obj, XmlObjectSerializerWriteContext context)
            at System.Runtime.Serialization.XmlObjectSerializerWriteContext.WriteDataContractValue(DataContract dataContract, XmlWriterDelegator xmlWriter, Object obj, RuntimeTypeHandle declaredTypeHandle)
            at System.Runtime.Serialization.XmlObjectSerializerWriteContext.SerializeWithoutXsiType(DataContract dataContract, XmlWriterDelegator xmlWriter, Object obj, RuntimeTypeHandle declaredTypeHandle)
            at System.Runtime.Serialization.DataContractSerializer.InternalWriteObjectContent(XmlWriterDelegator writer, Object graph, DataContractResolver dataContractResolver)
            at System.Runtime.Serialization.DataContractSerializer.InternalWriteObject(XmlWriterDelegator writer, Object graph, DataContractResolver dataContractResolver)
            at System.Runtime.Serialization.XmlObjectSerializer.WriteObjectHandleExceptions(XmlWriterDelegator writer, Object graph, DataContractResolver dataContractResolver)
            at System.Runtime.Serialization.DataContractSerializer.WriteObject(XmlWriter writer, Object graph)
            at System.Net.Http.Formatting.XmlMediaTypeFormatter.WriteToStream(Type type, Object value, Stream writeStream, HttpContent content)
            at System.Net.Http.Formatting.XmlMediaTypeFormatter.WriteToStreamAsync(Type type, Object value, Stream writeStream, HttpContent content, TransportContext transportContext, CancellationToken cancellationToken)
                --- End of stack trace from previous location where exception was thrown ---
            at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
            at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
            at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
            at System.Web.Http.WebHost.HttpControllerHandler.<WriteBufferedResponseContentAsync>d__1b.MoveNext()
        </StackTrace>
    </InnerException>
</Error>

что я делаю не так? Что-то не так с тем, как я пытаюсь получить XML или будет проблемой с самим XML?

я попробовал следующее:

var doc = new XmlDocument();
string s = "";
using (XmlReader reader = command.ExecuteXmlReader())
{
    while (reader.Read())
    {
        //doc.Load(reader);
        s = reader.ReadOuterXml();
        doc.LoadXml(s);
    }
}
return doc;

и получил эти ошибки:

<Error>
<Message>An error has occurred.</Message>
<ExceptionMessage>
The 'ObjectContent`1' type failed to serialize the response body for content type 'application/xml; charset=utf-8'.
</ExceptionMessage>
<ExceptionType>System.InvalidOperationException</ExceptionType>
<StackTrace/>
<InnerException>
<Message>An error has occurred.</Message>
<ExceptionMessage>
Type 'System.Xml.XmlDocument' is an invalid collection type since it does not have a valid Add method with parameter of type 'System.Object'.
</ExceptionMessage>
<ExceptionType>
System.Runtime.Serialization.InvalidDataContractException
</ExceptionType>
<StackTrace>
at System.Runtime.Serialization.DataContract.DataContractCriticalHelper.ThrowInvalidDataContractException(String message, Type type) at WriteArrayOfanyTypeToXml(XmlWriterDelegator , Object , XmlObjectSerializerWriteContext , CollectionDataContract ) at System.Runtime.Serialization.CollectionDataContract.WriteXmlValue(XmlWriterDelegator xmlWriter, Object obj, XmlObjectSerializerWriteContext context) at System.Runtime.Serialization.XmlObjectSerializerWriteContext.WriteDataContractValue(DataContract dataContract, XmlWriterDelegator xmlWriter, Object obj, RuntimeTypeHandle declaredTypeHandle) at System.Runtime.Serialization.XmlObjectSerializerWriteContext.SerializeWithoutXsiType(DataContract dataContract, XmlWriterDelegator xmlWriter, Object obj, RuntimeTypeHandle declaredTypeHandle) at System.Runtime.Serialization.DataContractSerializer.InternalWriteObjectContent(XmlWriterDelegator writer, Object graph, DataContractResolver dataContractResolver) at System.Runtime.Serialization.DataContractSerializer.InternalWriteObject(XmlWriterDelegator writer, Object graph, DataContractResolver dataContractResolver) at System.Runtime.Serialization.XmlObjectSerializer.WriteObjectHandleExceptions(XmlWriterDelegator writer, Object graph, DataContractResolver dataContractResolver) at System.Runtime.Serialization.DataContractSerializer.WriteObject(XmlWriter writer, Object graph) at System.Net.Http.Formatting.XmlMediaTypeFormatter.WriteToStream(Type type, Object value, Stream writeStream, HttpContent content) at System.Net.Http.Formatting.XmlMediaTypeFormatter.WriteToStreamAsync(Type type, Object value, Stream writeStream, HttpContent content, TransportContext transportContext, CancellationToken cancellationToken) --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Runtime.CompilerServices.TaskAwaiter.GetResult() at System.Web.Http.WebHost.HttpControllerHandler.<WriteBufferedResponseContentAsync>d__1b.MoveNext()
</StackTrace>
</InnerException>
</Error>

4 ответов


проблема, вероятно, в том, где вы используете результат вашего XmlDocument поскольку я также не могу найти никаких проблем с кодом. Почему я в это верю?--15-->

  1. никто из вашей трассировки стека показывают звонки в ваш написал код. Они фактически указывают на исключение, начинающееся с HttpControllerHandler таким образом, это, вероятно, имеет какое-то отношение к тому, где или как вы обслуживаете свой ответ.
  2. на основе документации, вызов свойство sqlcommand.ExecuteXmlReader могу кинуть InvalidOperationException но он гласит, что это произойдет, только если The SqlConnection closed or dropped during a streaming operation. For more information about streaming, see SqlClient Streaming Support.
  3. на основе документации, вызов XmlDocument.Загрузить бросает исключение XmlException так что это не будет возможной точкой отказа на основе исключения, которое вы получаете.
  4. после Googling сообщение об исключении я нашел предыдущие пользователи испытали эту ошибку из-за проблем с конфигурацией веб-API, поэтому опять же ничего общего с загрузкой в Xml из база данных.

спекуляции на решение

это спекуляция, поскольку я не знаю, какую структуру вы используете, или если это метод WebApi или метод, который обслуживает результат дальше по стеку вызовов.

таким образом, исправление для вас состоит в том, чтобы получить ваши данные в формате, который может быть сериализован любой используемой платформой. Для получения справки нужно предоставить.

  1. какие рамки вы используете (web api, контроллер mvc, asp.net, что-то еще)
  2. If web Api-вызываемая конечная точка метода, предоставляющая данные. Если это тот метод, то ошибка заключается в том, что вы пытаетесь вернуть XmlDocument напрямую.
    • убедитесь, что правильные форматеры настроены в вашем . GlobalConfiguration.Configuration.Formatters.Add(new System.Net.Http.Formatting.XmlMediaTypeFormatter()); GlobalConfiguration.Configuration.Formatters.XmlFormatter.UseXmlSerializer = true;
    • посмотреть это предыдущий, так что ответ о том, как можно изменить свой метод работы.
    • я бы рекомендовал хранить это как есть и пометить его как частный, а затем добавить метод Api, который вызывает этот метод и форматирует результаты к чему-то, что может быть передано обратно клиенту.

Исправление Веб-Api

я могу дублировать точную ошибку и сообщение, вернув XmlDocument как ответ в контроллере веб-API. Это снова иллюстрирует, что это не имеет ничего общего с содержимым метода, но больше, где XmlDocument используется в ответ. Если вы возвращаете XmlDocument непосредственно в ответе (так что не часть большего объекта), вы можете исправить это, добавив новый HttpContent тип, который знает, как десериализовать (извлечь фактическое содержимое Xml)XmlDocument. Это основано на этом предыдущем так что ответ сообщение от Darrel Miller. Вот это исправление в полном самодостаточном примере.

using System.IO;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
using System.Web.Http;
using System.Xml;

namespace WebApiTests.Controllers
{
    public class HomeController : ApiController
    {
        const string sampleData = "<?xml version=\"1.0\"?><catalog><book id=\"bk101\"><author>Gambardella, Matthew</author><title>XML Developer's Guide</title><genre>Computer</genre><price>44.95</price><publish_date>2000-10-01</publish_date><description>An in-depth look at creating applications with XML.</description></book></catalog>";
        public HttpResponseMessage Get()
        {
            var doc = new XmlDocument();
            doc.LoadXml(sampleData);

            return new HttpResponseMessage()
            {
                RequestMessage = Request,
                Content = new XmlContent(doc)
            };
        }
    }

    public class XmlContent : HttpContent
    {
        private readonly MemoryStream _Stream = new MemoryStream();

        public XmlContent(XmlDocument document)
        {
            document.Save(_Stream);
            _Stream.Position = 0;
            Headers.ContentType = new MediaTypeHeaderValue("application/xml");
        }

        protected override Task SerializeToStreamAsync(Stream stream, System.Net.TransportContext context)
        {
            _Stream.CopyTo(stream);
            var tcs = new TaskCompletionSource<object>();
            tcs.SetResult(null);
            return tcs.Task;
        }

        protected override bool TryComputeLength(out long length)
        {
            length = _Stream.Length;
            return true;
        }

        protected override void Dispose(bool disposing)
        {
            if(_Stream != null)
                _Stream.Dispose();
        }
    }
}

общие исправления кода

ваш метод имеет некоторые проблемы в нем, поскольку он относится к SqlConnection. Ниже приведен очищенный код с пояснениями.

public XmlDocument GetPunchListXml(string communityDesc)
{
    // 1. Use a new SqlConnection everywhere and do not register SqlConnection as a field on the class.
    // This is a Microsoft recommended best practice. Sql Server handles connection pooling so the call new SqlConnection is very cheap.
    // 2. a using block will close and dispose the connection for you
    using (SqlConnection connection = new SqlConnection(connectionString))
    {
        connection.Open();

        using (SqlCommand command = new SqlCommand("GetPunchList", connection))
        {
            command.CommandType = CommandType.StoredProcedure;

            SqlParameter parameter1 = new SqlParameter("@communityDesc", SqlDbType.VarChar);
            parameter1.Value = communityDesc;
            parameter1.Direction = ParameterDirection.Input;
            command.Parameters.Add(parameter1);

            // wrap your DataReader in a using block
            using (var reader = command.ExecuteXmlReader())
            {
                var doc = new XmlDocument();
                if (reader.Read())
                {
                   doc.Load(reader);
                }
                return doc;
            }
        }
    }
}

измените код ниже,

var doc = new XmlDocument();
var reader = command.ExecuteXmlReader();
if (reader.Read())
{
   doc.Load(reader);
}

ниже

XmlDocument doc = new XmlDocument();
XmlReader reader = command.ExecuteXmlReader();
if (reader.Read())
{
    doc.Load(reader);
}

Если все еще не работает, пожалуйста, поделитесь своим xml, который вы возвращаете из SP.


XmlDocument не сериализуется, возвращает строковое представление XML вместо

using (var stringWriter = new StringWriter())
using (var xmlTextWriter = XmlWriter.Create(stringWriter))
{
    xmlDoc.WriteTo(xmlTextWriter);
    xmlTextWriter.Flush();
    return stringWriter.GetStringBuilder().ToString();
}

вы можете создать свой собственный простой MediaTypeFormatter на ASP.NET Web API чтобы поток XmlDocument непосредственно к ответу. Пример реализации в конце этого ответа.

зарегистрировать его в public static void Register(HttpConfiguration config) метод автогенерируемые WebApiConfig класс, который вы получаете при создании нового проекта с помощью мастера VS.

// This custom media type converter must be inserted before the default media type converter that handles application/xml, do not use config.Formatters.Add(...
config.Formatters.Insert(0, new MediaTypeFormatters.XmlDocumentMediaTypeFormatter());

в методе класса контроллера сделайте что-то подобное этому

private HttpResponseMessage Get()
{
    XmlDocument doc = new XmlDocument();
    XmlDeclaration dec = doc.CreateXmlDeclaration("1.0", "utf-8", null);
    doc.AppendChild(dec);
    XmlElement rootElement = doc.CreateElement("user");
    doc.AppendChild(rootElement);
    rootElement.SetAttribute("id", "1");

    var response = Request.CreateResponse(HttpStatusCode.OK, doc, "application/xml");
    return (response);
}

образец пользовательского MediaTypeFormatter для потоковой передачи XmlDocument в ответ.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Net.Http.Formatting;
using System.Net.Http.Headers;
using System.IO;
using System.Net.Http;
using System.Threading.Tasks;
using System.Net;
using System.Xml;

namespace Take5.MediaTypeFormatters
{
    public class XmlDocumentMediaTypeFormatter : MediaTypeFormatter
    {
        private static Type _supportedType = typeof(XmlDocument);

        public XmlDocumentMediaTypeFormatter()
        {
            SupportedMediaTypes.Add(new MediaTypeHeaderValue("application/xml"));
        }

        public override bool CanReadType(Type type)
        {
            return type == _supportedType;
        }

        public override bool CanWriteType(Type type)
        {
            return type == _supportedType;
        }

        public override Task<object> ReadFromStreamAsync(Type type, Stream readStream, HttpContent content, IFormatterLogger formatterLogger)
        {
            var taskSource = new TaskCompletionSource<object>();
            try
            {
                System.Diagnostics.Debug.Assert(false, "Not fully implemented, get doc to read from readStream.");
                var doc = new XmlDocument();
                taskSource.SetResult(doc);
            }
            catch (Exception e)
            {
                taskSource.SetException(e);
            }
            return taskSource.Task;
        }

        public override Task WriteToStreamAsync(Type type, object value, Stream writeStream, HttpContent content, TransportContext transportContext)
        {
            var taskSource = new TaskCompletionSource<object>();
            try
            {
                XmlDocument doc = (XmlDocument)value;
                XmlWriter writer = XmlWriter.Create(writeStream, new XmlWriterSettings() { Indent = true });
                doc.WriteContentTo(writer);
                writer.Flush();

                taskSource.SetResult(null);
            }
            catch (Exception e)
            {
                taskSource.SetException(e);
            }
            return taskSource.Task;
        }
    }
}