I'm using json to write data that the user enters. I want this to be done with SerializeAsync. But how can I best do this based on this code?
This is my code without SerializeAsync
public static bool TempFileExists()
{
return File.Exists(TempJsonPath());
}
public static async Task WriteToTempJsonAsync(DeclarationJoined declaration)
{
if (TempFileExists())
{
var json = JsonSerializer.Serialize(declaration);
File.WriteAllText(PaymentCardListPath(), json);
}
}
And this is what I have tried
public static async Task WriteToTempJsonAsync(DeclarationJoined declaration)
{
if (TempFileExists())
{
Stream writer = new FileStream(TempJsonPath(), FileMode.Append);
await JsonSerializer.SerializeAsync(writer, declaration);
}
}
Every time I do this SerializeAsync I got a sharing violation. How can I do this asynchronously?
I had a perfectly working API that was making http calls & a UI that was using that API.
Everything was working and it was built really ugly ( 2 solutions for everything), so I wanted to separate everything so it would be more organized.
UI
DataManagerService
DataManager
Contracts
After a lot of copy paste & dependencies references it all looked like its working with 0 errors.
BUT now something weird happens, A method is being skipped and I have NO IDEA WHY.
I am not on release mode.
If anyone has any ideas I would appreciate it!
The method that is being skipped :
private static List<Actor> ReadActorsFromJson(string json)
{
List<Actor> celebListReadFromFile;
try
{
var celebJson = File.ReadAllText(json);
celebListReadFromFile = JsonConvert.DeserializeObject<List<Actor>>(celebJson);
}
catch (Exception ex)
{
celebListReadFromFile = new List<Actor>();
// Empty list/whatever it got in it
}
return celebListReadFromFile;
}
Which is being invoked by :
public static async Task SaveOriginal()
{
foreach (var currceleb in ReadActorsFromJson(filePath))
{
var curr = currceleb;
originalList.TryAdd(currceleb.name, currceleb);
}
}
and this method is being invoked by the classes static constructor:
static Logic()
{
originalList = new ConcurrentDictionary<string, Actor>();
filePath = ConfigurationManager.AppSettings["tempList"];
File.Copy(filePath, BACKUP, true);
// invoking the method
SaveOriginal();
}
The API:
using Contracts;
using System.Threading.Tasks;
using System.Web.Mvc;
namespace WebApplication12.Controllers
{
public class ValuesController : Controller
{
public ILogic _Ilogic;
public ValuesController(ILogic logic)
{
_Ilogic = logic;
}
// GET api/values
public async Task<ActionResult> GetActors()
{
return Json(await _Ilogic.GetAllActorsAsync(), JsonRequestBehavior.AllowGet);
}
public async Task<ActionResult> RemoveActorAsync(Actor actor) {
await _Ilogic.RemoveActorAsync(actor.name);
return Json(await _Ilogic.GetAllActorsAsync());
}
public async Task<ActionResult> ResetAsync()
{
await _Ilogic.ResetAsync();
return Json(await _Ilogic.GetAllActorsAsync());
}
}
}
The business logic :
using System;
using System.Collections.Generic;
using System.IO;
using System.Configuration;
using System.Collections.Concurrent;
using System.Threading.Tasks;
using System.Linq;
using Newtonsoft.Json;
using Contracts;
namespace DataManager
{
public class Logic : ILogic
{
static string filePath;
private static ConcurrentDictionary<string, Actor> originalList;
const string BACKUP = #"C:\tempList\backup.txt";
static Logic()
{
originalList = new ConcurrentDictionary<string, Actor>();
filePath = ConfigurationManager.AppSettings["tempList"];
File.Copy(filePath, BACKUP, true);
SaveOriginal();
}
public async static Task<List<Actor>> GetCelebritiesInner()
{
return originalList.Values.ToList();
}
public async Task<List<Actor>> GetAllActorsAsync()
{
return await GetCelebritiesInner();
}
// Try to read the data from the Json and initialize it. if failed , initialize with whatever it got. return
private static List<Actor> ReadActorsFromJson(string json)
{
List<Actor> celebListReadFromFile;
try
{
var celebJson = File.ReadAllText(json);
celebListReadFromFile = JsonConvert.DeserializeObject<List<Actor>>(celebJson);
}
catch (Exception ex)
{
celebListReadFromFile = new List<Actor>();
// Empty list/whatever it got in it
}
return celebListReadFromFile;
}
public async Task RemoveActorAsync(string name)
{
if (originalList.TryRemove(name, out Actor removedActor))
{
var jsonToWrite = JsonConvert.SerializeObject(await GetCelebritiesInner());
try
{
File.WriteAllText(filePath, jsonToWrite);
}
catch (Exception ex)
{
//Unable to remove due to an error.
}
}
}
public async Task ResetAsync()
{
originalList.Clear();
await UpdateFile();
await SaveOriginal();
}
//Saving the actor, adding the name as key & object as value.
public static async Task SaveOriginal()
{
foreach (var currceleb in ReadActorsFromJson(filePath))
{
var curr = currceleb;
originalList.TryAdd(currceleb.name, currceleb);
}
}
public static async Task UpdateFile()
{
File.Copy(BACKUP, filePath, true);
}
}
}
When running the program, the static ctor is being invoked and should invoke the SaveOriginal method. which it doesn't.
Static constructor calls when the first access is made. You are using dependency injection which is lazy loading. It doesn't create object until first access is made.
So, Try to get/set any property or method, static constructor will be called first.
Ok so I cleaned and re built every solution one by one ( I don't know if it helped) & then I ran the API, sent a request from POSTMAN instead of opening the UI, in postman I got the following error:
Could not load file or assembly 'Newtonsoft.Json, Version=12.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed' or one of its dependencies
I looked at the versions in the Nuget packages and there was no 12.0.0.0 in there, so I removed Newtonsoft.Json from all solutions, re-installed Newtonsoft.Json in every solution and it worked.
turns out somehow when opening the new solutions, I installed different Newtonsoft.Json versions. and this caused the program to skip the method without giving the stack trace like postman did, weird.
I am trying to use the concept of async and await in my program. The program abruptly exits. I am trying to get the content length from few random urls and process it and display the size in bytes of each url.
Code:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net;
namespace TestProgram
{
public class asyncclass
{
public async void MainCall() {
await SumPageSizes();
}
public async Task SumPageSizes(){
List<string> urllist = GetUrlList();
foreach (var url in urllist)
{
byte[] content = await GetContent(url);
Displayurl(content, url);
}
}
private void Displayurl(byte[] content, string url)
{
var length = content.Length;
Console.WriteLine("The bytes length for the url response " + url + " is of :" +length );
}
private async Task<byte[]> GetContent(string url)
{
var content = new MemoryStream();
try
{
var obj = (HttpWebRequest)WebRequest.Create(url);
WebResponse response = obj.GetResponse();
using (Stream stream = response.GetResponseStream())
{
await stream.CopyToAsync(content);
}
}
catch (Exception ex)
{
Console.WriteLine(ex.StackTrace);
}
return content.ToArray();
}
private List<string> GetUrlList()
{
var urllist = new List<string>(){
"http://msdn.microsoft.com/library/windows/apps/br211380.aspx",
"http://msdn.microsoft.com",
"http://msdn.microsoft.com/en-us/library/hh290136.aspx",
"http://msdn.microsoft.com/en-us/library/ee256749.aspx",
"http://msdn.microsoft.com/en-us/library/hh290138.aspx",
"http://msdn.microsoft.com/en-us/library/hh290140.aspx",
"http://msdn.microsoft.com/en-us/library/dd470362.aspx",
"http://msdn.microsoft.com/en-us/library/aa578028.aspx",
"http://msdn.microsoft.com/en-us/library/ms404677.aspx",
"http://msdn.microsoft.com/en-us/library/ff730837.aspx"
};
return urllist;
}
}
}
Main
public static void Main(string[] args)
{
asyncclass asyncdemo = new asyncclass();
asyncdemo.MainCall();
}
MainCall returns an uncompleted task and no other line of code is present beyond that, so your program ends
To wait for it use:
asyncdemo.MainCall().Wait();
You need to avoid async void and change MainCall to async Task in order to be able to wait for it from the caller.
Since this seems to be a console application, you can't use the await and async for the Main method using the current version of the compiler (I think the feature is being discussed for upcoming implementation in C# 7).
The problem is that you don't await an asynchron method and therefore you application exits before the method ended.
In c# 7 you could create an async entry point which lets you use the await keyword.
public static async Task Main(string[] args)
{
asyncclass asyncdemo = new asyncclass();
await asyncdemo.MainCall();
}
If you want to bubble your exceptions from MainCall you need to change the return type to Task.
public async Task MainCall()
{
await SumPageSizes();
}
If you wanted to run your code async before c# 7 you could do the following.
public static void Main(string[] args)
{
asyncclass asyncdemo = new asyncclass();
asyncdemo.MainCall().Wait();
// or the following line if `MainCall` doesn't return a `Task`
//Task.Run(() => MainCall()).Wait();
}
You have to be very careful when using async void methods. Those will not be awaited. One normal example of an async void is when you are calling an awaitable method inside a button click:
private async void Button_Click(object sender, RoutedEventArgs e)
{
// run task here
}
This way the UI won't be stuck waiting for the button click to complete.
On most custom methods you will almost always want to return a Task so that you are able to know when your method is finished.
I have been trying to use some sample code provided by Microsoft. I
Downloaded the file
Hit unblock on the file
Unzipped the file
Clicked on the solution called QuizGame sample
The solution opened in Visual Studio 2015
The solution automatically registered hundreds of errors
I opened the C# files in the solution explorer to see what was going on and each C# file had tons of errors. Each error was some how related to the System reference.
Error CS0246 The type or namespace name 'System' could not be found
(are you missing a using directive or an assembly reference?)
The warning was
Warning Cannot resolve Assembly or Windows Metadata file 'System.Runtime.dll'
It also shows all the System imports with red lines underneath them. It sounds like it is not recognizing the System reference.
Here is the code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Networking.Sockets;
namespace P2PHelper
{
public class P2PSessionHost : P2PSession, IDisposable
{
private Dictionary<Guid, P2PClient> ClientMap { get; set; }
private StreamSocketListener SessionListener { get; set; }
private Timer Timer { get; set; }
public P2PSessionHost(P2PSessionConfigurationData config) : base(config)
{
this.SessionListener = new StreamSocketListener();
this.ClientMap = new Dictionary<Guid, P2PClient>();
}
public void Dispose()
{
this.SessionListener.Dispose();
this.SessionListener = null;
}
public async Task<bool> CreateP2PSession(SessionType type)
{
if (this.SessionListener == null) return false;
if (type != SessionType.LocalNetwork) throw new NotSupportedException(
"SessionType.LocalNetwork is the only SessionType supported.");
this.SessionHost = true;
this.SessionListener.ConnectionReceived += async (s, e) => await OnConnectionReceived(e.Socket);
await this.SessionListener.BindEndpointAsync(null, Settings.tcpPort);
this.InitializeNetworkInfo();
return await this.InitializeMulticast(null);
}
public bool RemoveClient(Guid clientID)
{
return this.ClientMap.Remove(clientID);
}
private bool AcceptingConnections { get; set; }
public void StartAcceptingConnections()
{
AcceptingConnections = true;
this.Timer = new Timer(async state => await SendMulticastMessage(""), null, 0, 500);
}
public void StopAcceptingConnections()
{
AcceptingConnections = false;
this.Timer.Dispose();
}
private async Task OnConnectionReceived(StreamSocket socket)
{
byte[] message = await RetrieveMessage(socket);
var newClient = new P2PClient { clientTcpIP = socket.Information.RemoteAddress.ToString() };
if (AcceptingConnections)
{
if (GetGuid(newClient).ToString() == (new Guid()).ToString())
{
Guid newGuid = Guid.NewGuid();
this.ClientMap.Add(newGuid, newClient);
this.OnConnectionComplete(newGuid);
}
}
this.OnMessageReceived(message, GetGuid(newClient));
}
private Guid GetGuid(P2PClient client)
{
return this.ClientMap.FirstOrDefault(
kvp => kvp.Value.clientTcpIP == client.clientTcpIP).Key;
}
protected async Task SendMulticastMessage(string output)
{
using (var multicastOutput = await this.MulticastSocket.GetOutputStreamAsync(
new Windows.Networking.HostName(Settings.multicastIP),
this.MulticastSocket.Information.LocalPort))
{
await multicastOutput.WriteAsync(Encoding.UTF8.GetBytes(output).AsBuffer());
}
}
public async Task<bool> SendMessage(Guid clientID, object message, Type type = null)
{
P2PClient client;
if (this.ClientMap.TryGetValue(clientID, out client))
{
return await base.SendMessage(message, client.clientTcpIP, Settings.tcpPort, type ?? typeof(object));
}
return false;
}
public async Task<bool> SendMessageToAll(object message, Type type = null)
{
var messageTasks = this.ClientMap.Keys.Select(guid => this.SendMessage(guid, message, type));
// When all the tasks complete, return true if they all succeeded.
return (await Task.WhenAll(messageTasks)).All(value => { return value; });
}
}
}
Not recognizing .NET references and namespaces like System.Generics, System, etc, usualy caused by compiling in Client Profile or referencing DLLs that are compiled using different versions of .NET.
Go to the project settings and validate you are compiling to the right version of .NET with no Client Profile turned on.
I generated a proxy with task-based operations.
How should this service be invoked properly (disposing of the ServiceClient and the OperationContext afterwards) using async/await?
My first attempt was:
public async Task<HomeInfo> GetHomeInfoAsync(DateTime timestamp)
{
using (var helper = new ServiceHelper<ServiceClient, ServiceContract>())
{
return await helper.Proxy.GetHomeInfoAsync(timestamp);
}
}
Being ServiceHelper a class which creates the ServiceClient and the OperationContextScope and disposes of them afterwards:
try
{
if (_operationContextScope != null)
{
_operationContextScope.Dispose();
}
if (_serviceClient != null)
{
if (_serviceClient.State != CommunicationState.Faulted)
{
_serviceClient.Close();
}
else
{
_serviceClient.Abort();
}
}
}
catch (CommunicationException)
{
_serviceClient.Abort();
}
catch (TimeoutException)
{
_serviceClient.Abort();
}
catch (Exception)
{
_serviceClient.Abort();
throw;
}
finally
{
_operationContextScope = null;
_serviceClient = null;
}
However, this failed miserably when calling two services at the same time with the following error: "This OperationContextScope is being disposed on a different thread than it was created."
MSDN says:
Do not use the asynchronous “await” pattern within a OperationContextScope block. When the continuation occurs, it may run on a different thread and OperationContextScope is thread specific. If you need to call “await” for an async call, use it outside of the OperationContextScope block.
So that's the problem! But, how do we fix it properly?
This guy did just what MSDN says:
private async void DoStuffWithDoc(string docId)
{
var doc = await GetDocumentAsync(docId);
if (doc.YadaYada)
{
// more code here
}
}
public Task<Document> GetDocumentAsync(string docId)
{
var docClient = CreateDocumentServiceClient();
using (new OperationContextScope(docClient.InnerChannel))
{
return docClient.GetDocumentAsync(docId);
}
}
My problem with his code, is that he never calls Close (or Abort) on the ServiceClient.
I also found a way of propagating the OperationContextScope using a custom SynchronizationContext. But, besides the fact that it's a lot of "risky" code, he states that:
It’s worth noting that it does have a few small issues regarding the disposal of operation-context scopes (since they only allow you to dispose them on the calling thread), but this doesn’t seem to be an issue since (at least according to the disassembly), they implement Dispose() but not Finalize().
So, are we out of luck here? Is there a proven pattern for calling WCF services using async/await AND disposing of BOTH the ServiceClient and the OperationContextScope? Maybe someone form Microsoft (perhaps guru Stephen Toub :)) can help.
Thanks!
[UPDATE]
With a lot of help from user Noseratio, I came up with something that works: do not use OperationContextScope. If you are using it for any of these reasons, try to find a workaround that fits your scenario. Otherwise, if you really, really, need OperationContextScope, you'll have to come up with an implementation of a SynchronizationContext that captures it, and that seems very hard (if at all possible - there must be a reason why this isn't the default behavior).
So, the full working code is:
public async Task<HomeInfo> GetHomeInfoAsync(DateTime timestamp)
{
using (var helper = new ServiceHelper<ServiceClient, ServiceContract>())
{
return await helper.Proxy.GetHomeInfoAsync(timestamp);
}
}
With ServiceHelper being:
public class ServiceHelper<TServiceClient, TService> : IDisposable
where TServiceClient : ClientBase<TService>, new()
where TService : class
{
protected bool _isInitialized;
protected TServiceClient _serviceClient;
public TServiceClient Proxy
{
get
{
if (!_isInitialized)
{
Initialize();
_isInitialized = true;
}
else if (_serviceClient == null)
{
throw new ObjectDisposedException("ServiceHelper");
}
return _serviceClient;
}
}
protected virtual void Initialize()
{
_serviceClient = new TServiceClient();
}
// Implement IDisposable.
// Do not make this method virtual.
// A derived class should not be able to override this method.
public void Dispose()
{
Dispose(true);
// Take yourself off the Finalization queue
// to prevent finalization code for this object
// from executing a second time.
GC.SuppressFinalize(this);
}
// Dispose(bool disposing) executes in two distinct scenarios.
// If disposing equals true, the method has been called directly
// or indirectly by a user's code. Managed and unmanaged resources
// can be disposed.
// If disposing equals false, the method has been called by the
// runtime from inside the finalizer and you should not reference
// other objects. Only unmanaged resources can be disposed.
protected virtual void Dispose(bool disposing)
{
// If disposing equals true, dispose all managed
// and unmanaged resources.
if (disposing)
{
try
{
if (_serviceClient != null)
{
if (_serviceClient.State != CommunicationState.Faulted)
{
_serviceClient.Close();
}
else
{
_serviceClient.Abort();
}
}
}
catch (CommunicationException)
{
_serviceClient.Abort();
}
catch (TimeoutException)
{
_serviceClient.Abort();
}
catch (Exception)
{
_serviceClient.Abort();
throw;
}
finally
{
_serviceClient = null;
}
}
}
}
Note that the class supports extension; perhaps you need to inherit and provide credentials.
The only possible "gotcha" is that in GetHomeInfoAsync, you can't just return the Task you get from the proxy (which should seem natural, why create a new Task when you already have one). Well, in this case you need to await the proxy Task and then close (or abort) the ServiceClient, otherwise you'll be closing it right away after invoking the service (while bytes are being sent over the wire)!
OK, we have a way to make it work, but it'd be nice to get an answer from an authoritative source, as Noseratio states.
I think a feasible solution might be to use a custom awaiter to flow the new operation context via OperationContext.Current. The implementation of OperationContext itself doesn't appear to require thread affinity. Here is the pattern:
async Task TestAsync()
{
using(var client = new WcfAPM.ServiceClient())
using (var scope = new FlowingOperationContextScope(client.InnerChannel))
{
await client.SomeMethodAsync(1).ContinueOnScope(scope);
await client.AnotherMethodAsync(2).ContinueOnScope(scope);
}
}
Here is the implementation of FlowingOperationContextScope and ContinueOnScope (only slightly tested):
public sealed class FlowingOperationContextScope : IDisposable
{
bool _inflight = false;
bool _disposed;
OperationContext _thisContext = null;
OperationContext _originalContext = null;
public FlowingOperationContextScope(IContextChannel channel):
this(new OperationContext(channel))
{
}
public FlowingOperationContextScope(OperationContext context)
{
_originalContext = OperationContext.Current;
OperationContext.Current = _thisContext = context;
}
public void Dispose()
{
if (!_disposed)
{
if (_inflight || OperationContext.Current != _thisContext)
throw new InvalidOperationException();
_disposed = true;
OperationContext.Current = _originalContext;
_thisContext = null;
_originalContext = null;
}
}
internal void BeforeAwait()
{
if (_inflight)
return;
_inflight = true;
// leave _thisContext as the current context
}
internal void AfterAwait()
{
if (!_inflight)
throw new InvalidOperationException();
_inflight = false;
// ignore the current context, restore _thisContext
OperationContext.Current = _thisContext;
}
}
// ContinueOnScope extension
public static class TaskExt
{
public static SimpleAwaiter<TResult> ContinueOnScope<TResult>(this Task<TResult> #this, FlowingOperationContextScope scope)
{
return new SimpleAwaiter<TResult>(#this, scope.BeforeAwait, scope.AfterAwait);
}
// awaiter
public class SimpleAwaiter<TResult> :
System.Runtime.CompilerServices.INotifyCompletion
{
readonly Task<TResult> _task;
readonly Action _beforeAwait;
readonly Action _afterAwait;
public SimpleAwaiter(Task<TResult> task, Action beforeAwait, Action afterAwait)
{
_task = task;
_beforeAwait = beforeAwait;
_afterAwait = afterAwait;
}
public SimpleAwaiter<TResult> GetAwaiter()
{
return this;
}
public bool IsCompleted
{
get
{
// don't do anything if the task completed synchronously
// (we're on the same thread)
if (_task.IsCompleted)
return true;
_beforeAwait();
return false;
}
}
public TResult GetResult()
{
return _task.Result;
}
// INotifyCompletion
public void OnCompleted(Action continuation)
{
_task.ContinueWith(task =>
{
_afterAwait();
continuation();
},
CancellationToken.None,
TaskContinuationOptions.ExecuteSynchronously,
SynchronizationContext.Current != null ?
TaskScheduler.FromCurrentSynchronizationContext() :
TaskScheduler.Current);
}
}
}
Simple way is to move the await outside the using block
public Task<Document> GetDocumentAsync(string docId)
{
var docClient = CreateDocumentServiceClient();
using (new OperationContextScope(docClient.InnerChannel))
{
var task = docClient.GetDocumentAsync(docId);
}
return await task;
}
I decide to write my own code that helps with this, posting in case this helps anyone. Seems to be a little less to go wrong (unforeseen races etc) vs the SimpleAwaiter implementation above but you be the judge:
public static class WithOperationContextTaskExtensions
{
public static ContinueOnOperationContextAwaiter<TResult> WithOperationContext<TResult>(this Task<TResult> #this, bool configureAwait = true)
{
return new ContinueOnOperationContextAwaiter<TResult>(#this, configureAwait);
}
public static ContinueOnOperationContextAwaiter WithOperationContext(this Task #this, bool configureAwait = true)
{
return new ContinueOnOperationContextAwaiter(#this, configureAwait);
}
public class ContinueOnOperationContextAwaiter : INotifyCompletion
{
private readonly ConfiguredTaskAwaitable.ConfiguredTaskAwaiter _awaiter;
private OperationContext _operationContext;
public ContinueOnOperationContextAwaiter(Task task, bool continueOnCapturedContext = true)
{
if (task == null) throw new ArgumentNullException("task");
_awaiter = task.ConfigureAwait(continueOnCapturedContext).GetAwaiter();
}
public ContinueOnOperationContextAwaiter GetAwaiter() { return this; }
public bool IsCompleted { get { return _awaiter.IsCompleted; } }
public void OnCompleted(Action continuation)
{
_operationContext = OperationContext.Current;
_awaiter.OnCompleted(continuation);
}
public void GetResult()
{
OperationContext.Current = _operationContext;
_awaiter.GetResult();
}
}
public class ContinueOnOperationContextAwaiter<TResult> : INotifyCompletion
{
private readonly ConfiguredTaskAwaitable<TResult>.ConfiguredTaskAwaiter _awaiter;
private OperationContext _operationContext;
public ContinueOnOperationContextAwaiter(Task<TResult> task, bool continueOnCapturedContext = true)
{
if (task == null) throw new ArgumentNullException("task");
_awaiter = task.ConfigureAwait(continueOnCapturedContext).GetAwaiter();
}
public ContinueOnOperationContextAwaiter<TResult> GetAwaiter() { return this; }
public bool IsCompleted { get { return _awaiter.IsCompleted; } }
public void OnCompleted(Action continuation)
{
_operationContext = OperationContext.Current;
_awaiter.OnCompleted(continuation);
}
public TResult GetResult()
{
OperationContext.Current = _operationContext;
return _awaiter.GetResult();
}
}
}
Usage (a little manual and nesting is untested...):
/// <summary>
/// Make a call to the service
/// </summary>
/// <param name="action"></param>
/// <param name="endpoint"> </param>
public async Task<ResultCallWrapper<TResult>> CallAsync<TResult>(Func<T, Task<TResult>> action, EndpointAddress endpoint)
{
using (ChannelLifetime<T> channelLifetime = new ChannelLifetime<T>(ConstructChannel(endpoint)))
{
// OperationContextScope doesn't work with async/await
var oldContext = OperationContext.Current;
OperationContext.Current = new OperationContext((IContextChannel)channelLifetime.Channel);
var result = await action(channelLifetime.Channel)
.WithOperationContext(configureAwait: false);
HttpResponseMessageProperty incomingMessageProperty = (HttpResponseMessageProperty)OperationContext.Current.IncomingMessageProperties[HttpResponseMessageProperty.Name];
string[] keys = incomingMessageProperty.Headers.AllKeys;
var headersOrig = keys.ToDictionary(t => t, t => incomingMessageProperty.Headers[t]);
OperationContext.Current = oldContext;
return new ResultCallWrapper<TResult>(result, new ReadOnlyDictionary<string, string>(headersOrig));
}
}
Async flow is supported from .Net 4.6.2.
We have an ASP.Net WebApi application running on .Net 4.6 where we used the accepted answer. TaskScheduler.FromCurrentSynchronizationContext() caused deadlock issues when the current synchronization context is AspNetSynchronizationContext.
I believe the continuation task was queued after the actual task, causing the actual task is waiting on the continuation whereas the continuation task must run to complete the actual task. i.e. tasks are both waiting on each other.
So I fixed the issue by changing using continuation task to use TaskAwaiter. See: https://blogs.msdn.microsoft.com/lucian/2012/12/11/how-to-write-a-custom-awaiter/
It's been a while on this one, but I'll chime in with my own home-baked solution.
If one doesn't mind doing without OperationContextScope, one might consider something along these lines:
Extension methods
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Security;
using System.Text;
using System.Threading.Tasks;
namespace Intexx.ServiceModel
{
public static class WcfExtensions
{
[DebuggerStepThrough]
public static void Call<TChannel>(this TChannel Client, Action<TChannel> Method) where TChannel : ICommunicationObject
{
try
{
Method.Invoke(Client);
}
finally
{
Cleanup(Client);
}
}
[DebuggerStepThrough]
public static TResult Call<TChannel, TResult>(this TChannel Client, Func<TChannel, TResult> Method) where TChannel : ICommunicationObject
{
try
{
return Method.Invoke(Client);
}
finally
{
Cleanup(Client);
}
}
[DebuggerStepThrough]
public async static Task CallAsync<TChannel>(this TChannel Client, Func<TChannel, Task> Method) where TChannel : ICommunicationObject
{
try
{
await Method.Invoke(Client);
}
finally
{
Cleanup(Client);
}
}
[DebuggerStepThrough]
public async static Task<TResult> CallAsync<TChannel, TResult>(this TChannel Client, Func<TChannel, Task<TResult>> Method) where TChannel : ICommunicationObject
{
try
{
return await Method.Invoke(Client);
}
finally
{
Cleanup(Client);
}
}
private static void Cleanup<TChannel>(TChannel Client) where TChannel : ICommunicationObject
{
try
{
if (Client.IsNotNull)
{
if (Client.State == CommunicationState.Faulted)
Client.Abort();
else
Client.Close();
}
}
catch (Exception ex)
{
Client.Abort();
if (!ex is CommunicationException && !ex is TimeoutException)
throw new Exception(ex.Message, ex);
}
finally
{
Client = null;
}
}
}
}
Client class
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Security;
using System.Text;
using System.Threading.Tasks;
namespace Reader
{
public class Client
{
public static CemReaderClient Create()
{
Tuple<Channels.Binding, EndpointAddress, double> oService;
try
{
oService = Main.Services(typeof(ICemReader));
return new CemReaderClient(oService.Item1, oService.Item2);
}
catch (KeyNotFoundException ex)
{
return null;
}
}
}
}
Usage (in VB, as the code wouldn't convert)
Using oReader As Reader.CemReaderClient = Reader.Client.Create
If oReader.IsNotNothing Then
Dim lIsReading = Await oReader.CallAsync(Function(Reader As Reader.CemReaderClient)
Me.ConfigFilePath = If(Me.ConfigFilePath, Reader.GetConfigFilePath)
Me.BackupDrive = If(Me.BackupDrive, Reader.GetBackupDrive)
Me.SerialPort = If(Me.SerialPort, Reader.GetSerialPort)
Me.LogFolder = If(Me.LogFolder, Reader.GetLogFolder)
Return Reader.GetIsReadingAsync
End Function)
End If
End Using
I've had this running reliably in production under frequency loads of around 15 calls/sec on the client side (that's as fast as serial processing would allow). That was on a single thread, though—this hasn't been rigorously tested for thread safety. YMMV.
In my case, I decided to roll the extension methods into their own private NuGet package. The whole construct has turned out to be pretty handy.
This will have to be reevaluated, of course, if OperationContextScope ever ends up being needed.
The bit with the Tuple in the Client class is for Service Discovery support. If anyone would like to see that code as well, give a shout and I'll update my answer.
I am a little bit confused, I found this Blog : Task-based asynchronous operation in WCF
There this is a async wcf communication:
[ServiceContract]
public interface IMessage
{
[OperationContract]
Task<string> GetMessages(string msg);
}
public class MessageService : IMessage
{
async Task<string> IMessage.GetMessages(string msg)
{
var task = Task.Factory.StartNew(() =>
{
Thread.Sleep(10000);
return "Return from Server : " + msg;
});
return await task.ConfigureAwait(false);
}
}
Client:
var client = new Proxy("BasicHttpBinding_IMessage");
var task = Task.Factory.StartNew(() => client.GetMessages("Hello"));
var str = await task;
So is this also a good way??
I ran into the same issue, however it dawned on me that I didn't need to use async/await at all.
Since you are not post processing the result, there is no need to wait for the reply. If you do need to process the result, just use the old fashion TPL continuation.
public Task<MyDomainModel> GetHomeInfoAsync(DateTime timestamp)
{
using (var helper = new ServiceHelper<ServiceClient, ServiceContract>())
{
return helper.Proxy.GetHomeInfoAsync(timestamp).ContinueWith(antecedent=>processReplay(antecedent.Result));
}
}
I don't know if this helps, but after seeing this question on my search to answer the same question, I came upon this.
Leading from that, I should think your code should look something like this:
public async Task<HomeInfo> GetHomeInfoAsync(DateTime timestamp)
{
using (var client = CreateDocumentServiceClient())
{
await client.BeginGetHomeInfoAsync(timestamp);
}
}
I realise my answer comes rather late :P but it might help someone else.