cannot convert from 'method group' to 'TimerCallback' [duplicate] - c#

This question already has answers here:
The best overloaded method match for System.Threading.Timer.Timer() has some invalid arguments
(3 answers)
Closed 3 years ago.
I'm trying to run function getOrg though hosted services but some how its not working I'm not sure what I'm doing wrong.
Error:
Argument 1: cannot convert from 'method group' to 'TimerCallback'
(CS1503)
public class TokenService : IHostedService
{
public IConfiguration _Configuration { get; }
protected IMemoryCache _cache;
private Timer _timer;
public IHttpClientFactory _clientFactory;
private readonly IServiceScopeFactory _scopeFactory;
public TokenService(IConfiguration configuration, IMemoryCache memoryCache, IHttpClientFactory clientFactory, IServiceScopeFactory scopeFactory)
{
_Configuration = configuration;
_cache = memoryCache;
_clientFactory = clientFactory;
_scopeFactory = scopeFactory;
}
public Task StartAsync(CancellationToken cancellationToken)
{
_timer = new Timer(getOrg, null, 0, 1000); // getting error here
return Task.CompletedTask;
}
public Task StopAsync(CancellationToken cancellationToken)
{
//Timer does not have a stop.
_timer?.Change(Timeout.Infinite, 0);
return Task.CompletedTask;
}
public async Task getOrg()
{
var request = new HttpRequestMessage(HttpMethod.Get, "organizations");
var response = await _client_NP.SendAsync(request);
var json = await response.Content.ReadAsStringAsync();
OrganizationsClass.OrgsRootObject model = JsonConvert.DeserializeObject<OrganizationsClass.OrgsRootObject>(json);
using (var scope = _scopeFactory.CreateScope())
{
var _DBcontext = scope.ServiceProvider.GetRequiredService<DBContext>();
foreach (var item in model.resources)
{
var g = Guid.Parse(item.guid);
var x = _DBcontext.Organizations.FirstOrDefault(o => o.OrgGuid == g);
if (x == null)
{
_DBcontext.Organizations.Add(new Organizations
{
OrgGuid = g,
Name = item.name,
CreatedAt = item.created_at,
UpdatedAt = item.updated_at,
Timestamp = DateTime.Now,
Foundation = 3
});
}
else if (x.UpdatedAt != item.updated_at)
{
x.CreatedAt = item.created_at;
x.UpdatedAt = item.updated_at;
x.Timestamp = DateTime.Now;
}
}
await getSpace();
await _DBcontext.SaveChangesAsync();
}
}
}

TimerCallback takes on object parameter for state. Try changing getOrg to:
public async void getOrg(object state)

You are providing wrong parameters to System.Threading.Timer constructor.
The first parameter should be a delegate type (instead of getOrg):
public delegate void TimerCallback(object state);
So add a delegate to your code:
private void TimerProc(object state)
{
}
Change the constructor:
_timer = new Timer(TimerProc, null, 0, 1000); // getting error here

Related

TimerCallback function not getting hit in deployed environment C#

I have a Microsoft Chatbot C# code,which has a TimerCallback function, which runs after some inactive time by user. This function gets hit when running code locally, but the same function does not get hit, when deployed on Azure environment.
Appologies for too much logging lines in the code, it was just to verify that function gets hit on deployed environment or not.
Here is the whole code:
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Lebara.Crm.Bot.Core.Data;
using Lebara.Crm.Bot.Core.ServiceContracts;
using Microsoft.Bot.Builder;
using Microsoft.Bot.Schema;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Options;
using Newtonsoft.Json;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Bot.Builder.Integration.AspNet.Core;
using Microsoft.AspNetCore.Mvc;
using Microsoft.ApplicationInsights;
using Microsoft.ApplicationInsights.DataContracts;
using Microsoft.Extensions.Logging;
namespace Lebara.Crm.Bot.Services
{
public class ChatSessionTimeoutMiddleware : ActivityHandler, IMiddleware
{
private readonly IDistributedCache _distributedCache;
private readonly string _cachekey;
private readonly BotOptions _botOptions;
private readonly ISystemMessageSender _systemMessageSender;
private readonly CustomConversationStateAccessors _customConversationStateAccessors;
private readonly ITelemetryManager _telemetryManager;
private readonly ISessionHandler _sessionHandler;
private readonly IServiceProvider _serviceProvider;
private static ConcurrentDictionary<string, Timer> _sessionTimers = new ConcurrentDictionary<string, Timer>();
private readonly IBotFrameworkHttpAdapter _adapter;
private readonly string _appId;
private readonly ConcurrentDictionary<string, ConversationReference> _conversationReferences;
private string ObjectKey;
private readonly TelemetryClient telemetry = new TelemetryClient();
private readonly ILogger<ChatSessionTimeoutMiddleware> _logger;
// Timer related variables, made global, to avoid collected by Garbage Collector as they were created in memory previously.
private Timer _warningTimer = null;
private Timer _warningTimerGCIssue = null;
private Timer _timer = null;
private Timer _timerGCIssue = null;
public ChatSessionTimeoutMiddleware(
IDistributedCache distributedCache,
IConfiguration configuration,
IOptions<BotOptions> options,
ISystemMessageSender systemMessageSender,
CustomConversationStateAccessors customConversationStateAccessors,
ITelemetryManager telemetryManager,
ISessionHandler sessionHandler,
IServiceProvider serviceProvider,
IBotFrameworkHttpAdapter adapter,
ConcurrentDictionary<string, ConversationReference> conversationReferences,
ILogger<ChatSessionTimeoutMiddleware> logger)
{
_cachekey = $"{configuration["RedisCachingRoot"]}session-timeout:";
_distributedCache = distributedCache;
_botOptions = options.Value;
_systemMessageSender = systemMessageSender;
_customConversationStateAccessors = customConversationStateAccessors;
_telemetryManager = telemetryManager;
_sessionHandler = sessionHandler;
_serviceProvider = serviceProvider;
_adapter = adapter;
_conversationReferences = conversationReferences;
_appId = configuration["MicrosoftAppId"] ?? string.Empty;
_logger = logger;
}
private void AddConversationReference(Activity activity)
{
var conversationReference = activity.GetConversationReference();
_conversationReferences.AddOrUpdate(conversationReference.User.Id, conversationReference, (key, newValue) => conversationReference);
}
public async Task OnTurnAsync(ITurnContext turnContext, NextDelegate nextTurn, CancellationToken cancellationToken)
{
_logger.LogDebug("chat session timeout middleware");
//telemetry.TrackEvent("ChatSessionMiddleware - OnTurnAsync");
//telemetry.TrackTrace("ChatSessionMiddleware - OnTurnAsync", SeverityLevel.Warning, null);
var customConversationState = await _customConversationStateAccessors.CustomConversationState.GetAsync(turnContext, () => new CustomConversationState());
var hasChatSessionRunning = await _sessionHandler.HasRunningSessionAsync(turnContext.Activity.Conversation.Id);
if (turnContext.Activity.Type == ActivityTypes.Message
&& !string.IsNullOrEmpty(turnContext.Activity.Text)
&& !hasChatSessionRunning)
{
_logger.LogDebug("chatsessiontimeout OnTurnAsync if statement");
var key = _cachekey + turnContext.Activity.Conversation.Id;
_logger.LogDebug($"Key {key}");
var warningKey = "warning_" + key;
_logger.LogDebug($"WarningKey {warningKey}");
var period = _botOptions.InactivityPeriod;
_logger.LogDebug($"chatsessiontimeout period {period}");
var warningPeriod = _botOptions.WarningInactivityPeriod;
_logger.LogDebug($"chatsessiontimeout warningPeriod {warningPeriod}");
var cacheOptions = new DistributedCacheEntryOptions
{
AbsoluteExpirationRelativeToNow = period.Add(period)
};
_logger.LogDebug($"cacheOptions {cacheOptions}");
AddConversationReference(turnContext.Activity as Activity);
await _distributedCache.SetStringAsync(key, JsonConvert.SerializeObject(DateTime.Now.Add(period)), cacheOptions);
await _distributedCache.SetStringAsync(warningKey, JsonConvert.SerializeObject(DateTime.Now.Add(warningPeriod)), cacheOptions);
var timerPeriod = period.Add(TimeSpan.FromSeconds(1));
var warningTimePeriod = warningPeriod.Add(TimeSpan.FromSeconds(1));
_warningTimer = null;
_warningTimerGCIssue = null;
_sessionTimers.TryGetValue(warningKey, out _warningTimer);
_logger.LogDebug($"warningTimer {_warningTimer}");
if (_warningTimer == null)
{
_logger.LogDebug("warningTimer is null");
_warningTimerGCIssue = new Timer(new TimerCallback(WarningCallback), (warningKey, turnContext), warningTimePeriod, warningTimePeriod);
_warningTimer = _sessionTimers.GetOrAdd(warningKey, _warningTimerGCIssue);
}
_timer = null;
_timerGCIssue = null;
_sessionTimers.TryGetValue(key, out _timer);
_logger.LogDebug($"timer {_timer}");
if (_timer == null)
{
_logger.LogDebug("timer is null");
_logger.LogDebug($"key {key}");
_timerGCIssue = new Timer(new TimerCallback(Callback), (key, turnContext), timerPeriod, timerPeriod);
_timer = _sessionTimers.GetOrAdd(key, _timerGCIssue);
}
_warningTimer.Change(warningTimePeriod, warningTimePeriod);
_logger.LogDebug("chatSessionTimeoutMiddleware timer change");
_timer.Change(timerPeriod, timerPeriod);
}
_logger.LogDebug("chatSessionTimeoutMiddleware nextturn");
await nextTurn(cancellationToken).ConfigureAwait(false);
}
private async void Callback(object target)
{
//telemetry.TrackEvent("ChatSessionMiddleware - InactivityCallback");
_logger.LogDebug("ChatSessionMiddleware InactivityCallback");
var tuple = ((string, ITurnContext))target;
ObjectKey = tuple.Item1;
var turnContext = tuple.Item2;
foreach (var conversationReference in _conversationReferences.Values)
{
_logger.LogDebug("ChatSessionMiddleware InactivityCallback for loop");
await ((BotAdapter)_adapter).ContinueConversationAsync(_appId, conversationReference, EndOfChatCallback, default(CancellationToken));
}
}
private async void WarningCallback(object target)
{
//telemetry.TrackEvent("ChatSessionMiddleware - WarningCallback");
_logger.LogDebug("ChatSessionMiddleware WarningCallback");
var tuple = ((string, ITurnContext))target;
ObjectKey = tuple.Item1;
var turnContext = tuple.Item2;
foreach (var conversationReference in _conversationReferences.Values)
{
_logger.LogDebug("ChatSessionMiddleware WarningCallback for loop");
await ((BotAdapter)_adapter).ContinueConversationAsync(_appId, conversationReference, WarningMessageCallback, default(CancellationToken));
}
}
private async Task WarningMessageCallback(ITurnContext turnContext, CancellationToken cancellationToken)
{
//telemetry.TrackEvent("ChatSessionMiddleware - WarningMessageCallback");
_logger.LogDebug("ChatSessionMiddleware WarningMessageCallback");
var customConversationState = await _customConversationStateAccessors.CustomConversationState.GetAsync(turnContext, () => new CustomConversationState());
void DisposeTimer()
{
bool found = _sessionTimers.TryRemove(ObjectKey, out var timer);
if (found)
{
timer.Dispose();
timer = null;
}
}
var json = await _distributedCache.GetStringAsync(ObjectKey);
var hasChatSessionRunning = await _sessionHandler.HasRunningSessionAsync(turnContext.Activity.Conversation.Id);
if (hasChatSessionRunning)
{
DisposeTimer();
return;
}
if (!string.IsNullOrEmpty(json))
{
var sessionEnd = JsonConvert.DeserializeObject<DateTime>(json);
if (DateTime.Now >= sessionEnd)
{
//telemetry.TrackEvent("ChatSessionMiddleware - SendingWarningMessage");
_logger.LogDebug("ChatSessionMiddleware SendingWarningMessage");
await _systemMessageSender.SendSystemMessage(turnContext, customConversationState, turnContext.Activity, ResourceIds.BotWarningEndOfChat);
}
}
DisposeTimer();
}
private async Task EndOfChatCallback(ITurnContext turnContext, CancellationToken cancellationToken)
{
//telemetry.TrackEvent("ChatSessionMiddleware - EndOfChatCallback");
_logger.LogDebug("ChatSessionMiddleware EndOfChatCallback");
var chatSdk = (IChatProvider)_serviceProvider.GetService(typeof(IChatProvider));
var customConversationState = await _customConversationStateAccessors.CustomConversationState.GetAsync(turnContext, () => new CustomConversationState());
void DisposeTimer()
{
bool found = _sessionTimers.TryRemove(ObjectKey, out var timer);
if (found)
{
timer.Dispose();
timer = null;
}
}
var json = await _distributedCache.GetStringAsync(ObjectKey);
var hasChatSessionRunning = await _sessionHandler.HasRunningSessionAsync(turnContext.Activity.Conversation.Id);
if (hasChatSessionRunning)
{
DisposeTimer();
return;
}
if (!string.IsNullOrEmpty(json))
{
var sessionEnd = JsonConvert.DeserializeObject<DateTime>(json);
if (DateTime.Now >= sessionEnd)
{
var parts = ObjectKey.Split(new char[] { ':' });
var dict = new Dictionary<string, string>
{
{"EndTime", json },
{"State", JsonConvert.SerializeObject(customConversationState) }
};
_telemetryManager.TrackEvent("AutomaticChatClosing", parts[parts.Length - 1], dict);
DisposeTimer();
//telemetry.TrackEvent("ChatSessionMiddleware - SendingEndOfChatMessage");
_logger.LogDebug("ChatSessionMiddleware SendingEndOfChatMessage");
await _systemMessageSender.SendSystemMessage(turnContext, customConversationState, turnContext.Activity, ResourceIds.BotAutomaticEndOfChat);
await Task.Delay(2000);
await chatSdk.EndChat(customConversationState.ChatContext, turnContext);
}
}
else
{
DisposeTimer();
}
}
}
}
The code creating issue/ or not getting hit:
if (_warningTimer == null)
{
_logger.LogDebug("warningTimer is null");
_warningTimerGCIssue = new Timer(new TimerCallback(WarningCallback), (warningKey, turnContext), warningTimePeriod, warningTimePeriod);
_warningTimer = _sessionTimers.GetOrAdd(warningKey, _warningTimerGCIssue);
}
the above mentioned part should call the WarningCallback function after a specific warning time, it does call it successfully while running the code locally, but does not call on deployed environment.

Async or Task<T> confusion [duplicate]

This question already has answers here:
Why use async and return await, when you can return Task<T> directly?
(9 answers)
How and when to use ‘async’ and ‘await’
(25 answers)
Closed 1 year ago.
I am trying to understand how to write async controllers in webapi. Below is an example, I fake a 5 sec work in DB.
[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
private readonly ILogger<WeatherForecastController> _logger;
public WeatherForecastController(ILogger<WeatherForecastController> logger)
{
_logger = logger;
}
[HttpGet(Name = "GetWeatherForecast")]
public async Task<int> Get()
{
var r = new ServiceLayer();
var res = r.getDataAsync(_logger);
var res2 = r.getDataAsync(_logger);
var val = await res;
var val2 = await res2;
_logger.Log(LogLevel.Error, "UNDER");
return val+val2;
}
}
public class ServiceLayer
{
public Task<int> getDataAsync(ILogger<WeatherForecastController> _logger)
{
var a = new DAtaBaseLayer();
return a.queryAsync(_logger);
}
}
public class DAtaBaseLayer
{
public Task<int> queryAsync(ILogger<WeatherForecastController> _logger)
{
var t = new Task<int>(() =>
{
_logger.Log(LogLevel.Error, "BEFORE");
Thread.Sleep(5555);
_logger.Log(LogLevel.Error, "AFTER");
return 77;
});
t.Start();
return t;
}
}
I am a bit confused what would be the difference if I let the ServiceLayer be async too. What benefit would I get?
Is this idiomatic?
Should it then not be called getDataAsync but instead getData?
public class ServiceLayer
{
public async Task<int> getDataAsync(ILogger<WeatherForecastController> _logger)
{
var a = new DAtaBaseLayer();
return await a.queryAsync(_logger);
}
}

Polly FallbackAsync policy with a dynamic action that raises an event so that the request / response can be written to a database

I'm looking for a way to attach a generic Polly fallback policy to my typed HttpClient. My policy gets applied to certain request types as and when required, otherwise it applies a NoOpPolicy. Im interested in the onFallbackAsync Task. I need to save the request/response to a database table, but I'm struggling to figure out how to apply this because I can't see how to inject a dependency. I seem to be missing something obvious, but I don't know what. Can this be done with a delegating handler?
My typed client basically looks like this, and is used for a variety of different API calls:
public class ApiClient : IApiClient
{
private readonly HttpClient _httpClient;
private readonly ILogger _logger;
private HttpRequestMessage _httpRequestMessage;
private HttpContent _httpContent;
private IDictionary<string, string> _queryParameters;
public ApiClient(HttpClient httpClient, ILogger logger)
{
_httpClient = httpClient;
_logger = logger;
_httpRequestMessage = new HttpRequestMessage();
_queryParameters = new Dictionary<string, string>();
}
public void SetUrl(string urlPath)
{
urlPath = urlPath.TrimEnd('/');
_httpClient.BaseAddress = new Uri(urlPath);
}
public void SetQueryParamters(IDictionary<string, string> parameters)
{
_queryParameters = parameters;
}
public void AddQueryParamter(string key, string value)
{
_queryParameters.Add(key, value);
}
public async Task<T> Get<T>()
{
_httpRequestMessage.Method = HttpMethod.Get;
return await EnvokeService<T>();
}
public async Task<T> PostJsonAsync<T>(object body)
{
var settings = new JsonSerializerSettings()
{
ContractResolver = new CamelCasePropertyNamesContractResolver(),
NullValueHandling = NullValueHandling.Ignore
};
_httpContent = new JsonContent(JsonConvert.SerializeObject(body, settings));
_httpRequestMessage.Method = HttpMethod.Post;
return await EnvokeService<T>();
}
private async Task<T> EnvokeService<T>()
{
string responseContent = null;
try
{
_httpRequestMessage.Content = _httpContent;
_httpClient.DefaultRequestHeaders.Add("Accept", "application/json");
var responseMessage = await _httpClient.SendAsync(_httpRequestMessage, HttpCompletionOption.ResponseHeadersRead).ConfigureAwait(false);
responseContent = await responseMessage.Content.ReadAsStringAsync();
if (responseMessage.IsSuccessStatusCode)
{
T result;
var serializer = new JsonSerializer();
using (var sr = new StreamReader(await responseMessage.Content.ReadAsStreamAsync()))
using (var jsonTextReader = new JsonTextReader(sr))
{
result = serializer.Deserialize<T>(jsonTextReader);
}
_logger.Debug(logMessage);
return result;
}
throw new HttpRequestException($"Error Code: {responseMessage.StatusCode}. Response: {responseContent}");
}
catch (Exception e)
{
_logger.Exception(e, "Error");
throw;
}
}
}
It is setup like this:
public static class ApiHttpClientConfigurator
{
public static void AddApiHttpClient (this IServiceCollection services)
{
IPolicyRegistry<string> registry = services.AddPolicyRegistry();
registry.Add("WaitAndRetryPolicy", ResiliencePolicies.GetHttpPolicy(new TimeSpan(0, 1, 0)));
registry.Add("NoOpPolicy", Policy.NoOpAsync().AsAsyncPolicy<HttpResponseMessage>());
services.AddHttpClient<ApiHttpClient>().AddPolicyHandlerFromRegistry(PolicySelector);
services.AddSingleton<IApiHttpClientFactory, ApiHttpClientFactory>();
}
private static IAsyncPolicy<HttpResponseMessage> PolicySelector(IReadOnlyPolicyRegistry<string> policyRegistry, HttpRequestMessage httpRequestMessage)
{
// if we have a message of type X then apply the policy
if (httpRequestMessage.RequestUri.AbsoluteUri.Contains("/definitely/apply/a/retry/policy"))
{
return policyRegistry.Get<IAsyncPolicy<HttpResponseMessage>>("WaitAndRetryPolicy");
}
return policyRegistry.Get<IAsyncPolicy<HttpResponseMessage>>("NoOpPolicy");
}
}
Registered as so:
services.AddApiHttpClient();
My policies are defined as such:
public static class ResiliencePolicies
{
public static IAsyncPolicy<HttpResponseMessage> HttpFallBackPolicy
{
get => Policy<HttpResponseMessage>.Handle<Exception>().FallbackAsync(new HttpResponseMessage(HttpStatusCode.InternalServerError), d =>
{
// TODO: publish event
InstanceOfMediatr.Publish(new RequestFailedEvent(request/response/data/here))
Log.Warning($"Fallback: {d.Exception.GetType().Name} {d.Exception.Message}");
return Task.CompletedTask;
});
}
public static IAsyncPolicy<HttpResponseMessage> HttpRetryPolicy
{
get => HttpPolicyExtensions.HandleTransientHttpError()
.Or<TimeoutRejectedException>()
.WaitAndRetryAsync(3, retryAttempt => TimeSpan.FromSeconds(retryAttempt * 0.5));
}
public static IAsyncPolicy<HttpResponseMessage> GetHttpTimeoutPolicy(TimeSpan timeSpan) =>
Policy.TimeoutAsync<HttpResponseMessage>(timeSpan);
public static IAsyncPolicy<HttpResponseMessage> GetHttpPolicy(TimeSpan timeout) =>
Policy.WrapAsync(HttpFallBackPolicy, HttpRetryPolicy, GetHttpTimeoutPolicy(timeout));
}
The final part of the puzzle would appear to be, how can I complete the TODO section above? I seem to need a dynamic fallback action, but I'm really not sure how to implement it, or whether or not it is even possible.
Just in case anyone else comes across this problem, there are two ways to solve it depending upon your requirements (using AddPolicyHandlerFromRegistry() or AddPolicyHandler()).
Through IServiceProvider
AddPolicyHandler() has a convenient overload where you can inject IServiceProvider:
.AddPolicyHandler((provider, message) =>
HttpPolicyExtensions
.HandleTransientHttpError()
.FallbackAsync(
fallbackValue: new HttpResponseMessage(HttpStatusCode.InternalServerError),
onFallbackAsync: (result, context) =>
{
var publisher = provider.GetService<IPublisher>();
publisher.Publish(new HttpRequestFallbackEvent());
return Task.CompletedTask;
}))
Through Context
If you are using a PolicyRegistry and AddPolicyHandlerFromRegistry(), then it is easier to use the Context as described here. First extend the Polly's Context:
public static class ContextExtensions
{
private static readonly string PublisherKey = "PublisherKey";
public static Context WithDependencies(this Context context, IPublisher publisher)
{
context[PublisherKey] = publisher;
return context;
}
public static IPublisher GetPublisher(this Context context)
{
if (context.TryGetValue(PublisherKey, out object publisher))
{
return publisher as IPublisher;
}
return null;
}
}
Then in your client, inject your dependency (i.e. IPublisher) and add it to the new Context and add that to the executing context:
var context = new Context().WithDependencies(_publisher);
request.SetPolicyExecutionContext(context);
var responseMessage = await _httpClient.SendAsync(request)
Now you can use that in your registered and selected policy:
private static IAsyncPolicy<HttpResponseMessage> FallbackPolicy()
{
return HttpPolicyExtensions
.HandleTransientHttpError()
.Or<Exception>()
.FallbackAsync(
fallbackValue: new HttpResponseMessage(HttpStatusCode.InternalServerError),
onFallbackAsync: (result, context) =>
{
var publisher = context.GetPublisher();
publisher.Publish(new HttpRequestFallbackEvent());
return Task.CompletedTask;
}));
}

Cannot consume scoped service from singleton

I'm trying to use DBContext in Hosted services but getting this error.
I tried to follow this accepted answer but somehow its not working, I'm not sure what I'm doing wrong.
I'm new to .net please guide me into right direction.
Unhandled Exception: System.InvalidOperationException: Cannot consume
scoped service 'StatusApp.Context.DBContext' from singleton
'Microsoft.AspNetCore.Hosting.Internal.HostedServiceExecutor'.
public class TokenService : IHostedService
{
public IConfiguration _Configuration { get; }
protected IMemoryCache _cache;
private Timer _timer;
public IHttpClientFactory _clientFactory;
public DBContext _DBcontext;
private readonly IServiceScopeFactory _scopeFactory;
public TokenService(IConfiguration configuration, IMemoryCache memoryCache, IHttpClientFactory clientFactory, DBContext DBcontext, IServiceScopeFactory scopeFactory)
{
_Configuration = configuration;
_cache = memoryCache;
_clientFactory = clientFactory;
_scopeFactory = scopeFactory;
_DBcontext = _scopeFactory.CreateScope().ServiceProvider.GetRequiredService<DBcontext>();
}
public Task StartAsync(CancellationToken cancellationToken)
{
_timer = new Timer(getOrg, null, 0, 1000);
return Task.CompletedTask;
}
public Task StopAsync(CancellationToken cancellationToken)
{
//Timer does not have a stop.
_timer?.Change(Timeout.Infinite, 0);
return Task.CompletedTask;
}
public async Task getOrg()
{
var request = new HttpRequestMessage(HttpMethod.Get, "organizations");
var response = await _client_NP.SendAsync(request);
var json = await response.Content.ReadAsStringAsync();
OrganizationsClass.OrgsRootObject model = JsonConvert.DeserializeObject<OrganizationsClass.OrgsRootObject>(json);
foreach (var item in model.resources)
{
var g = Guid.Parse(item.guid);
var x = _DBcontext.Organizations.FirstOrDefault(o => o.OrgGuid == g);
if (x == null)
{
_DBcontext.Organizations.Add(new Organizations
{
OrgGuid = g,
Name = item.name,
CreatedAt = item.created_at,
UpdatedAt = item.updated_at,
Timestamp = DateTime.Now,
Foundation = 3
});
}
else if (x.UpdatedAt != item.updated_at)
{
x.CreatedAt = item.created_at;
x.UpdatedAt = item.updated_at;
x.Timestamp = DateTime.Now;
}
}
await _DBcontext.SaveChangesAsync();
}
}
You're almost there, but you've left DBContext as a dependency in TokenService's constructor. Remove that and you'll no longer receive the error.
public TokenService(
IConfiguration configuration,
IMemoryCache memoryCache,
IHttpClientFactory clientFactory,
DBContext DBcontext,
IServiceScopeFactory scopeFactory)
However, you're not quite following the recommendation for dealing with DbContext's in a singleton service. Instead of creating a single instance of DBContext in the constructor and storing it as a field, create a scope and a corresponding DBContext whenever you need it. In your case, that's in the getOrg method.
Follow these steps to achieve that:
Remove the _DBcontext field from your TokenService class:
public DBContext _DBcontext;
Remove the associated assignment from the TokenService constructor:
_DBcontext = _scopeFactory.CreateScope().ServiceProvider.GetRequiredService<DBcontext>();
In getOrg, create a scope, resolve an instance of DBContext and, lastly, dispose of the scope:
public async Task getOrg()
{
var request = new HttpRequestMessage(HttpMethod.Get, "organizations");
var response = await _client_NP.SendAsync(request);
var json = await response.Content.ReadAsStringAsync();
OrganizationsClass.OrgsRootObject model = JsonConvert.DeserializeObject<OrganizationsClass.OrgsRootObject>(json);
using (var scope = _scopeFactory.CreateScope())
{
var dbContext = scope.ServiceProvider.GetRequiredService<DBcontext>();
foreach (var item in model.resources)
{
var g = Guid.Parse(item.guid);
var x = dbContext.Organizations.FirstOrDefault(o => o.OrgGuid == g);
if (x == null)
{
dbContext.Organizations.Add(new Organizations
{
OrgGuid = g,
Name = item.name,
CreatedAt = item.created_at,
UpdatedAt = item.updated_at,
Timestamp = DateTime.Now,
Foundation = 3
});
}
else if (x.UpdatedAt != item.updated_at)
{
x.CreatedAt = item.created_at;
x.UpdatedAt = item.updated_at;
x.Timestamp = DateTime.Now;
}
}
await dbContext.SaveChangesAsync();
}
}
Instead of using the _DBContext field, the code above creates a local, properly-scoped variable dbContext and uses that instead.

Property injection

I'm trying make a telegram bot with reminder. I'm using Telegram.Bot 14.10.0, Quartz 3.0.7, .net core 2.0. The first version should : get message "reminder" from telegram, create job (using Quartz) and send meaasage back in 5 seconds.
My console app with DI looks like:
Program.cs
static IBot _botClient;
public static void Main(string[] args)
{
// it doesn't matter
var servicesProvider = BuildDi(connecionString, section);
_botClient = servicesProvider.GetRequiredService<IBot>();
_botClient.Start(appModel.BotConfiguration.BotToken, httpProxy);
var reminderJob = servicesProvider.GetRequiredService<IReminderJob>();
reminderJob.Bot = _botClient;
Console.ReadLine();
_botClient.Stop();
// it doesn't matter
}
private static ServiceProvider BuildDi(string connectionString, IConfigurationSection section)
{
var rJob = new ReminderJob();
var sCollection = new ServiceCollection()
.AddSingleton<IBot, Bot>()
.AddSingleton<ReminderJob>(rJob)
.AddSingleton<ISchedulerBot>(s =>
{
var schedBor = new SchedulerBot();
schedBor.StartScheduler();
return schedBor;
});
return sCollection.BuildServiceProvider();
}
Bot.cs
public class Bot : IBot
{
static TelegramBotClient _botClient;
public void Start(string botToken, WebProxy httpProxy)
{
_botClient = new TelegramBotClient(botToken, httpProxy);
_botClient.OnReceiveError += BotOnReceiveError;
_botClient.OnMessage += Bot_OnMessage;
_botClient.StartReceiving();
}
private static async void Bot_OnMessage(object sender, MessageEventArgs e)
{
var me = wait _botClient.GetMeAsync();
if (e.Message.Text == "reminder")
{
var map= new Dictionary<string, object> { { ReminderJobConst.ChatId, e.Message.Chat.Id.ToString() }, { ReminderJobConst.HomeWordId, 1} };
var job = JobBuilder.Create<ReminderJob>().WithIdentity($"{prefix}{rnd.Next()}").UsingJobData(new JobDataMap(map)).Build();
var trigger = TriggerBuilder.Create().WithIdentity($"{prefix}{rnd.Next()}").StartAt(DateTime.Now.AddSeconds(5).ToUniversalTime())
.Build();
await bot.Scheduler.ScheduleJob(job, trigger);
}
}
}
Quartz.net not allow use constructor with DI. That's why I'm trying to create property with DI.
ReminderJob.cs
public class ReminderJob : IJob
{
static IBot _bot;
public IBot Bot { get; set; }
public async Task Execute(IJobExecutionContext context)
{
var parameters = context.JobDetail.JobDataMap;
var userId = parameters.GetLongValue(ReminderJobConst.ChatId);
var homeWorkId = parameters.GetLongValue(ReminderJobConst.HomeWordId);
await System.Console.Out.WriteLineAsync("HelloJob is executing.");
}
}
How can I pass _botClient to reminderJob in Program.cs?
If somebody looks for answer, I have one:
Program.cs (in Main)
var schedBor = servicesProvider.GetRequiredService<ISchedulerBot>();
var logger = servicesProvider.GetRequiredService<ILogger<DIJobFactory>>();
schedBor.StartScheduler();
schedBor.Scheduler.JobFactory = new DIJobFactory(logger, servicesProvider);
DIJobFactory.cs
public class DIJobFactory : IJobFactory
{
static ILogger<DIJobFactory> _logger;
static IServiceProvider _serviceProvider;
public DIJobFactory(ILogger<DIJobFactory> logger, IServiceProvider sp)
{
_logger = logger;
_serviceProvider = sp;
}
public IJob NewJob(TriggerFiredBundle bundle, IScheduler scheduler)
{
IJobDetail jobDetail = bundle.JobDetail;
Type jobType = jobDetail.JobType;
try
{
_logger.LogDebug($"Producing instance of Job '{jobDetail.Key}', class={jobType.FullName}");
if (jobType == null)
{
throw new ArgumentNullException(nameof(jobType), "Cannot instantiate null");
}
return (IJob)_serviceProvider.GetRequiredService(jobType);
}
catch (Exception e)
{
SchedulerException se = new SchedulerException($"Problem instantiating class '{jobDetail.JobType.FullName}'", e);
throw se;
}
}
// get from https://github.com/quartznet/quartznet/blob/139aafa23728892b0a5ebf845ce28c3bfdb0bfe8/src/Quartz/Simpl/SimpleJobFactory.cs
public void ReturnJob(IJob job)
{
var disposable = job as IDisposable;
disposable?.Dispose();
}
}
ReminderJob.cs
public interface IReminderJob : IJob
{
}
public class ReminderJob : IReminderJob
{
ILogger<ReminderJob> _logger;
IBot _bot;
public ReminderJob(ILogger<ReminderJob> logger, IBot bot)
{
_logger = logger;
_bot = bot;
}
public async Task Execute(IJobExecutionContext context)
{
var parameters = context.JobDetail.JobDataMap;
var userId = parameters.GetLongValue(ReminderJobConst.ChatId);
var homeWorkId = parameters.GetLongValue(ReminderJobConst.HomeWordId);
await _bot.Send(userId.ToString(), "test");
}
}

Categories

Resources