WCF Read stream changes the behaviour of a WebFaultException - wcf

We have the following web method:
public void RawData(string dataItemName, Stream sourceStream)
{
StreamReader reader = new StreamReader(sourceStream);
{
string data = reader.ReadToEnd();
throw new WebFaultException<string>("error", System.Net.HttpStatusCode.BadRequest);
}
}
and is defined as follows:
[WebInvoke(Method = "POST", UriTemplate= "RawData/{dataItemName}", ResponseFormat = WebMessageFormat.Xml)]
[OperationContract]
[FaultContract(typeof(LoginFaultInfo))]
[FaultContract(typeof(SecurityFaultInfo))]
[FaultContract(typeof(RequestFaultInfo))]
[Description("Sets data for the specified item")]
void RawData(string dataItemName, Stream dataStream);
This correctly generates the exception in the client.
However, if this code is changed as follows:
public void RawData(string dataItemName, Stream sourceStream)
{
using (StreamReader reader = new StreamReader(sourceStream))
{
string data = reader.ReadToEnd();
}
throw new WebFaultException<string>("error", System.Net.HttpStatusCode.BadRequest);
}
Then the client gets a 202 Accepted Status rather than a fault exception.
Does anyone know why this is the case? Reading and disposing of the stream has changed the behaviour somehow,

Do not use "using" in WCF Client.
You can check Microsoft documentation for instructions.
Close and Abort release resources safely when network connections have dropped
The C# "using" statement results in a call to Dispose(). This is the
same as Close(), which may throw exceptions when a network error
occurs. Because the call to Dispose() happens implicitly at the
closing brace of the "using" block, this source of exceptions is
likely to go unnoticed both by people writing the code and reading the
code. This represents a potential source of application errors.
I think you can use the Microsoft recommended way of handling WCF client calls.For more detail see: Expected Exceptions.

Related

.NET Core pdf downloader "No output formatter was found for content types 'application/pdf'..."

I'm creating a .NET Core 3.1 web api method to download a pdf for a given filename. This method is shared across teams where their client code is generated using NSwag.
I recently changed produces attribute to Produces("Application/pdf") from json, this change is required so other teams can generate valid client code. However since this change, I haven't been able to download any files from this method. Requests to download documents return with a 406 error (in Postman) and the following error is logged to the server event viewer.
No output formatter was found for content types 'application/pdf, application/pdf' to write the response.
Reverting the produced content-type to 'application/json' does allow documents to be downloaded, but as mentioned, this value is required to be pdf.
Any suggestions would be greatly appreciated.
Method:
[HttpGet("{*filePath}")]
[ProducesResponseType(typeof(FileStreamResult), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
[Produces("Application/pdf")]
public async Task<ActionResult> GetDocument(string fileName) {
RolesRequiredHttpContextExtensions.ValidateAppRole(HttpContext, _RequiredScopes);
var memoryStream = new MemoryStream();
var memoryStream = new MemoryStream();
using (var stream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read, bufferSize: 4096, useAsync: true)) {
stream.CopyTo(memoryStream);
}
memoryStream.Seek(offset: 0, SeekOrigin.Begin);
return new FileStreamResult(memoryStream, "Application/pdf");
}
I just came across the same error and after some investigation I found out that the cause of the exception was indeed in the model binding error. You already wrote about it in your answer, but on closer inspection it became obvious that the reason was not related to binding itself, rather to the response body.
Since you specified [Produces("application/pdf")] the framework assumes this content type is the only possible for this action, but when an exception is thrown, you get application/json containing error description instead.
So to make this work for both "happy path" and exceptions, you could specify multiple response types:
[Produces("application/pdf", "application/json")]
public async Task<ActionResult> GetDocument(string fileName)
{
...
}
I'am using
public asnyc Task<IActionResult> BuildPDF()
{
Stream pdfStream = _pdfService.GetData();
byte[] memoryContent = pdfStream.ToArray();
return File(memoryContent, "application/pdf");
}
and it works. Could you please try?
The issue was caused by renaming the method parameter and not updating [HttpGet("{*filePath}")] to [HttpGet("{*fileName}")]
I had the same error, it is very confusing in some cases.
I got this error after adding new parameter of type int[] to my method forgetting [FromQuery] attribute for it.
After adding [FromQuery] attribute error gone.

WCF streaming SQLFileStream by Message Contract

In my WCF service, I try to load a File from MS SQL table which has a FileStream column and I try to pass it as a stream back
responseMsg.DocSqlFileStream = new MemoryStream();
try
{
using (FileStreamDBEntities dbEntity = new FileStreamDBEntities())
{
...
using (TransactionScope x = new TransactionScope())
{
string sqlCmdStr = "SELECT dcraDocFile.PathName() AS InternalPath, GET_FILESTREAM_TRANSACTION_CONTEXT() AS TransactionContext FROM dcraDocument WHERE dcraDocFileID={0}";
var docFileStreamInfo = dbEntity.Database.SqlQuery<DocFileStreamPath>(sqlCmdStr, new object[] { docEntity.dcraDocFileID.ToString() }).First();
SqlFileStream sqlFS = new SqlFileStream(docFileStreamInfo.InternalPath, docFileStreamInfo.TransactionContext, FileAccess.Read);
sqlFS.CopyTo(responseMsg.DocSqlFileStream);
if( responseMsg.DocSqlFileStream.Length > 0 )
responseMsg.DocSqlFileStream.Position = 0;
x.Complete();
}
}
...
I'm wondering whats the best way to pass the SQLFileStream back through a message contract back to take advantage of streaming. Currently I copied the SQLFilEStream to a memory stream because I got an error message in WCF trace which says: Type 'System.Data.SqlTypes.SqlFileStream' cannot be serialized.
In WebApi there is such thing as PushStreamContent it allows delegating all transaction stuff to async lambda, don't know if there is something similar in WCF, but the following approach may be helpful:
http://weblogs.asp.net/andresv/archive/2012/12/12/asynchronous-streaming-in-asp-net-webapi.aspx
You can't stream an SQLFileStream back to the client because it can only be read within the SQL transaction. I think your solution with the MemoryStream is a good way of dealing with the problem.
I had a similar problem and was worried about the large object heap when using a new Memory Stream every time. I came up with the idea of using a temporary file on the disk instead of a memory stream. We are using this solution in several project now and it works really well.
See here for the example code:
https://stackoverflow.com/a/11307324/173711

WCF Routing with Content Transformation

I've implemented a WCF Routing service; I would also like the service (or a similar WCF service) to transform the payload in a prescribed and uniform (content-agnostic) fashion. For example, the payload will always take the form Foo<T> and I would like to pass it on as Bar<T> in all cases. I'm happy for the transformation to be XSLT or programmatic. I don't care what happens to messages received that aren't of the type Foo<T>.
I wish to use WCF as it provides a lot of OOTB functionality (e.g. its support for numerous bindings). It's not practical to implement a WCF service with numerous boilerplate methods to transform each closed generic (Foo<Class1> -> Bar<Class1>; Foo<Class2> -> Bar<Class2>; etc), as this would require recompilation/redeployment every time a new message type was to be routed.
To the best of my knowledge, WCF doesn't handle open generics and WCF Routing doesn't facilitate content transformation OOTB. That said, System.ServiceModel.Routing.RoutingService obviously intercepts WCF calls in some non-specific form, so I was hoping to leverage the same pattern to achieve my goal. Can anyone please provide direction on how to do this (or indicate why it's not possible)?
As I suggested in my comments on the question, there is a solution to this using the IDispatchMessageInspector. Please find below an extremely dumbed-down version of what I ended up writing (easier than me posting the code for 20 classes). If anyone wants a full solution implementing this code in a significantly cleaner and more advanced manner, let me know and I'll put my demo up on CodeProject. For now, I'll presume you're happy with a snippet of the guts.
The Console commands can obvious be removed (they're just so you can debug if you're self-hosting).
public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
{
if (request == null || request.IsEmpty)
return null;
Console.WriteLine();
Console.ForegroundColor = ConsoleColor.Green;
Console.WriteLine(request);
Console.ResetColor();
// Load the request into a document.
XPathDocument document;
MemoryStream stream;
using (stream = new MemoryStream())
{
using (XmlDictionaryWriter writer = XmlDictionaryWriter.CreateTextWriter(stream))
{
request.WriteMessage(writer);
writer.Flush();
stream.Position = 0L;
document = new XPathDocument(stream);
}
}
// Load the XSLT.
XslCompiledTransform transformer = new XslCompiledTransform();
transformer.Load("RequestTransformation.xslt");
// Transform the document.
byte[] transformedDocument;
using (stream = new MemoryStream())
{
transformer.Transform(document, null, stream);
transformedDocument = stream.ToArray();
}
// Construct new request from tranformed document.
stream = new MemoryStream(transformedDocument);
XmlReader reader = XmlReader.Create(stream);
Message modifiedMessage = Message.CreateMessage(reader, int.MaxValue, request.Version);
modifiedMessage.Properties.CopyProperties(request.Properties);
request = modifiedMessage;
Console.WriteLine();
Console.ForegroundColor = ConsoleColor.Yellow;
Console.WriteLine(new System.Text.UTF8Encoding(false).GetString(transformedDocument));
Console.ResetColor();
return null;
}

Building workaround for HttpWebRequest timeout issue

I'm using the HTTP Web Request class to call a RESTful web service. I need to pass data and receive data and it all seems to work very well. Today I attempted to configure the time-out of the class because there is a high likelihood of the server running the service being offline and I don't want to waste time waiting. I configured it all but it seemed to make no difference. The call still waited over 10 seconds before failing.
On looking into it I found that the time-out only deals with the processing of the call and that the DNS lookup beforehand is not included. As this would be the problem it would make sense as to why the time-out wasn't working as I'd expected.
Further reading suggested using the HttpWebRequest class in an asynchronous style instead. I've had a look at the code to do so but don't understand how to retrieve the callback in my code which is effectively synchronous.
The code I have as follows is as so:
HttpWebRequest _serviceRequest = (HttpWebRequest)WebRequest.Create(new Uri("http://mywebservice.com"));
_serviceRequest.Timeout = 3000;
HttpWebResponse response = (HttpWebResponse)_serviceRequest.GetResponse();
XmlReader reader = XmlReader.Create(response.GetResponseStream(), set);
The code I have to call asynchronously ends with the following line, but I'm not sure as to what I should do to get the response object.
IAsyncResult result = (IAsyncResult)req.BeginGetResponse(new AsyncCallback(RespCallback), reqState);
I'm also concerned about a half baked asynchronous solution such as this. Is it good practice to use an asynchronous method through a synchronous piece of code.
Any helpers appreciated...
The response would be available when the callback function RespCallback is invoked. I don't know what reqState has, but I assume it contains a reference to the original HttpWebRequest object. If this is the case, this would be a simple implementation of the RespCallback method:
void RespCallback(IAsyncResult asyncResult)
{
ReqState reqState = (ReqState)asyncResult.AsyncState;
HttpWebResponse resp = (HttpWebResponse)reqState.Request.EndGetResponse(asyncResult);
// do what you need with the response.
}
Update: more info as asked in the comment
If you want the response in the same method where you did the Begin call, you can have an event which will be set when the callback is received, and you can wait on that event after the Begin call, like in the example below
class ReqState {
public HttpWebRequest Request { get; set; }
public HttpWebResponse Response { get; set; }
public AutoResetEvent Evt { get; set; }
}
void RespCallback(IAsyncResult asyncResult) {
ReqState reqState = (ReqState)asyncResult.AsyncState;
reqState.Response = (HttpWebResponse)reqState.Request.EndGetResponse(asyncResult);
reqState.Evt.Set();
}
void CallMethod() {
HttpWebRequest req = (HttpWebRequest)HttpWebRequest.Create(...);
// set properties on req
ReqState state = new ReqState();
state.Request = req;
state.Evt = new ManualResetEvent(false);
req.BeginGetResponse(RespCallback, state);
state.Evt.WaitOne(TimeSpan.FromSeconds(30)); // wait for 30 seconds
// access response via state.Response
}
Now notice that you're essentially doing a synchronous call in an asynchronous way. That gives you more control over the timeout, but with the price of code complexity. Another thing, this will not work on platforms such as Silverlight (and Windows Phone, IIRC), where synchronous calls (even those dressed up as asynchronous) are forbidden.

Very slow performance deserializing using datacontractserializer in a Silverlight Application

Here is the situation:
Silverlight 3 Application hits an asp.net hosted WCF service to get a list of items to display in a grid. Once the list is brought down to the client it is cached in IsolatedStorage. This is done by using the DataContractSerializer to serialize all of these objects to a stream which is then zipped and then encrypted. When the application is relaunched, it first loads from the cache (reversing the process above) and the deserializes the objects using the DataContractSerializer.ReadObject() method. All of this was working wonderfully under all scenarios until recently with the entire "load from cache" path (decrypt/unzip/deserialize) taking hundreds of milliseconds at most.
On some development machines but not all (all machines Windows 7) the deserialize process - that is the call to ReadObject(stream) takes several minutes an seems to lock up the entire machine BUT ONLY WHEN RUNNING IN THE DEBUGGER in VS2008. Running the Debug configuration code outside the debugger has no problem.
One thing that seems to look suspicious is that when you turn on stop on Exceptions, you can see that the ReadObject() throws many, many System.FormatException's indicating that a number was not in the correct format. When I turn off "Just My Code" thousands of these get dumped to the screen. None go unhandled. These occur both on the read back from the cache AND on a deserialization at the conclusion of a web service call to get the data from the WCF Service. HOWEVER, these same exceptions occur on my laptop development machine that does not experience the slowness at all. And FWIW, my laptop is really old and my desktop is a 4 core, 6GB RAM beast.
Again, no problems unless running under the debugger in VS2008. Anyone else seem this? Any thoughts?
Here is the bug report link: https://connect.microsoft.com/VisualStudio/feedback/details/539609/very-slow-performance-deserializing-using-datacontractserializer-in-a-silverlight-application-only-in-debugger
EDIT: I now know where the FormatExceptions are coming from. It seems that they are "by design" - they occur when when I have doubles being serialized that are double.NaN so that that xml looks like NaN...It seems that the DCS tries to parse the value as a number, that fails with an exception and then it looks for "NaN" et. al. and handles them. My problem is not that this does not work...it does...it is just that it completely cripples the debugger. Does anyone know how to configure the debugger/vs2008sp1 to handle this more efficiently.
cartden,
You may want to consider switching over to XMLSerializer instead. Here is what I have determined over time:
The XMLSerializer and DataContractSerializer classes provides a simple means of serializing and deserializing object graphs to and from XML.
The key differences are:
1.
XMLSerializer has much smaller payload than DCS if you use [XmlAttribute] instead of [XmlElement]
DCS always store values as elements
2.
DCS is "opt-in" rather than "opt-out"
With DCS you explicitly mark what you want to serialize with [DataMember]
With DCS you can serialize any field or property, even if they are marked protected or private
With DCS you can use [IgnoreDataMember] to have the serializer ignore certain properties
With XMLSerializer public properties are serialized, and need setters to be deserialized
With XmlSerializer you can use [XmlIgnore] to have the serializer ignore public properties
3.
BE AWARE! DCS.ReadObject DOES NOT call constructors during deserialization
If you need to perform initialization, DCS supports the following callback hooks:
[OnDeserializing], [OnDeserialized], [OnSerializing], [OnSerialized]
(also useful for handling versioning issues)
If you want the ability to switch between the two serializers, you can use both sets of attributes simultaneously, as in:
[DataContract]
[XmlRoot]
public class ProfilePerson : NotifyPropertyChanges
{
[XmlAttribute]
[DataMember]
public string FirstName { get { return m_FirstName; } set { SetProperty(ref m_FirstName, value); } }
private string m_FirstName;
[XmlElement]
[DataMember]
public PersonLocation Location { get { return m_Location; } set { SetProperty(ref m_Location, value); } }
private PersonLocation m_Location = new PersonLocation(); // Should change over time
[XmlIgnore]
[IgnoreDataMember]
public Profile ParentProfile { get { return m_ParentProfile; } set { SetProperty(ref m_ParentProfile, value); } }
private Profile m_ParentProfile = null;
public ProfilePerson()
{
}
}
Also, check out my Serializer class that can switch between the two:
using System;
using System.IO;
using System.Runtime.Serialization;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
namespace ClassLibrary
{
// Instantiate this class to serialize objects using either XmlSerializer or DataContractSerializer
internal class Serializer
{
private readonly bool m_bDCS;
internal Serializer(bool bDCS)
{
m_bDCS = bDCS;
}
internal TT Deserialize<TT>(string input)
{
MemoryStream stream = new MemoryStream(input.ToByteArray());
if (m_bDCS)
{
DataContractSerializer dc = new DataContractSerializer(typeof(TT));
return (TT)dc.ReadObject(stream);
}
else
{
XmlSerializer xs = new XmlSerializer(typeof(TT));
return (TT)xs.Deserialize(stream);
}
}
internal string Serialize<TT>(object obj)
{
MemoryStream stream = new MemoryStream();
if (m_bDCS)
{
DataContractSerializer dc = new DataContractSerializer(typeof(TT));
dc.WriteObject(stream, obj);
}
else
{
XmlSerializer xs = new XmlSerializer(typeof(TT));
xs.Serialize(stream, obj);
}
// be aware that the Unicode Byte-Order Mark will be at the front of the string
return stream.ToArray().ToUtfString();
}
internal string SerializeToString<TT>(object obj)
{
StringBuilder builder = new StringBuilder();
XmlWriter xmlWriter = XmlWriter.Create(builder);
if (m_bDCS)
{
DataContractSerializer dc = new DataContractSerializer(typeof(TT));
dc.WriteObject(xmlWriter, obj);
}
else
{
XmlSerializer xs = new XmlSerializer(typeof(TT));
xs.Serialize(xmlWriter, obj);
}
string xml = builder.ToString();
xml = RegexHelper.ReplacePattern(xml, RegexHelper.WildcardToPattern("<?xml*>", WildcardSearch.Anywhere), string.Empty);
xml = RegexHelper.ReplacePattern(xml, RegexHelper.WildcardToPattern(" xmlns:*\"*\"", WildcardSearch.Anywhere), string.Empty);
xml = xml.Replace(Environment.NewLine + " ", string.Empty);
xml = xml.Replace(Environment.NewLine, string.Empty);
return xml;
}
}
}
This is a guess, but I think it is running slow in debug mode because for every exception, it is performing some actions to show the exception in the debug window, etc. If you are running in release mode, these extra steps are not taken.
I've never done this, so I really don't know id it would work, but have you tried just setting that one assembly to run in release mode while all others are set to debug? If I'm right, it may solve your problem. If I'm wrong, then you only waste 1 or 2 minutes.
About your debugging problem, have you tried to disable the exception assistant ? (Tools > Options > Debugging > Enable the exception assistant).
Another point should be the exception handling in Debug > Exceptions : you can disable the user-unhandled stuff for the CLR or only uncheck the System.FormatException exception.
Ok - I figured out the root issue. It was what I alluded to in the EDIT to the main question. The problem was that in the xml, it was correctly serializing doubles that had a value of double.NaN. I was using these values to indicate "na" for when the denominator was 0D. Example: ROE (Return on Equity = Net Income / Average Equity) when Average Equity is 0D would be serialized as:
<ROE>NaN</ROE>
When the DCS tried to de-serialize it, evidently it first tries to read the number and then catches the exception when that fails and then handles the NaN. The problem is that this seems to generate a lot of overhead when in DEBUG mode.
Solution: I changed the property to double? and set it to null instead of NaN. Everything now happens instantly in DEBUG mode now. Thanks to all for your help.
Try disabling some IE addons. In my case, the LastPass toolbar killed my Silverlight debugging. My computer would freeze for minutes each time I interacted with Visual Studio after a breakpoint.