Получение 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-->
- никто из вашей трассировки стека показывают звонки в ваш написал код. Они фактически указывают на исключение, начинающееся с
HttpControllerHandler
таким образом, это, вероятно, имеет какое-то отношение к тому, где или как вы обслуживаете свой ответ. - на основе документации, вызов свойство sqlcommand.ExecuteXmlReader могу кинуть
InvalidOperationException
но он гласит, что это произойдет, только еслиThe SqlConnection closed or dropped during a streaming operation. For more information about streaming, see SqlClient Streaming Support.
- на основе документации, вызов XmlDocument.Загрузить бросает исключение
XmlException
так что это не будет возможной точкой отказа на основе исключения, которое вы получаете. - после Googling сообщение об исключении я нашел предыдущие пользователи испытали эту ошибку из-за проблем с конфигурацией веб-API, поэтому опять же ничего общего с загрузкой в Xml из база данных.
спекуляции на решение
это спекуляция, поскольку я не знаю, какую структуру вы используете, или если это метод WebApi или метод, который обслуживает результат дальше по стеку вызовов.
таким образом, исправление для вас состоит в том, чтобы получить ваши данные в формате, который может быть сериализован любой используемой платформой. Для получения справки нужно предоставить.
- какие рамки вы используете (web api, контроллер mvc, asp.net, что-то еще)
- 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;
}
}
}