Suppose I have a method, which does not require a response, for example:
[ServiceContract]
public interface IWCFTestService
{
[OperationContract]
void ReceiveSomeData(MyDto someDtoObj);
}
Now, inside the actual service implementation, I can write:
public void ReceiveSomeData(MyDto receivedRequest)
{
Task.Run( () => OtherProjectOtherClass.DoWhateverYouWant(receivedRequest) )
//... because I am outta here as fast as possible
}
I assume the caller will get a 200-OK all the time; Note also that I have not written any task async/await inside the WCF method itself.
Is it acceptable to use TPL inside WCF this way, any gotchas?
Note: I am not concerned with business rules, I am more concerned whether it is technically acceptable for WCF/TPL to interact this way, will I run into (technical) trouble?
There are a couple of things that you have to take in consideration.
Callers won't be notitified when the task is actually completed, and if it does, whether it did so with a failure (an unhandled exception for example) or not.
When the process hosting the service stops, your tasks may not be completed yet and unless you keep a reference to them you won't be able to wait for them to finish.
If an unhandled exception occurs in OtherProjectOtherClass.DoWhateverYouWant(receivedRequest) you won't be signalled. So make sure the whole body is contained in a try..catch and apply some logging or take other measures.
If a lot of requests are made and the tasks take a long time to complete you can run out of threads in the threadpool. If so, use a dedicated thread as Task.Run grabs one from the pool.
I do wonder, what is happening inside OtherProjectOtherClass.DoWhateverYouWant(receivedRequest)? Is it CPU intensive or more I/O related? If it is I/O intensive, rewrite it to a Task based method instead of using Task.Run.
I can't tell if these issues are really problems for you but keep it in mind.
Related
My ASP.NET core application uses Entity Framework Core. As you would expect must controller methods are async and call async methods of EF Core.
I also have controller methods thats need to read from and write to excel files. I'm using OpenXml. Since these are IO operation, ideally I they would be an async operation but OpenXml doesn't offer any async methods. Here is a simplified example
private async Task<model> ReadFromExcel()
{
using var document = SpreadsheetDocument.Open("filePathAndName", false);
// read data into model
document.Close();
context.Models.Add(newModel);
await Context.SaveAsync();
return newModel;
}
Also, I need to find the file in a folder first which I would also like to make async.
Directory.EnumerateFiles("excelFolderName", ".xlsx");
According to this document ASP.NET Core Performance Best Practices I shouldn't use Task.Run to make an synchronous API asynchronous. I understand why but does that rule apply to IO operations which will block the thread potential for a few seconds? Should I make these IO operations async and if so what is the base way to make reading and writing excel file and getting file list asynchronous?
Since these are IO operation, ideally I they would be an async operation but OpenXml doesn't offer any async methods.
Also, I need to find the file in a folder first which I would also like to make async.
Ideally, those would be asynchronous APIs. But they're not. The way to make them asynchronous is to fix the API, not wrap it in Task.Run. You can open a request with the maintainers of OpenXml for asynchronous APIs. The file system operation is more awkward; it's a Win32 limitation, not a BCL limitation, and it's unlikely to be fixed, but you can ask.
does that rule apply to IO operations which will block the thread potential for a few seconds?
Yes.
The request is blocked for the same amount of time whether it's synchronous or asynchronous. So the thing to consider is how threads are blocked. In the ideal asynchronous case, no threads are blocked. Since you only have synchronous APIs, you do have to block a thread; calling the API directly will block a thread pool thread, and shoving it off to Task.Run will block a different thread pool thread - which is pointless.
Should I make these IO operations async and if so what is the base way to make reading and writing excel file and getting file list asynchronous?
You can't "make them async". You can request async APIs and then use the synchronous ones for now.
I (new to WCF) am writing a WCF service that acquires and analyzes an X-ray spectrum - i.e. it is a long-running process, sometimes several minutes. Naturally, this begs for asynchronous calls so, using wsDualHttpBinding and defining the following in my ServiceContract
[ServiceContract(Namespace="--removed--",
SessionMode=SessionMode.Required, CallbackContract=typeof(IAnalysisSubscriber))]
public interface IAnalysisController
{
// Simplified - removed other declarations for clarity
[OperationContract]
Task<Measurement> StartMeasurement(MeasurementRequest request);
}
And the (simplified) implementation has
async public Task<Measurement> StartMeasurement(MeasurementRequest request)
{
m_meas = m_config.GetMeasurement(request);
Spectrum sp = await m_mca.Acquire(m_meas.AcquisitionTime, null);
UpdateSpectrum(m_meas, sp);
return m_meas;
}
private void McaProgress(Spectrum sp)
{
m_client.ReportProgress(sp);
}
Where m_client is the callback object obtained from m_client = OperationContext.Current.GetCallbackChannel(); in the "Connect" method - called when the WCF client first connects. This works as long as I don't use progress reporting, but as soon as I add progress reporting by changing the "null" in the m_mca.Acquire() method to "new Progress(McaProgress)", on the first progress report, the client generates an error "The server did not provide a meaningful reply; this might be caused by a contract mismatch..."
I understand the client is probably awaiting a particular reply of a Task rather than having a callback made into it, but how do I implement this type of progress reporting with WCF? I would like the client to be able to see the live spectrum as it is generated and get an estimate of the time remaining to complete the spectrum acquisition. Any help or pointers to where someone has implemented this type of progress reporting with WCF is much appreciated (I've been searching but find mostly references to EAP or APM and WCF and not much related to TAP).
Thanks, Dave
Progress<T> wasn't really meant for use in WCF. It was designed for UI apps, and may behave oddly depending on your host (e.g., ASP.NET vs self-hosted).
I would recommend writing a simple IProgress<T> implementation that just called IAnalysisSubscriber.ReportProgress directly. Also make sure that IAnalysisSubscriber.ReportProgress has OperationContract.IsOneWay set to true.
I am trying to create a WCF service that supports asynchronous calls. I followed all samples and tutorials I could find, and all of them have the customary pattern of one synchronous method, and the async Begin and End such as:
[OperationContract(AsyncPattern = false)]
string GetData(int value);
[OperationContract(AsyncPattern = true)]
IAsyncResult BeginGetData(int value, AsyncCallback callback, object asyncState);
string EndGetData(IAsyncResult result);
However, only the synchronous GetData gets called, no matter what I do on the client side. Fiddler tells me that the message is always the same:
<s:Envelope
xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"><s:Body><GetData
xmlns="http://tempuri.org/"><value>0</value></GetData></s:Body></s:Envelope>
When I remove the synchronous GetData interface, the async method now is properly called.
Is this normal behavior? Is there anything else I should do to support sync and async versions of a method?
This is a common misconception. You assume that you need to make the server asynchronous in order for the client to be able to make async calls. This is not true. Server and client are 100% independent. They are separated by a binary wire protocol.
The message that you see in Fiddler is always the same because SOAP does not know anything about sync or async. At the SOAP level your decision does not manifest itself. For that reason the client cannot observe your server-side decision, either.
This means you can just make the server synchronous in still have a truely async client, or the other way around.
In any case, you should only implement one pattern on the server: Either sync or async. Never both. Get rid of one of your implementations. From a functional standpoint it doesn't matter which one stays.
I'm pulling up important information from the comments here:
It is hard to fit an explanation about when to use server-side async
into this comment box. In short, don't use it on the server by
default. Use it if special circumstances make it attractive or
necessary.
On a meta-level let me point out that async IO has become
a fad that should not be followed lightly. The community is in a very
unfortunate state of misinformation about this right now.
I am currently looking at some code which basically reads data line-by-line from a file and calls an appropriate method on a WCF client for each line. My problem with the code is that a new client is created for each operation ie:
function CallSomeOp(line)
{
using (var client = new Client()
{
client.SomeOp(line);
}
}
This seems to make the client close the connection and reopen a new one for each line in the file which seems like a big overhead. What I would like to do is move the creation of the client outside the function and into initialization, so that multiple operations are called on one client, ie:
using (var client = new Client())
{
for(var line in lines)
{
CallSomeOp(line, client);
}
}
But I'm not sure if this is OK or not. Is there some problem with calling multiple operations on a client, ie will I eventually run out of memory or something?
An operation is typically stateless, so that's expected behaviour. You can change the InstanceContextMode though. This determines if a new instance is created with each call or that there is a single instance. The question is, should you? Like I said, a single call is typically stateless. Having just a single instance doesn't scale very well either. I'd suggest to keep InstanceContextMode to be PerCall, that way, for every call the webserver creates a single instance, I wouldn't worry too much about overhead. If overhead really does become a problem, maybe a service isn't what you need.
Then there's the ConcurrencyMode which allows you to specify multiple threads to be created.
As far as the client is concerned, you don't need to re-create it everytime you want to call an operation. Alternatively, you could also look at the ChannelFactory if both client and server have knowledge of the contract. This answer provides some detail.
It's definitely a good idea to reuse a WCF client across multiple calls, since there is indeed a cost in creating new instances of it, even if that's been improved in later versions.
The most important thing to be aware of when reusing a WCF client, though, is that if a service operation fails, the whole client will be put in a faulted state and can no longer be used.
You will have to take this into consideration in your client code. Depending on your requirements, one way to handle this situation could be to recreate the client proxy in case of failure and continue processing the input, like shown in this example:
var client = new ServiceClient();
foreach (var line in lines)
{
try
{
client.DoSomething(line);
}
catch
{
client.Abort();
client = new ServiceClient()
}
}
Note that you may also want to keep track of how many times the service call fails and with what type of exception, since in some situations it would make more sense to completely abort the operation instead of keep trying, e.g. when the remote service is not reachable.
I need to extract several header values at the start of each request and place them into a ClientContext object that can be injected into my application code by MEF. I am using Preview 5 of the WCF Web API and don't see a way to do this.
In 'standard' WCF, I would create a class that implements IExtension<OperationContext> and have the following property to wire it all together:
[Export(typeof(IClientContext)]
[PartCreationPolicy(CreationPolicy.NonShared)]
public static ClientContextExtension Current
{
get
{
var operationContext = OperationContext.Current;
if (operationContext == null)
return null;
var extension = operationContext.Extensions.Find<ClientContextExtension>();
if (extension == null)
{
extension = new ClientContextExtension();
operationContext.Extensions.Add(extension);
}
return extension;
}
}
A custom DelegatingHandler calls ClientContextExtension.Current and sets the properties from the header values. Unfortunately, with WCF Web API, OperationContext.Current is always null!
I cannot figure out a way to make this work with the Web API. Any help is appreciated!!!
I've come up with a working solution but remain open to other options. First, some rationale behind the original approach...
Because WCF uses thread pooling, anything based on a per-thread model may (and will) have a lifetime that extends beyond an individual request. I needed a way to store client context information pulled from the HTTP headers for each request as the information will be different each time. This means I can't persist the context information per-thread because the thread will be re-used.
Or can I?
The flaw in my logic was that thread re-use was the problem. In reality, each thread is only every servicing a single request at one time thereby making any information in that thread isolated to that request. Therefore, all I need to do is make sure that the information is relavent to that request and my problem is solved.
My solution was to refactor the Current property to reference a private static field marked with the [ThreadStatic()] attribute, ensuring that each instance was specific to the thread. Then, in my DelegatingHandler, which executes for each request, I reset the properties of the object for that request. Subsequent calls to Current during that request return the request-specific information and the next request handled by the thread gets updated in the DelegatingHandler so as far as my other code is concerned, the context is per-request.
Not perfect, but it at least gets me up and running for the moment. As I said, I am open to other solutions.
UPDATE
Upon closer inspection, this solution is not working as there is no thread affinity between the DelegatingHandler and the service code that is making use of the context object. As a result, sometimes my call to retrieve the ThreadStatic object works as expected but on other occasions I get a new instance because the code is operating on a different thread than the handler.
So, disregard this solution. Back to the drawing board.
UPDATE TO MY UPDATE
After discussing my problem with Glenn Block, it turns out that it is just a matter of making sure the context is set on the same thread the request handler (the service) is executing. The solution is to use an HttpOperationHandler instead of a MessageHandler.
According to Glenn, message handlers operate asynchronously which means they could execute on a different thread from the request handler (service) so we should never do anything in a message handler that requires thread affinity. On the other hand, operation handlers run synchronously on the same thread as the request handler, therefore we can rely on thread affinity.
So, I simply moved my code from a MessageHandler to an HttpOperationHandler and have the desired results.
You can read a full explanation here: http://sonofpirate.blogspot.com/2011/11/modeling-client-context-in-wcf-web-api.html
You can try to use a
HttpOperationHandler<HttpRequestMessage, HttpRequestMessage>
There you should be able to access the headers.