I want to register consumer by interface, send message, initialize it by interface from container, then consume:
public sealed class TestConsumer<T> : IConsumer<T>
where T : class
{
private readonly Func<ConsumeContext<T>, Task> _onConsume;
private readonly EventWaitHandle _handle;
public TestConsumer(Func<ConsumeContext<T>, Task> onConsume)
{
_onConsume = onConsume;
_handle = new EventWaitHandle(false, EventResetMode.ManualReset);
}
public async Task Consume(ConsumeContext<T> context)
{
try
{
await _onConsume(context).ConfigureAwait(false);
}
finally
{
_handle.Set();
}
}
public async Task GetTask()
{
while (!_handle.WaitOne(0))
await Task.Delay(100);
}
}
public class MyRequest { }
[TestFixture]
public class ConsumerTests
{
[Test]
public async Task Test()
{
var services = new ServiceCollection();
var tc = new TestConsumer<MyRequest>(async (c) => Console.WriteLine("request"));
services.AddSingleton<IConsumer<MyRequest>>(tc);
services.AddSingleton<IBusControl>(x => Bus.Factory.CreateUsingInMemory(cfg =>
{
cfg.ReceiveEndpoint("foobar", c => { c.Consumer<IConsumer<MyRequest>>(x); });
}));
var sp = services.BuildServiceProvider();
await sp.GetRequiredService<IBusControl>().StartAsync();
//and how do I send it?
//this will obviously not work with Uri!!!
var sendEndpoint = await sp.GetRequiredService<IBusControl>().GetSendEndpoint(new Uri("foobar", UriKind.Relative));
await sendEndpoint.Send(new MyRequest());
await tc.GetTask();
Console.WriteLine("done");
}
}
Honestly, lack of documentation is driving me crazy. There is such thing as harness, but it works only if you throw your DI container into garbage can or write a ton of adapters.
How do one can use InMemory and combine it to completely uncompatible Uri in Send method?
Related
I have a WebAPI server with integrated SignalR Hubs.
The problem it is in the integration between both components and efficiently call the clients interested on a given item that was updated through REST with the least overhead possible on the Controller side.
I have read about Background tasks with hosted services in ASP.NET Core, or Publish Subscriber Patterns but they don't seem the right fit for this problem.
From the documentation examples, the background tasks seem to atempt to preserve order which is not required, in fact, it is desired to allow multiple requests to be handled concurrently, as efficiently as possible.
With this in mind, I created this third component called MappingComponent that is being called through a new Task.
It is important to design the Controller in a way that he spends the least amount of work "raising the events" possible. Exceptions should be (i believe) handled within the MappingComponent.
What would be a better approach/design pattern that the following implementation, to avoid using Task.Run?
ApiController
[Route("api/[controller]")]
[ApiController]
public class ItemController : ControllerBase
{
private readonly MappingComponent mappingComponent;
private readonly IDataContext dataContext;
[HttpPost]
public async Task<ActionResult<Item>> PostItem(ItemDTO itemDTO)
{
await dataContext.Items.AddAsync(item);
(...)
_ = Task.Run(async () =>
{
try
{
await mappingComponent.NotifyOnItemAdd(item);
}
catch (Exception e)
{
Console.WriteLine(e);
}
});
return CreatedAtAction("Get", new { id = item.Id }, item);
}
[HttpDelete("{id}", Name = "Delete")]
public async Task<IActionResult> Delete(int id)
{
var item = await dataContext.Items.FindAsync(id);
(...)
_ = Task.Run(async () =>
{
try
{
await mappingComponent.NotifyOnItemDelete(item);
}
catch (Exception e)
{
Console.WriteLine(e);
}
});
return NoContent();
}
[HttpPatch("{id}", Name = "Patch")]
public async Task<IActionResult> Patch(int id,
[FromBody] JsonPatchDocument<Item> itemToPatch)
{
var item = await dataContext.Items.FindAsync(id);
(...)
_ = Task.Run(async () =>
{
try
{
await mappingComponent.NotifyOnItemEdit(item);
}
catch (Exception e)
{
Console.WriteLine(e);
}
});
return StatusCode(StatusCodes.Status202Accepted);
}
}
SignalR Hub
public class BroadcastHub : Hub<IHubClient>
{
private readonly MappingComponent mappingComponent;
public BroadcastHub(MappingComponent mappingComponent)
{
this.mappingComponent = mappingComponent;
}
public override Task OnConnectedAsync()
{
mappingComponent.OnConnected(Context.User.Identity.Name, Context.ConnectionId));
return base.OnConnectedAsync();
}
public override Task OnDisconnectedAsync()
{
mappingComponent.OnDisconnected(Context.User.Identity.Name, Context.ConnectionId));
return base.OnDisconnectedAsync();
}
public void Subscribe(string itemQuery)
{
mappingComponent.SubscribeConnection(Context.User.Identity.Name, Context.ConnectionId, itemQuery));
}
public void Unsubscribe()
{
mappingComponent.UnsubscribeConnection(Context.ConnectionId));
}
}
"MappingComponent" being registered as singleton on startup
public class MappingComponent
{
private readonly IServiceScopeFactory scopeFactory;
private readonly IHubContext<BroadcastHub, IHubClient> _hubContext;
private static readonly ConcurrentDictionary<string, User> Users = new(StringComparer.InvariantCultureIgnoreCase);
private static readonly ConcurrentDictionary<int, List<string>> ItemConnection = new();
private static readonly ConcurrentDictionary<string, List<int>> ConnectionItem = new();
public MappingComponent(IServiceScopeFactory scopeFactory, IHubContext<BroadcastHub, IHubClient> hubContext)
{
//this.dataContext = dataContext;
this._hubContext = hubContext;
this.scopeFactory = scopeFactory;
}
internal void OnConnected(string userName, string connectionId){(...)}
internal void OnDisconnected(string userName, string connectionId){(...)}
internal void SubscribeConnection(string userName, string connectionId, string query){(...)}
internal void UnsubscribeConnection(string connectionId){(...)}
internal async Task NotifyOnItemAdd(Item item)
{
List<string> interestedConnections = new();
(...)
//Example containing locks
lock()
{
//There is a need to acess EF database
using (var scope = scopeFactory.CreateScope())
{
var dataContext = scope.ServiceProvider.GetRequiredService<IDataContext>()
await dataContext.Items.(...)
interestedConnections = ...
}
}
await _hubContext.Clients.Clients(interestedConnections).BroadcastItem(item);
}
internal async Task NotifyOnItemEdit(Item item)
{
List<string> interestedConnections = new();
(...)
await _hubContext.Clients.Clients(interestedConnections).BroadcastItem(item);
}
internal async Task NotifyOnItemDelete(Item item)
{
List<string> interestedConnections = new();
(...)
await _hubContext.Clients.Clients(interestedConnections).BroadcastAllItems();
}
}
I saw the implementation of Stephen Cleary's Disposables NuGet package and it seems like it's perfect in my case, even tho, I couldn't find examples on how to inherit from it.
My idea is to make UnsubscribeAsync().GetAwaiter().GetResult(); to await UnsubscribeAsync();, which means it should be wrapped into an IAsyncDisposable. How could I achieve that with a sealed class?
public sealed class LiveTradeManager : ITradeManager, IDisposable
{
private bool _disposed;
private readonly ILogger<LiveTradeManager> _logger;
private readonly TradeOptions _tradeOptions;
private readonly IBotClient _client;
private string _listenKey;
private UpdateSubscription _candleSubscription, _accountUpdateSubscription;
private IDictionary<string, Channel<IBinanceStreamKlineData>> _channels;
public LiveTradeManager(ILogger<LiveTradeManager> logger, IOptions<TradeOptions> tradeOptions, IOptions<ExchangeOptions> exchangeOptions, IBotClientFactory clientFactory)
{
_logger = logger;
_tradeOptions = tradeOptions.Value;
_client = clientFactory.GetBotClient(exchangeOptions.Value.BotClientType);
}
public bool IsPaused { get; set; }
public async Task RunAsync(CancellationToken cancellationToken)
{
try
{
await SubscribeAsync(cancellationToken).ConfigureAwait(false);
}
catch (TaskCanceledException)
{
}
catch (Exception ex) when (Handle(() => _logger.LogError(ex, "Unexpected error.")))
{
}
}
private async Task SubscribeAsync(CancellationToken cancellationToken)
{
// Subscribe to account updates
_listenKey = await _client.GetListenKeyAsync(cancellationToken).ConfigureAwait(false);
void OnOrderUpdate(BinanceStreamOrderUpdate order)
{
// order update logic
}
_accountUpdateSubscription = await _client.SubscribeToUserDataUpdatesAsync(_listenKey, OnOrderUpdate).ConfigureAwait(false);
_ = Task.Run(async () =>
{
while (!cancellationToken.IsCancellationRequested)
{
await _client.KeepAliveListenKeyAsync(_listenKey, cancellationToken).ConfigureAwait(false);
await Task.Delay(TimeSpan.FromMinutes(30), cancellationToken).ConfigureAwait(false);
}
}, cancellationToken);
// Subscribe to candle updates
var symbols = _tradeOptions.Symbols.Select(x => x.ToString()).ToList();
_channels = symbols.ToDictionary(x => x, _ =>
Channel.CreateBounded<IBinanceStreamKlineData>(new BoundedChannelOptions(1)
{FullMode = BoundedChannelFullMode.DropOldest}));
async void OnCandleReceived(IBinanceStreamKlineData data)
{
if (IsPaused) return;
try
{
var ohlcv = data.Data.ToCandle();
if (data.Data.Final)
{
_logger.LogInformation(
$"[{data.Symbol}] Finalized candle | Open time: {ohlcv.Timestamp.ToDateTimeFormat()} | Price: {ohlcv.Close}");
_ = Task.Run(async () =>
{
await Task.Delay(10000, cancellationToken).ConfigureAwait(false);
}, cancellationToken);
}
else
{
_logger.LogInformation(
$"[{data.Symbol}] Candle update | Open time: {ohlcv.Timestamp.ToDateTimeFormat()} | Price: {ohlcv.Close}");
await _channels[data.Symbol].Writer.WriteAsync(data, cancellationToken).ConfigureAwait(false);
}
}
catch (TaskCanceledException)
{
}
}
_candleSubscription = await _client
.SubscribeToCandleUpdatesAsync(symbols, KlineInterval.OneMinute, OnCandleReceived)
.ConfigureAwait(false);
var tasks = _channels.Values.Select(async channel =>
{
await foreach (var data in channel.Reader.ReadAllAsync(cancellationToken))
{
// long-running logic...
await Task.Delay(10000, cancellationToken).ConfigureAwait(false);
}
});
// NOTE: this would block further logic
await Task.WhenAll(tasks).ConfigureAwait(false);
}
private async Task UnsubscribeAsync()
{
// Unsubscribe account updates
if (!string.IsNullOrEmpty(_listenKey))
{
await _client.StopListenKeyAsync(_listenKey).ConfigureAwait(false);
}
if (_accountUpdateSubscription != null)
{
await _client.UnsubscribeAsync(_accountUpdateSubscription).ConfigureAwait(false);
}
// Unsubscribe candle updates
if (_candleSubscription != null)
{
await _client.UnsubscribeAsync(_candleSubscription).ConfigureAwait(false);
}
// Channels
if (_channels != null)
{
foreach (var channel in _channels.Values)
{
channel.Writer.Complete();
}
_channels.Clear();
}
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private void Dispose(bool disposing)
{
if (_disposed) return;
if (disposing)
{
UnsubscribeAsync().GetAwaiter().GetResult();
}
_disposed = true;
}
}
public class BotManagerService : BackgroundService
{
private readonly IHostApplicationLifetime _hostApplicationLifetime;
private readonly IDiscordClient _discordClient;
private readonly ITradeManager _tradeManager;
public BotManagerService(
IHostApplicationLifetime hostApplicationLifetime,
IOptions<ExchangeOptions> options,
IDiscordClient discordClient,
ITradeManagerFactory tradeManagerFactory)
{
_hostApplicationLifetime = hostApplicationLifetime;
_discordClient = discordClient;
_tradeManager = tradeManagerFactory.GetTradeManager(options.Value.TradeManagerType);
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
try
{
var task1 = _tradeManager.RunAsync(stoppingToken);
var task2 = _discordClient.StartAsync();
await Task.WhenAll(task1, task2).ConfigureAwait(false);
}
finally
{
_hostApplicationLifetime.StopApplication();
}
}
}
I couldn't find examples on how to inherit from it.
Nito.Disposables, like the vast majority of the code I write, is written for composition rather than inheritance.
So, if you have a type that needs to implement IAsyncDisposable, it should contain an IAsyncDisposable implementation and forward its interface methods to that contained object:
public sealed class LiveTradeManager : ITradeManager, IAsyncDisposable
{
private readonly AsyncDisposable _disposable;
...
public LiveTradeManager(...)
{
...
_disposable = new(async () => await UnsubscribeAsync());
}
public ValueTask DisposeAsync() => _disposable.DisposeAsync();
}
In my .Net Core 3.0 app I want to use the Microsoft Graph Nuget library. I have created a connection class that authenticates my application using [MSAL][1] and then creates the connection and returns this. My idea was to inject this connection object in the constructor using Dependency Injection. However, since the method that creates the connection is async, I seem to have a problem how to use it in the constructor.
My Connect Class
public class AuthorizeGraphApi: IAuthorizeGraphApi
{
private readonly IConfiguration _config;
public AuthorizeGraphApi(IConfiguration config)
{
_config = config;
}
public async Task<GraphServiceClient> ConnectToAAD()
{
string accessToken = await GetAccessTokenFromAuthorityAsync();
var graphServiceClient = new GraphServiceClient(new DelegateAuthenticationProvider((requestMessage) => {
requestMessage
.Headers
.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
return Task.FromResult(0);
}));
return graphServiceClient;
}
private async Task<string> GetAccessTokenFromAuthorityAsync()
{
// clientid, authUri, etc removed for this example.
IConfidentialClientApplication _conn;
_conn = ConfidentialClientApplicationBuilder.Create(clientId)
.WithClientSecret(clientSecret)
.WithAuthority(new Uri(authUri))
.Build();
string[] scopes = new string[] { $"api://{clientId}/.default" };
AuthenticationResult result = null;
// AcquireTokenForClient only has async method.
result = await _conn.AcquireTokenForClient(scopes)
.ExecuteAsync();
return result.AccessToken;
}
}
My Graph Service to send requests
public class AzureIntuneService
{
private readonly IAuthorizeGraphApi _graphClient;
public AzureIntuneService(IAuthorizeGraphApi client)
{
//Gives: cannot implicitely convert to Threading.Tasks.Task.... error
_graphClient = client.ConnectToAAD();
}
public async Task<IList<string>> GetAADInformationAsync()
{
// then here, use the graphClient object for the request...
var payload = await _graphClient.Groups.Request().GetAsync();
return payload
}
}
I register the above classess in my startup as follows:
services.AddScoped<IAuthorizeGraphApi, AuthorizeGraphApi>();
The idea was that this way, I don't need to call the _graphClient in each method. How can I inject the connection object in a correct way? Or what are the best practices regarding this (injecting connection objects)?
One way would be to store a reference to the Task and make sure any public methods that use the connection are async:
public class AzureIntuneService
{
private readonly Task<GraphServiceClient> _graphClientTask;
public AzureIntuneService(IAuthorizeGraphApi client)
{
_graphClientTask = client.ConnectToAAD();
}
public async Task<IList<string>> GetAADInformationAsync()
{
var client = await _graphClientTask; // Get the client when connected
var payload = await client.Groups.Request().GetAsync();
return payload;
}
}
Constructors aren't async and should never be used to initialize anything async. The only way to workaround it is to do sync-over-async by doing a .Result which is always a problem.
In your case, the GraphServiceClient that takes in DelegateAuthenticationProvider, accepts an AuthenticateRequestAsyncDelegate. This allows you to have an async delegate to construct the client.
So now you can do
new DelegateAuthenticationProvider(async requestMessage =>
{
string accessToken = await GetAccessTokenFromAuthorityAsync();
//rest of code here
}
)
and this allows you to change your ConnectToAAD signature to just return a GraphServiceClient and not a Task<GraphServiceClient>.
When you need async data you have to look away from the regular constructor and create a factory method (private static function). Something like below:
public sealed class MyClass
{
private MyData asyncData;
private MyClass() { ... }
private async Task<MyClass> InitializeAsync()
{
asyncData = await GetDataAsync();
return this;
}
public static Task<MyClass> CreateAsync()
{
var ret = new MyClass();
return ret.InitializeAsync();
}
}
public static async Task UseMyClassAsync()
{
MyClass instance = await MyClass.CreateAsync();
...
}
More here: https://blog.stephencleary.com/2013/01/async-oop-2-constructors.html
I'm trying to create trigger and start job using quartz.net 3.0 in my net core app.
Here is a code :
mycontroller.cs
public async Task<IActionResult> CreateHomeWorkReminder([FromBody] CreateHomeWorkReminderRequest request)
{
if (!ModelState.IsValid)
{
return BadRequest();
}
_logger.LogInformation("data valid");
var dict = new Dictionary<string, object> { {nameof(CreateHomeWorkReminderRequest.UserId), request.UserId },
{nameof(CreateHomeWorkReminderRequest.HomeWorkId), request.HomeWorkId}};
await _registerJob.CreateJob<ReminderJob>(nameof(CreateHomeWorkReminder), dict, request.DateOfReminder);
_logger.LogInformation("saving ok");
return Ok(result.Result);
}
RegisterJob.cs (DI in contructor in mycontroller.cs) :
public class RegisterJob : IRegisterJob
{
ISchedulerBot bot;
public async Task CreateJob<T>(string jobName, IDictionary<string, object> map, DateTime dateOfReminder) where T : IJob
{
NameValueCollection props = new NameValueCollection
{
{ "quartz.serializer.type", "binary" }
};
StdSchedulerFactory factory = new StdSchedulerFactory(props);
// get a scheduler
IScheduler sched = await factory.GetScheduler();
await sched.Start();
var job = JobBuilder.Create<ReminderJob>().Build();
var trigger = TriggerBuilder.Create().StartNow().Build();
await sched.ScheduleJob(job, trigger);
}
}
}
and ReminderJob.cs
public class ReminderJob : IJob
{
ILogger<ReminderJob> logger;
public ReminderJob(ILogger<ReminderJob> _logger)
{
logger = _logger;
}
public Task Execute(IJobExecutionContext context)
{
var parameters = context.JobDetail.JobDataMap;
var userId = parameters.GetLongValue("UserId");
var homeWorkId = parameters.GetLongValue("HomeWorkId");
logger.LogInformation($"userId = {userId}, homeWorkId = {homeWorkId}");
return Task.FromResult(0);
}
}
I'm trying to debug my code, but never go to the Execute method of ReminderJob.cs. Where is my mistake? What should I to execute job? Thank you for your attention.
Right answer : it's impossible to use DI with Quartz 3.0. I deleted constructor in ReminderJob with DI of ILogger and code worked.
public class ReminderJob : IJob
{
public async Task Execute(IJobExecutionContext context)
{
var parameters = context.JobDetail.JobDataMap;
var userId = parameters.GetLongValue("UserId");
var homeWorkId = parameters.GetLongValue("HomeWorkId");
await System.Console.Out.WriteLineAsync("HelloJob is executing.");
}
This is a followup question to the following question:
Volatile IEnlistmentNotification and TransactionScope.AsyncFlowEnabled = true
The approach accepted in the question above works as long as you don't await multiple statements. Let me show an example:
public class SendResourceManager : IEnlistmentNotification
{
private readonly Action onCommit;
public SendResourceManager(Action onCommit)
{
this.onCommit = onCommit;
}
public void Prepare(PreparingEnlistment preparingEnlistment)
{
preparingEnlistment.Prepared();
}
public void Commit(Enlistment enlistment)
{
Debug.WriteLine("Committing");
this.onCommit();
Debug.WriteLine("Committed");
enlistment.Done();
}
public void Rollback(Enlistment enlistment)
{
enlistment.Done();
}
public void InDoubt(Enlistment enlistment)
{
enlistment.Done();
}
}
public class AsyncTransactionalMessageSender : ISendMessagesAsync
{
private readonly List<Message> sentMessages = new List<Message>();
public IReadOnlyCollection<Message> SentMessages
{
get { return new ReadOnlyCollection<Message>(this.sentMessages); }
}
public async Task SendAsync(Message message)
{
if (Transaction.Current != null)
{
await Transaction.Current.EnlistVolatileAsync(
new SendResourceManager(async () => await this.SendInternal(message)),
EnlistmentOptions.None);
}
else
{
await this.SendInternal(message);
}
}
private async Task SendInternal(Message message)
{
Debug.WriteLine("Sending");
await Task.Delay(1000);
this.sentMessages.Add(message);
Debug.WriteLine("Sent");
}
}
[Test]
public async Task ScopeRollbackAsync_DoesntSend()
{
var sender = new AsyncTransactionalMessageSender();
using (var tx = new System.Transactions.TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
{
await sender.SendAsync(new Message("First"));
await sender.SendAsync(new Message("Second"));
await sender.SendAsync(new Message("Last"));
// We do not commit the scope
}
sender.SentMessages.Should().BeEmpty();
}
[Test]
public async Task ScopeCompleteAsync_Sends()
{
var sender = new AsyncTransactionalMessageSender();
using (var tx = new System.Transactions.TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
{
await sender.SendAsync(new Message("First"));
await sender.SendAsync(new Message("Second"));
await sender.SendAsync(new Message("Last"));
tx.Complete();
}
sender.SentMessages.Should().HaveCount(3)
.And.Contain(m => m.Value == "First")
.And.Contain(m => m.Value == "Second")
.And.Contain(m => m.Value == "Last");
}
As soon as you introduce a Task.Delay like shown in the example above the generated asynchronous statemachine will never come back and invoke the this.sentMessages.Add(message) and Debug.WriteLine("Sent")
The problem is I currently see now way to properly enlist asynchronous code inside the enlistment notification. Any ideas how to tackle this challenge?