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
Related
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;
}
i wrote a function for download a webpage : function like:
public string GetWebPage(string sURL)
{
System.Net.WebResponse objResponse = null;
System.Net.WebRequest objRequest = null;
System.IO.StreamReader objStreamReader = null;
string sResultPage = null;
try
{
objRequest = System.Net.HttpWebRequest.Create(sURL);
objResponse = objRequest.GetResponse();
objStreamReader = new System.IO.StreamReader(objResponse.GetResponseStream());
sResultPage = objStreamReader.ReadToEnd();
return sResultPage;
}
catch (Exception ex)
{
return "";
}
}
But my problem is that. when this function working at that time application goto freeze (not response) and that time my can't not do any thing. How can i solve this problem. when downloading at time user can do other thing in my application.
Welcome to the world of blocking IO.
Consider the following:
You want your program to download a web page and then return the first 10 letters it finds in the source html. Your code might look like this:
...
string page = GetWebPage("http://example.com"); // download web page
page = page.Substring(0, 10);
Console.WriteLine(page);
....
When your program calls GetWebPage(), it must WAIT for the web page to be fully downloaded before it can possibly try to call Substring() - else it may try to get the substring before it actually downloads the letters.
Now consider your program. You've got lots of code - maybe a GUI interface running - and it's all executing line by line one instruction at a time. When your code calls GetWebPage(), it can't possibly continue executing additional code until that request is fully finished. Your entire program is waiting on that request to finish.
The problem can be solved in a few different ways, and the best solution depends on exactly what you're doing with your code. Ideally, your code needs to execute asynchronously. c# has methods that can handle a lot of this for you, but one way or another, you're going to want to start some work - downloading the web page in your case - and then continue executing code until your main thread is notified that the webpage is fully downloaded. Then your main thread can begin parsing the return value.
I'm assuming that since you've asked this question, you are very new to threads and concurrency in general. You have a lot of work to do. Here are some resources for you to read up about threading and implementing concurrency in c#:
C# Thread Introduction
.NET Asynchronous IO Design
the best was is to use thread
new Thread(download).Start(url);
and if your download page size is large use chunk logic.
HttpWebRequest ObjHttpWebRequest = (HttpWebRequest)HttpWebRequest.Create(Convert.ToString(url));
ObjHttpWebRequest.AddRange(99204);
ObjHttpWebRequest.Timeout = Timeout.Infinite;
ObjHttpWebRequest.Method = "get";
HttpWebResponse ObjHttpWebResponse = (HttpWebResponse)ObjHttpWebRequest.GetResponse();
Stream ObjStream = ObjHttpWebResponse.GetResponseStream();
StreamReader ObjStreamReader = new StreamReader(ObjStream);
byte[] buffer = new byte[1224];
int length = 0;
while ((length = ObjStream.Read(buffer, 0, buffer.Length)) > 0)
{
downloaddata += Encoding.GetEncoding(936).GetString(buffer);
i'm having some problems implementing my WCF message interceptor. Basically i'm accessing the body contents and performing an xslt tranform over the nodeset to sort it alphabethicaly.
I've tested the XSLT stylesheet and it's working no problems. I write the result of the transform to a MemoryStream object and then attempt to recreate the message from the stream contents.
I examine the resulting stream using either a StreamReader or by loading it into an XmlDocument and i can see the the xml it contains it my expected result from the XSLT transform.
My problem occures when i try to recreate the message! I create an XmlReader based on the stream and use this as my body source for Message.CreateMessage(.....);
I cannot understand why i'm suddenly losing the "correct" contents in the stream when i could examine it and see a couple of statements earlier.
Help very much appreciated!
Full code of the method below:
public object AfterReceiveRequest(ref System.ServiceModel.Channels.Message request, IClientChannel channel, InstanceContext instanceContext)
{
MessageBuffer msgbuf = request.CreateBufferedCopy(int.MaxValue);
Message tmpMessage = msgbuf.CreateMessage();
XmlDictionaryReader xdr = tmpMessage.GetReaderAtBodyContents();
MemoryStream ms = new MemoryStream();
_compiledTransform.Transform(xdr,null,ms);
ms.Position = 0;
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(ms);
MemoryStream newStream = new MemoryStream();
xmlDoc.Save(newStream);
newStream.Position = 0;
//To debug contents of the stream
StreamReader sr = new StreamReader(newStream);
var temp = sr.ReadToEnd();
//At this point the XSLT tranforms has resulted in the fragment we want so all good!
XmlReaderSettings settings = new XmlReaderSettings();
settings.ConformanceLevel = ConformanceLevel.Fragment;
newStream.Position = 0;
XmlReader reader = XmlReader.Create(newStream,settings);
reader.MoveToContent();
//Reader seems to have lost the correct fragment!!! At least returned message does not contain correct fragment.
Message newMessage = Message.CreateMessage(request.Version, null, reader);
newMessage.Properties.CopyProperties(request.Properties);
request = newMessage;
return request;
}
I think your code works Griff. I've just plugged it into an existing an existing IDispatchMessageInspector implementation and it generated a good (transformed) message. I therefore suspect your problem lies elsewhere.
How are you establishing that the 'losing' the correct contents? Could whatever is inspecting the transformed message be reading the message prior to transformation by mistake?
Unless you are trying to correlate state with the BeforeSendReply method then you should be returning null instead of the request reference.
Our c#.net software connects to an online app to deal with accounts and a shop. It does this using HttpWebRequest and HttpWebResponse.
An example of this interaction, and one area where the exception in the title has come from is:
var request = HttpWebRequest.Create(onlineApp + string.Format("isvalid.ashx?username={0}&password={1}", HttpUtility.UrlEncode(username), HttpUtility.UrlEncode(password))) as HttpWebRequest;
request.Method = "GET";
using (var response = request.GetResponse() as HttpWebResponse)
using (var ms = new MemoryStream())
{
var responseStream = response.GetResponseStream();
byte[] buffer = new byte[4096];
int read;
do
{
read = responseStream.Read(buffer, 0, buffer.Length);
ms.Write(buffer, 0, read);
} while (read > 0);
ms.Position = 0;
return Convert.ToBoolean(Encoding.ASCII.GetString(ms.ToArray()));
}
The online app will respond either 'true' or 'false'. In all our testing it gets one of these values, but for a couple of customers (out of hundreds) we get this exception System.FormatException: String was not recognized as a valid Boolean Which sounds like the response is being garbled by something. If we ask them to go to the online app in their web browser, they see the correct response. The clients are usually on school networks which can be fairly restrictive and often under proxy servers, but most cope fine once they've put the proxy details in or added a firewall exception. Is there something that could be messing up the response from the server, or is something wrong with our code?
Indeed, it's possible that the return result is somehow different.
Is there any particular reason you are doing the reasonably elaborate method of reading the repsonse there? Why not:
string data;
using(HttpWebResponse response = request.GetResponse() as HttpWebResponse){
StreamReader str = new StreamReader(response.GetResponseStream());
data = str.ReadToEnd();
str.Close();
}
string cleanResult = data.Trim().ToLower();
// log this
return Convert.ToBoolean(cleanResult);
First thing to note is I would definitely use something like:
bool myBool = false;
Boolean.TryParse(Encoding.ASCII.GetString(ms.ToArray()), myBool);
return myBool;
It's not some localisation issue is it? It's expecting the Swahili version of 'true', and getting confused. Are all the sites in one country, with the same language, etc?
I'd add logging, as suggested by others, and see what results you're seeing.
I'd also lean towards changing the code as silky suggested, though with a few further changes from me (code 'smell' issues, IMO); Use using around the stream reader, as well as the response.
Also, I don't think the use of as is appropriate in this instance. If the Response can't be cast to HttpWebResponse (which, admittedly is unlikely, but still) you'll get a NullRef exception on the response.GetResponseStream() bit which is both a vague error, and you've lost the original line number. Using (HttpWebResponse)request.GetResponse() will give you a more correct error, and the correct line number of the actual error.
I have implemented a small Windows Service which runs every 9 minutes and writes the data which comes througth a webservice to the db.
Do do the db work I use Linq To SQL
using (var db = new DataClasses1DataContext())
{
var currentWeather = this.GetWeatherData();
//////TODO Add the data correct
var newEntry = new WeatherData()
{
test = currentWeather.dateGenerated.ToShortTimeString()
};
//var test = db.WeatherDatas.First();
db.WeatherDatas.InsertOnSubmit(newEntry); // this throws Invalid Operation Exception
db.SubmitChanges();
}
Why does it throw this exception? the same codeblock in a console programm runs nice
alt text http://img687.imageshack.us/img687/7588/unbenanntxb.png
Have you set up the connection string in app.Config correctly?
IIRC, the default constructor on an L2S DataContext reads the connection string from the config file. If the connection string points to the the wrong database (or one that doesn't exist) you may very well receive an exception.
This could also explain why this piece of code works when executed in a different context.