Apart from .NET 4.5.1 there is a new option on the TransactionScope which enables to use async flow. This allows to write the following client code
using(var txt = new TransactionScope(..., TransactionScopeAsyncFlowOption.Enabled)
{
await sender.SendAsync();
}
So far so good. But when I need to implement a volatile IEnlistmentNotification I'm struggling to do that. Let's imagine the following scenario, assumption: My underlying infrastructure is completely async from bottom to top
public class MessageSender : ISendMessages
{
public async Task SendAsync(TransportMessage message, SendOptions options)
{
await sender.SendAsync(message);
}
}
So what I want to achieve is to introduce a volatile IEnlistmentNotification like this:
internal class SendResourceManager : IEnlistmentNotification
{
private readonly Func<Task> onCommit;
public SendResourceManager(Func<Task> onCommit)
{
this.onCommit = onCommit;
}
public void Prepare(PreparingEnlistment preparingEnlistment)
{
preparingEnlistment.Prepared();
}
public void Commit(Enlistment enlistment)
{
await this.onCommit();
enlistment.Done();
}
public void Rollback(Enlistment enlistment)
{
enlistment.Done();
}
public void InDoubt(Enlistment enlistment)
{
enlistment.Done();
}
}
and the new sender
public class MessageSender : ISendMessages
{
public async Task SendAsync(TransportMessage message, SendOptions options)
{
// Dirty: Let's assume Transaction.Current is never null
Transaction.Current.EnlistVolatile(new SendResourceManager(async () => { await sender.SendAsync(message) }));
}
}
Note: Of course this code doesn't compile. It would require me to declare the commit method async void. Which is aweful.
So my question is: How can I write an enlistment which can internally await an asynchronous operation?
As long as EnlistVolatile isn't a heavy CPU bound time consuming operation, you can create a thin Task based wrapper over EnlistVolatile using Task.FromResult:
public static class TranscationExtensions
{
public static Task EnlistVolatileAsync(this Transaction transaction,
IEnlistmentNotification
enlistmentNotification,
EnlistmentOptions enlistmentOptions)
{
return Task.FromResult(transaction.EnlistVolatile
(enlistmentNotification,
enlistmentOptions));
}
}
and then consume it inside your method:
public class MessageSender : ISendMessages
{
public Task SendAsync(TransportMessage message, SendOptions options)
{
return Transaction.Current.EnlistVolatileAsync
(new SendResourceManager(async () =>
{ await sender.SendAsync(message) }));
}
}
which can be awaited higher in your callstack:
MessageSender sender = new MessageSender();
await sender.SendAsync(message, options);
Related
I create the BackgroundService:
public class CustomService : BackgroundService
{
protected override async Task ExecuteAsync(CancellationToken cancellationToken)
{
//...
}
}
and I added to the project:
public class Startup
{
//...
public void ConfigureServices(IServiceCollection services)
{
services.AddHostedService<CustomService>();
//...
}
//...
}
How can I find the CustomService from another class?
How to start it again?
Create an interface just for the call to StartAsync:
public interface ICustomServiceStarter
{
Task StartAsync(CancellationToken token = default);
}
public class CustomService : BackgroundService, ICustomServiceStarter
{
//...
Task ICustomServiceStarter.StartAsync(CancellationToken token = default) => base.StartAsync(token);
//...
}
Register the interface as a singleton:
public class Startup
{
//...
public void ConfigureServices(IServiceCollection services)
{
//...
services.AddSingleton<ICustomServiceStarter, CustomService>();
}
//...
}
and inject ICustomServiceStarter when needed:
public class MyServiceControllerr : Controller
{
ICustomServiceStarter _starter;
public MyServiceController(ICustomServiceStarter starter)
{
_starter = starter;
}
[HttpPost]
public async Task<IActionResult> Start()
{
await _starter.StartAsync();
return Ok();
}
}
When it comes to controller's action, using "await BackgroundService.StartAsync" is the wrong way for long-running tasks.
For instance, the main ussue could be request's timeout depended on proxy settings.
Here is an example how to make your BackgroundService restartable.
BackgroundService implementation:
public class MyBackgroundService: BackgroundService
{
private volatile bool _isFinished = false;
private SemaphoreSlim _semaphore = new SemaphoreSlim(0,1);
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
_isFinished = false;
// DoSomeWork
_isFinished = true;
await WaitToRestartTask(stoppingToken);
}
private async Task WaitToRestartTask(CancellationToken stoppingToken)
{
// wait until _semaphore.Release()
await _semaphore.WaitAsync(stoppingToken);
// run again
await base.StartAsync(stoppingToken);
}
public void RestartTask()
{
if (!_isFinished)
throw new InvalidOperationException("Background service is still working");
// enter from _semaphore.WaitAsync
_semaphore.Release();
}
}
Controller's action (for instance):
public async Task<IActionResult> Restart()
{
var myBackgroundService= _serviceProvider.GetServices<IHostedService>()
.OfType<MyBackgroundService>()
.First();
try
{
myBackgroundService.RestartTask();
return Ok($"MyBackgroundService was restarted");
}
catch (InvalidOperationException exception)
{
return BadRequest(exception.Message);
}
}
I have implemented a REST API Server with asp.NET Core. I need a background task which does some administrative stuff.
I tried the several suggestions in the web to create a timed background task and implemented it as fpllowing:
BackgroundService.cs
public abstract class BackgroundService : IHostedService, IDisposable
{
private Task _executingTask;
private readonly CancellationTokenSource _stoppingCts = new CancellationTokenSource();
protected abstract Task ExecuteAsync(CancellationToken stoppingToken);
public virtual Task StartAsync(CancellationToken cancellationToken)
{
_executingTask = ExecuteAsync(_stoppingCts.Token);
if (_executingTask.IsCompleted)
{
return _executingTask;
}
return Task.CompletedTask;
}
public virtual async Task StopAsync(CancellationToken cancellationToken)
{
if (_executingTask == null)
{
return;
}
try
{
_stoppingCts.Cancel();
}
finally
{
await Task.WhenAny(_executingTask, Task.Delay(Timeout.Infinite, cancellationToken));
}
}
public virtual void Dispose()
{
_stoppingCts.Cancel();
}
}
RecureHostedService.cs
public class RecureHostedService : BackgroundService
{
private static readonly ILog log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private int _executionCount = 0;
private Timer _timer;
protected async override Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
var count = Interlocked.Increment(ref _executionCount);
log.InfoFormat(
"Timed Hosted Service is working. Count: {0}", count);
await Task.Delay(10000, stoppingToken);
}
}
}
SystemController.cs
public class SystemController : Controller
{
private readonly RecureHostedService _recureHostedService;
public SystemController(IHostedService hostedService)
{
_recureHostedService = hostedService as RecureHostedService;
}
[HttpGet(ApiRoutes.System.Start)]
public IActionResult Start()
{
Console.WriteLine("Start Service");
_recureHostedService.StartAsync(new CancellationToken());
return Ok();
}
[HttpGet(ApiRoutes.System.Stop)]
public IActionResult Stop()
{
Console.WriteLine("Stop Service");
Console.WriteLine(_recureHostedService == null);
_recureHostedService.StopAsync(new CancellationToken());
return Ok();
}
}
in Startup.cs I added the Service as Singleton:
services.AddSingleton<IHostedService,RecureHostedService>();
The RecureHostedService starts on startup of my app and does its work. When I do an APIrequest /api/v1/stop from the SystemController (method Stop()) I got an NullReferenceException in
_recureHostedService.StopAsync(new CancellationToken());
I found out that the private member variable _recureHostedService is null even if I had assigned it in the Constructor of the SystemController. So it seems that the Service Injection of _recureHostedService is not working. So the line
_recureHostedService = hostedService as RecureHostedService;
in the Constructor of SystemController assigns null to _recureHostedService.
How can that be? I've a lot of other Services injected to other Controllers and all works fine.
Any idea from anyone, why it is not working with that Hosted Service?
Best
Mic
I used a set of static variables for myself
public class RecureHostedService : BackgroundService {
public static bool isPause = false;
/* for example
public static string ErrorText;
public static bool isError = false;
public static bool isWorked = false;
public static bool firstStart = true; */
protected async override Task ExecuteAsync(CancellationToken stoppingToken){
while (!stoppingToken.IsCancellationRequested)
{
await Task.Delay(TimeSpan.FromSeconds(1), stoppingToken);
if (isPause)
continue;
var count = Interlocked.Increment(ref _executionCount);
log.InfoFormat(
"Timed Hosted Service is working. Count: {0}", count);
await Task.Delay(10000, stoppingToken);
}
}
}
public class SystemController : Controller
{
[HttpGet(ApiRoutes.System.Start)]
public IActionResult Start()
{
Console.WriteLine("Start Service");
RecureHostedService.isPause = false;
return Ok();
}
[HttpGet(ApiRoutes.System.Stop)]
public IActionResult Stop()
{
Console.WriteLine("Stop Service");
RecureHostedService.isPause = true;
return Ok();
}
}
You need to register you hosted service using AddHostedService instedad of AddSingleton
so it will be something like this
services.AddHostedService<IHostedService,RecureHostedService>();
If you perfer inject IHostedService on constructor, keep _recureHostedService property declared as type of IHostedService, instead of the concrete class RecureHostedService. It's not necessary down-casting here.
[Route("[controller]")]
public class SystemController : Controller
{
private readonly IHostedService _recureHostedService;
public SystemController(IHostedService hostedService)
{
_recureHostedService = hostedService ?? throw new ArgumentNullException(nameof(hostedService));
}
}
Update
I've tested this controller, no null exception any longer in either this way
services.AddSingleton<IHostedService, RecureHostedService>();
or
services.AddHostedService(p =>
{
return new RecureHostedService();
});
Using dependency injection to obtain an IHostedService will inject whichever hosted service was defined first. Which is likely to be the web server itself. Instead you should define two service definitions that return the same singleton;
services.AddSingleton<RecureHostedService>();
services.AddSingleton<IHostedService>(p => p.GetRequiredService<RecureHostedService>());
public class SystemController : Controller
{
private readonly RecureHostedService _recureHostedService;
public SystemController(RecureHostedService hostedService)
{
_recureHostedService = hostedService;
}
}
I have to use an API that mandates a class that implements its callback interface as a parameter to an authentication method.
public class CallBack : ICallBack
{
public object Response;
public void OnSuccess(object response)
{
Response = response;
}
public void OnException(Exception ex) { }
}
Authentication Call
public async Task<bool> LoginAsync(string username, string password)
{
CallBack callback = new CallBack();
await Task.Factory.StartNew(
() => userService.Authenticate(username, password, callback),
TaskCreationOptions.AttachedToParent);
return callback.Response is User ? true : false;
}
The problem is that LoginAsync finishes before the callback is invoked. I hoped that by starting the Authenticate call using TaskCreationOptions.AttachedToParent, it would propagate down to any child tasks started in Authenticate but it does not.
You should use a TaskCompletionSource object to wrap your callback based async method into a awaitable task.
I assume your ICallBack is like this :
public interface ICallBack
{
void OnSuccess(object response);
void OnException(Exception ex);
}
So you can implement LoginAsync like this :
public async Task<bool> LoginAsync(string username, string password)
{
var tcs = new TaskCompletionSource<object>();
ICallBack callback = new CallBackAsync(tcs);
userService.Authenticate(username, password, callback);
var result = await tcs.Task;
return result is User ? true : false;
}
public class CallBackAsync : ICallBack
{
private TaskCompletionSource<object> _tcs;
public CallBackAsync(TaskCompletionSource<object> tcs)
{
_tcs = tcs;
}
public void OnSuccess(object response)
{
_tcs.TrySetResult(response);
}
public void OnException(Exception ex) {
_tcs.TrySetException(ex);
}
}
For the quick explanation, when you use Task.Factory.StartNew(), the completion of the task is raised at the end of the lambda expression. But in your case this occurs before CallBack.OnSuccess call. So the result is not set.
The TaskCompletionSource class allow you to fully control when the task completion must occurs.
It is a good practice to have a simple methods, which returns tasks:
public class MessageService : IMessageService
{
public Task<IEnumerable<Message>> DownloadMessagesTask()
{
return MyWebClient.GetMessages();
}
Now, I'd like to add a caching to the local storage:
public Task<bool> WriteMessagesTask(IEnumerable<Message> list)
{
return MyIsoStorageManager.Write(list);
}
// same for reading
Most naive way is to call them from viewmodel:
public async void Init()
{
var result = await messageService.ReadMessagesTask();
if (result == null)
{
MessagesList = await messageService.DownloadMessagesTask();
var writingResult = await messageService.WriteMessagesTask(MessagesList);
}
But how can I store this logic in a service, just to avoid code repeating in another viewmodels? Or should I keep service clean and call tasks in a viewmodel?
Expose one method from your service that wraps what you now have in you async void Init() and call it from VM. You could also extract interface and inject that in your viewmodel (via constructor or property).
META
public class MyViewModel
{
public MyViewModel()
:this(new Service())
{}
public MyViewModel(IService service)
{
Service = service;
Initialize();
}
public IService Service { get; set; }
private async void Initialize()
{
// Fire forget
await Service.DoSomething();
}
}
META
public interface IService
{
// change if you need to return something
Task DoSomething();
}
public Service : IService
{
public async Task DoSomething()
{
var result = await ReadMessagesAsync();
if (result == null)
{
var messages = await DownloadMessagesAsync();
await WriteMessagesAsync(messages);
}
}
// private read/write/download methods here...
}
Maybe you cold split these methods into some helper classes and use them in viewmodels as you see fit. Or if you already have some base viewmodel class (for INotifyPropertyChanged for example) you could move them there, assuming they are mainly for viewmodels.
Get rid of the void after async. Because it is "fire and forget", you essentially start the method but have no control over when it is completed. In some cases it is ok, but avoid it if you can.
I have a kind of bus that implements this interface:
public interface IBus
{
void Publish<T>(T t);
void Subscribe<T>(Guid subscriptionId, Action<T> action);
void Unsubscribe<T>(Guid subscriptionId);
}
Here is an example on how I use it:
public void PrintName()
{
IBus bus = new Bus();
var id = Guid.NewGuid();
bus.Subscribe<ReplyUserName>(id, replyUserName =>
{
bus.Unsubscribe<ReplyUserName>(id);
Console.WriteLine(replyUserName.UserName);
});
Bus.Publish(new RequestUserName());
}
And here are the RequestUserName and ReplyUserName classes:
public class RequestUserName {}
public class ReplyUserName
{
public string UserName { get; set; }
}
However I would like to write an extension method that would wrap this with async:
public static class BusExtension
{
public static async Task<TResult> Request<TRequest, TResult>(this IBus bus, TRequest request)
{
// TODO...
}
}
So that I will be able to write the previous code in such a way:
public async void PrintName()
{
IBus bus = new Bus();
var replyUserName = await bus.Request<RequestUserName, ReplyUserName>(new RequestUserName());
Console.WriteLine(replyUserName.UserName);
}
what should I write instead of the TODO?
You can use TaskCompletionSource<T> to wrap anything into an await-compatible method.
public static Task<TResult> Request<TRequest, TResult>(this IBus bus, TRequest request)
{
var tcs = new TaskCompletionSource<TResult>();
var id = Guid.NewGuid();
bus.Subscribe<TResult>(id, result =>
{
bus.Unsubscribe<TResult>(id);
tcs.TrySetResult(result);
});
bus.Publish(request);
return tcs.Task;
}
Note, however, that you should ensure that the task is completed. If there's any chance that the bus won't respond to the request, you should have a timer or something that faults the TaskCompletionSource.
You could implement this as follows:
var taskCompletionSource = new TaskCompletionSource<TResult>();
bus.Subscribe<TResult>(id, result =>
{
bus.Unsubscribe<TResult>(id);
taskCompletionSource.SetResult(result);
});
bus.Publish(request);
return taskCompletionSource.Task;
You might also want to check out Reactive Extensions (Rx) as your IBus interface looks similar to the ISubject interface (http://msdn.microsoft.com/en-us/library/hh211669.aspx). The Reactive Extensions library already provides convenient extension methods similar to the one you are attempting to implement.