Quartz .NET The instance of entity type 'TABLENAME' cannot be tracked because - .net-core

We have built an API with .NET Core 3.1 that extracts data from an Excel and stores it via
EF Core into a MS SQL database. We use Quartz. NET so that it is handled in a background thread. For DI we use Autofac.
We use Scoped Services to be able to use the DBContext via DI (as described here https://andrewlock.net/creating-a-quartz-net-hosted-service-with-asp-net-core/).
Unfortunately, saving the data still does not work when multiple users are using the application at the same time. We get the following error message:
The instance of entity type 'TABLENAME' cannot be tracked because another instance with the same key value for {'TABLEKEY'} is already being tracked. When attaching existing entities, ensure that only one entity instance with a given key value is attached. Consider using 'DbContextOptionsBuilder.EnableSensitiveDataLogging' to see the conflicting key values.
Here our related code:
Startup.cs
// Add DbContext
services.AddDbContext<ApplicationDbContext>(options => options.UseSqlServer(Configuration.GetConnectionString("Default"), b => b.MigrationsAssembly("XY.Infrastructure")));
// Add Quartz
services.AddQuartz(q =>
{
// as of 3.3.2 this also injects scoped services (like EF DbContext) without problems
q.UseMicrosoftDependencyInjectionJobFactory();
// these are the defaults
q.UseSimpleTypeLoader();
q.UseDefaultThreadPool(tp =>
{
tp.MaxConcurrency = 24;
});
});
services.AddQuartzServer(options =>
{
// when shutting down we want jobs to complete gracefully
options.WaitForJobsToComplete = true;
});
// Add Services
services.AddHostedService<QuartzHostedService>();
services.AddSingleton<IJobFactory, SingletonJobFactory>();
services.AddSingleton<ISchedulerFactory, StdSchedulerFactory>();
services.AddTransient<ImportJob>();
services.AddScoped<IApplicationDbContext, ApplicationDbContext>();
services.AddScoped<IMyRepository, MyRepository>();
Logic.cs
// Grab the Scheduler instance from the Factory
var factory = new StdSchedulerFactory();
var scheduler = await factory.GetScheduler();
var parameters = new JobDataMap()
{
new KeyValuePair<string, object>("request", message),
new KeyValuePair<string, object>("sales", sales),
};
var jobId = $"processJob{Guid.NewGuid()}";
var groupId = $"group{Guid.NewGuid()}";
// defines the job
IJobDetail job = JobBuilder.Create<ImportJob>()
.WithIdentity(jobId, groupId)
.UsingJobData(parameters)
.Build();
// defines the trigger
ITrigger trigger = TriggerBuilder.Create()
.WithIdentity($"Trigger{Guid.NewGuid()}", groupId)
.ForJob(job)
.StartNow()
.Build();
// schedule Job
await scheduler.ScheduleJob(job, trigger);
// and start it off
await scheduler.Start();
QuartzHostedService.cs
public class QuartzHostedService : IHostedService
{
private readonly ISchedulerFactory _schedulerFactory;
private readonly IJobFactory _jobFactory;
private readonly ILogger<QuartzHostedService> _logger;
private readonly IEnumerable<JobSchedule> _jobSchedules;
public QuartzHostedService(
ISchedulerFactory schedulerFactory,
IJobFactory jobFactory,
IEnumerable<JobSchedule> jobSchedules,
ILogger<QuartzHostedService> logger)
{
_schedulerFactory = schedulerFactory;
_jobSchedules = jobSchedules;
_jobFactory = jobFactory;
_logger = logger;
}
public IScheduler Scheduler { get; set; }
public async Task StartAsync(CancellationToken cancellationToken)
{
try
{
Scheduler = await _schedulerFactory.GetScheduler(cancellationToken);
Scheduler.JobFactory = _jobFactory;
foreach (var jobSchedule in _jobSchedules)
{
var job = CreateJob(jobSchedule);
var trigger = CreateTrigger(jobSchedule);
await Scheduler.ScheduleJob(job, trigger, cancellationToken);
}
await Scheduler.Start(cancellationToken);
}
catch (Exception ex)
{
_logger.LogError(ex.Message);
}
}
public async Task StopAsync(CancellationToken cancellationToken)
{
await Scheduler?.Shutdown(cancellationToken);
}
private static IJobDetail CreateJob(JobSchedule schedule)
{
var jobType = schedule.JobType;
return JobBuilder
.Create(jobType)
.WithIdentity(jobType.FullName)
.WithDescription(jobType.Name)
.Build();
}
private static ITrigger CreateTrigger(JobSchedule schedule)
{
return TriggerBuilder
.Create()
.WithIdentity($"{schedule.JobType.FullName}.trigger")
.StartNow()
.Build();
}
}
SingletonJobFactory.cs
public class SingletonJobFactory : IJobFactory
{
private readonly IServiceProvider _serviceProvider;
public SingletonJobFactory(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public IJob NewJob(TriggerFiredBundle bundle, IScheduler scheduler)
{
try
{
return _serviceProvider.GetRequiredService(bundle.JobDetail.JobType) as IJob;
}
catch (Exception ex)
{
throw;
}
}
public void ReturnJob(IJob job) { }
}
Importjob.cs
[DisallowConcurrentExecution]
public class ImportJob : IJob
{
private readonly IServiceProvider _provider;
private readonly ILogger<ImportJob> _logger;
public ImportJob(IServiceProvider provider, ILogger<ImportJob> logger)
{
_provider = provider;
_logger = logger;
}
public async Task Execute(IJobExecutionContext context)
{
try
{
using (var scope = _provider.CreateScope())
{
var jobType = context.JobDetail.JobType;
var job = scope.ServiceProvider.GetRequiredService(jobType) as IJob;
var repo = _provider.GetRequiredService<MYRepository>();
var importFactSales = _provider.GetRequiredService<IImportData>();
var savedRows = 0;
var request = (MyRequest)context.JobDetail.JobDataMap.Get("request");
var sales = (IEnumerable<MyData>)context.JobDetail.JobDataMap.Get("sales");
await importFactSales.saveValidateItems(repo, request, sales, savedRows);
}
}
catch (Exception ex)
{
_logger.LogError(ex.Message);
}
}
}

I have found a solution in the meantime. As #marko-lahma described in the comment, use the built-in hosted service and don't implement your own JobFactory.
Remove the SingletonJobFactory.cs and QuartzHostedService.cs
Use the Autofac.Extras.Quartz and Quartz.Extensions.Hosting Nuget Package
Don't use CreateScope anymore, inject all needed Dependencies over the Constructor
Register QuartzAutofacFactoryModule and QuartzAutofacJobsModule in the Startup.

Related

firebase repository in .net core 3.1 does not work on the server when i deploy it, and works well locally

i want to send commend in Firestore using a API .net core 3.1, and i am working using clean architecture. and an CQRS pattern.
like this:
namespace SmartRestaurant.Infrastructure.Services
{
public class FirebaseConfig
{
public string BasePath { get; set; }
}
public class FirebaseRepository : IFirebaseRepository
{
readonly string _DataBaseBasepath;
readonly FirestoreDb _db;
public FirebaseRepository(IOptions<FirebaseConfig> conf)
{
_DataBaseBasepath = conf.Value.BasePath;
if (string.IsNullOrEmpty(_DataBaseBasepath))
{
throw new ArgumentNullException("fireBase Path not found in appsettings");
}
var pathConfigFile = Path.Combine(AppContext.BaseDirectory, "jsonProjectConfogFile.json");
Environment.SetEnvironmentVariable("GOOGLE_APPLICATION_CREDENTIALS", pathConfigFile);
_db = FirestoreDb.Create("^projectName");
}
public async Task<T> AddAsync<T>(string path, T data, CancellationToken cancellationToken)
{
try
{
DocumentReference doc = _db.Document(_DataBaseBasepath + "/" + path);
var objectTosend = getOrderToDictionary(data);
await doc.SetAsync(objectTosend, null, cancellationToken);
return data;
}
catch (Exception exe)
{
throw exe;
}
}
public async Task<T> UpdateAsync<T>(string path, T data, CancellationToken cancellationToken)
{
try
{
DocumentReference doc = _db.Document(_DataBaseBasepath + "/" + path);
var objectTosend = getOrderToDictionary(data);
await doc.UpdateAsync(objectTosend, null, cancellationToken);
return data;
}
catch (Exception exe)
{
throw exe;
}
}
}
}
that code is in the infrastrecture layer and the interface of this service is in the application layer
public interface IFirebaseRepository
{
Task<T> AddAsync<T>(string path,T data,CancellationToken cancellationToken);
Task<T> UpdateAsync<T>(string path,T data, CancellationToken cancellationToken);
}
the injection of this service is in the infrastrecture layer like this
services.AddTransient < IFirebaseRepository, FirebaseRepository> ();
i use this service in the application layer like this :
public class OrdersCommandsHandlers : IRequestHandler<CreateOrderCommand, OrderDto>,
IRequestHandler<UpdateOrderCommand, NoContent>,
{
private readonly IApplicationDbContext _context;
private readonly IMapper _mapper;
private readonly IUserService _userService;
private readonly IFirebaseRepository _fireBase;
private readonly string CreateAction = "CreateAction";
private readonly string UpdateAction = "UpdateAction";
public OrdersCommandsHandlers(IApplicationDbContext context,
IMapper mapper,
IUserService userService,
IFirebaseRepository fireBase)
{
_context = context;
_mapper = mapper;
_userService = userService;
_fireBase = fireBase;
}
public async Task<OrderDto> Handle(CreateOrderCommand request, CancellationToken cancellationToken)
{
var validator = new CreateOrderCommandValidator();
var result = await validator.ValidateAsync(request, cancellationToken).ConfigureAwait(false);
if (!result.IsValid) throw new ValidationException(result);
....
await _context.SaveChangesAsync(cancellationToken);
var orderDto = _mapper.Map<OrderDto>(order);
orderDto.CurrencyExchange = CurrencyConverter.GetDefaultCurrencyExchangeList(orderDto.TotalToPay, foodBusiness.DefaultCurrency);
var path = request.FoodBusinessId + "/Orders/" + orderDto.OrderId;
await _fireBase.AddAsync(path, orderDto, cancellationToken);
......
return _mapper.Map<OrderDto>(newOrder);
}
public async Task<NoContent> Handle(UpdateOrderCommand request, CancellationToken cancellationToken)
{
var validator = new UpdateOrderCommandValidator();
var result = await validator.ValidateAsync(request, cancellationToken).ConfigureAwait(false);
if (!result.IsValid) throw new ValidationException(result);
......
var orderDto = _mapper.Map<OrderDto>(order);
var foodBusiness = await _context.FoodBusinesses.FindAsync(order.FoodBusinessId);
if (foodBusiness != null)
orderDto.CurrencyExchange = CurrencyConverter.GetDefaultCurrencyExchangeList(orderDto.TotalToPay, foodBusiness.DefaultCurrency);
var path = order.FoodBusinessId + "/Orders/" + order.OrderId;
await _fireBase.UpdateAsync(path,orderDto, cancellationToken);
return default;
}
}
when i deploiye this code in the server i have this erreur :
Error constructing handler for request of type MediatR.IRequestHandler`2[SmartRestaurant.Application.Orders.Commands.CreateOrderCommand,SmartRestaurant.Application.Common.Dtos.OrdersDtos.OrderDto]. Register your handlers with the container. See the samples in GitHub for examples.
i try to using fireStor in the NetCore 3 api with CQRS, and a erreur Appear when i use the hendler that contaie firebase service.
and locally all work well , the probleme appear in the server when i deploie the code.
the reason of this issues is in the constructor of firebaseService
public FirebaseRepository(IOptions<FirebaseConfig> conf)
{
_DataBaseBasepath = conf.Value.BasePath;
if (string.IsNullOrEmpty(_DataBaseBasepath))
{
throw new ArgumentNullException("fireBase Path not found in appsettings");
}
var pathConfigFile = Path.Combine(AppContext.BaseDirectory, "jsonProjectConfogFile.json");
Environment.SetEnvironmentVariable("GOOGLE_APPLICATION_CREDENTIALS", pathConfigFile);
_db = FirestoreDb.Create("^projectName");
}
the conf.Value.BasePath is null in the server because the appsetting.json does not contain the FirebaseConfig section.

Azure Service Bus not all messages received in hosted service web app

Inside a .net web app, I set up a hosted service to receive messages from an Azure Service Bus topic. The problem is that not all messages are received, only an arbitrary amount (e.g. of 20 messages only 12 are received). The rest of them ended up in the dead letter queue. This happens when the messages are send simultaneously.
I tried the following steps to solve this:
Increased the amount of maximum concurrent calls, which helped but didn't provide a guarantee
Added a prefetch count
I also tried to send messages via the functionality in the service bus resource in Azure. 500 messages, no interval time --> didn't work (for all messages). 500 messages, 1s interval time, all messages were received.
I just don't understand why the receiver is not recieving all of the messages.
I want to build a event-driven architecture and cannot make it a gamble if all messages will be processed.
Startup.cs
...
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IServiceBusTopicSubscription,ServiceBusSubscription>();
services.AddHostedService<WorkerServiceBus>();
}
...
WorkerService.cs
public class WorkerServiceBus : IHostedService, IDisposable
{
private readonly ILogger<WorkerServiceBus> _logger;
private readonly IServiceBusTopicSubscription _serviceBusTopicSubscription;
public WorkerServiceBus(IServiceBusTopicSubscription serviceBusTopicSubscription,
ILogger<WorkerServiceBus> logger)
{
_serviceBusTopicSubscription = serviceBusTopicSubscription;
_logger = logger;
}
public async Task StartAsync(CancellationToken stoppingToken)
{
_logger.LogInformation("Starting the service bus queue consumer and the subscription");
await _serviceBusTopicSubscription.PrepareFiltersAndHandleMessages().ConfigureAwait(false);
}
public async Task StopAsync(CancellationToken stoppingToken)
{
_logger.LogInformation("Stopping the service bus queue consumer and the subscription");
await _serviceBusTopicSubscription.CloseSubscriptionAsync().ConfigureAwait(false);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual async void Dispose(bool disposing)
{
if (disposing)
{
await _serviceBusTopicSubscription.DisposeAsync().ConfigureAwait(false);
}
}
}
ServiceBusSubscription.cs
public class ServiceBusSubscription : IServiceBusTopicSubscription
{
private readonly IConfiguration _configuration;
private const string TOPIC_PATH = "test";
private const string SUBSCRIPTION_NAME = "test-subscriber";
private readonly ILogger _logger;
private readonly ServiceBusClient _client;
private readonly IServiceScopeFactory _scopeFactory;
private ServiceBusProcessor _processor;
public ServiceBusBookingsSubscription(IConfiguration configuration,
ILogger<ServiceBusBookingsSubscription> logger,
IServiceScopeFactory scopeFactory)
{
_configuration = configuration;
_logger = logger;
_scopeFactory = scopeFactory;
var connectionString = _configuration.GetConnectionString("ServiceBus");
var serviceBusOptions = new ServiceBusClientOptions()
{
TransportType = ServiceBusTransportType.AmqpWebSockets
};
_client = new ServiceBusClient(connectionString, serviceBusOptions);
}
public async Task PrepareFiltersAndHandleMessages()
{
ServiceBusProcessorOptions _serviceBusProcessorOptions = new ServiceBusProcessorOptions
{
MaxConcurrentCalls = 200,
AutoCompleteMessages = false,
PrefetchCount = 1000,
};
_processor = _client.CreateProcessor(TOPIC_PATH, SUBSCRIPTION_NAME, _serviceBusProcessorOptions);
_processor.ProcessMessageAsync += ProcessMessagesAsync;
_processor.ProcessErrorAsync += ProcessErrorAsync;
await _processor.StartProcessingAsync().ConfigureAwait(false);
}
private async Task ProcessMessagesAsync(ProcessMessageEventArgs args)
{
_logger.LogInformation($"Received message from service bus");
_logger.LogInformation($"Message: {args.Message.Body}");
var payload = args.Message.Body.ToObjectFromJson<List<SchedulerBookingViewModel>>();
// Create scoped dbcontext
using var scope = _scopeFactory.CreateScope();
var dbContext = scope.ServiceProvider.GetRequiredService<dbContext>();
// Process payload
await new TestServiceBus().DoThings(payload);
await args.CompleteMessageAsync(args.Message).ConfigureAwait(false);
}
private Task ProcessErrorAsync(ProcessErrorEventArgs arg)
{
_logger.LogError(arg.Exception, "Message handler encountered an exception");
_logger.LogError($"- ErrorSource: {arg.ErrorSource}");
_logger.LogError($"- Entity Path: {arg.EntityPath}");
_logger.LogError($"- FullyQualifiedNamespace: {arg.FullyQualifiedNamespace}");
return Task.CompletedTask;
}
public async ValueTask DisposeAsync()
{
if (_processor != null)
{
await _processor.DisposeAsync().ConfigureAwait(false);
}
if (_client != null)
{
await _client.DisposeAsync().ConfigureAwait(false);
}
}
public async Task CloseSubscriptionAsync()
{
await _processor.CloseAsync().ConfigureAwait(false);
}
}
So this is how we solved the problem. It was related to the message lock duration, which is set for the Azure Resource in the portal. Previous Value: 30s. New Value: 3min.

Task was canceled but the process continued

I am processing a set of records (batch) based on a ID.
Based on exception, I am collecting exception details and sending an email.
However recently I got a “A Task was canceled” exception, so this information was added and sent in an email. But even after the exception, the record made it to the database. There were other exception and those records didn't make it to the database.
This only happens sporadically. Most of the times the exception and the records getting into the database matches.
I am using Autofac.
To give an idea
internal class Program
{
public static void Main ( )
{
IContainer context = Program.BuildContainer( );
try{
context.Resolve<IBatchProvider>().DoAsynBatchProcess().Wait();
}
catch(Exception ex)
{
//log errors
}
}
private static IContainer BuildContainer( )
{
ContainerBuilder builder = new ContainerBuilder();
builder.RegisterType<LogBuilder>.As<ILogBuilder>().InstancePerDependency();
builder.RegisterType<EmailSender>.As<IEmailSender>().InstancePerDependency();
builder.RegisterType<BatchProvider>As<IBatchProvider>().InstancePerDependency();
return builder.Build();
}
}
public class BatchProvider : IBatchProvider {
private readonly ILogBuilder _logBulider;
private readonly IEmailsender _emailSender;
public BatchProvider(ILogBuilder logBuilder, IEmailSender emailSender)
{
_logBuilder = logBuilder;
_emailSender = emailSender;
}
public async Task DoAsyncBatchProcess()
{
//Get ID from DB
….
await BatchProcessing (ID)
}
public async Task BatchProcessing (int ID)
{
//Get all records for this ID
//loop through the records and post it.
for (int index=0; i< dataRowArray.Length; ++ index)
{
try{
bool result = await ServiceClient.PostData(guid)
}
catch(Exception ex)
{
//log exception
}
finally
{
//log to file
}
}
await SendEmail ( )
}
private async Task SendEmail()
{
//email
}
private void LogToFile()
{ //log to file
}
}
public class ServiceClient
{
public static async Task<bool> PostData(string guid)
{
using( var client = new HttpClient(new HttpClientHandler() {useDefaultCredentials = true}))
{
string _baseAddress = “http://Mywebserver/“;
client.baseAddress = new Uri(_baseAddress)
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(“application/json”);
string _method = “MyMethod”;
HttpResponseMessage response = await client.PostAsJsonAsync(_method, new ServiceRequest() {MyGuid = guid}).configureAwait(false);
if(response.IsSuccessStausCode)
{
var result = await response.Content.ReadAsAsync<ServiceResponse>();
return result.Status;
}
else
{
string message = await _response.Content.ReadAsStringAsync();
throw new Exception(message);
}
}
}
}
In my Main method does the .wait() is enough or should I do the following
Context.Resolve().DoAsyncBatchProcess().ConfigureAwait(false).GetAwaiter().GetResult();
Is there a way to ensure that the exception happen and it doesn't continue for that task but continue for the other tasks?
Thank you.

Why is my blazor app leaving so many ports open

I created a .net 6 app using server side Blazor and SignalR. The app was basically a single page with 10 different components. Each component was a client that looked something like this:
#code {
private HubConnection? hubConnection;
private ExampleViewModel data { get; set; } = new ExampleViewModel();
protected override async Task OnInitializedAsync()
{
hubConnection = new HubConnectionBuilder()
.WithUrl(NavigationManager.ToAbsoluteUri("/mainhub"))
.Build();
hubConnection.On<ExampleViewModel>("example", (Data) =>
{
data = Data;
StateHasChanged();
});
await hubConnection.StartAsync();
}
public async ValueTask DisposeAsync()
{
if (hubConnection is not null)
{
await hubConnection.DisposeAsync();
}
}
}
Each component has a "broadcaster" that runs on a timer and makes a call to the database using Mediator and Dapper. Example:
public class ExampleBroadcaster : IDataBroadcaster
{
private readonly IMediator _mediator;
private readonly ILogger<ExampleBroadcaster> _logger;
private readonly IHubContext<MainHub> _mainHub;
private readonly IMemoryCache _cache;
private const string Something = "example";
private Timer _timer;
public ExampleBroadcaster(IHubContext<MainHub> mainHub,
IMediator mediator, ILogger<ExampleBroadcaster> logger,
IMemoryCache cache)
{
_mainHub = mainHub;
_mediator = mediator;
_logger = logger;
_cache = cache;
}
public void Start()
{
_timer = new Timer(BroadcastData, null, 0, 30000);
}
private async void BroadcastData(object? state)
{
ExampleViewModel viewModel;
try
{
if (_cache.TryGetValue(Something, out ExampleViewModel data))
{
viewModel = data;
}
else
{
viewModel = _mediator.Send(new GetExampleData()).Result;
_cache.Set(Something, viewModel, TimeSpan.FromMinutes(10));
}
await _mainHub.Clients.All.SendAsync("example", viewModel);
}
catch (Exception ex)
{
_logger.LogError(ex, ex.Message);
}
}
}
The mediator handler simply uses Dapper to get data from the database:
public class GetExampleData : IRequest<ExampleViewModel>
{
}
public class GetExampleDataHandler : IRequestHandler<GetExampleData, ExampleViewModel>
{
private readonly IDbConnectionFactory _connectionFactory;
private string _storedProcedure = "some sproc name";
public GetExampleDataHandler(IDbConnectionFactory connectionFactory)
{
_connectionFactory = connectionFactory;
}
public async Task<ExampleViewModel> Handle(GetExampleData request, CancellationToken cancellationToken)
{
using (var connection = _connectionFactory.GetReadOnlyConnection())
{
return await connection.QueryFirstAsync<ExampleViewModel>(_storedProcedure, CommandType.StoredProcedure);
}
}
}
This is the main razor page that houses all the individual components:
#code {
private HubConnection? hubConnection;
protected override async Task OnInitializedAsync()
{
try
{
hubConnection = new HubConnectionBuilder()
.WithUrl(NavigationManager.ToAbsoluteUri("/mainhub"))
.Build();
await hubConnection.StartAsync();
await hubConnection.SendAsync("Init");
}
catch(Exception exception)
{
Logger.LogError(exception, exception.Message);
}
}
public async ValueTask DisposeAsync()
{
if (hubConnection is not null)
{
await hubConnection.DisposeAsync();
}
}
}
Finally, the MainHub.cs code:
public class MainHub : Hub
{
IEnumerable<IDataBroadcaster> _broadcasters;
private static bool _started;
public MainHub(IEnumerable<IDataBroadcaster> broadcasters)
{
_broadcasters = broadcasters;
}
public void Init()
{
if (!_started)
{
StartBroadcasting();
_started = true;
}
}
private void StartBroadcasting()
{
foreach (var broadcaster in _broadcasters)
{
broadcaster.Start();
}
}
}
This all worked fine locally, in our dev environment, and our test environment. In production, we found that the app was crashing after a number of hours. According to the server admins, the app is opening 100s or 1000s of ports and leaving them open until the number of allotted ports was hit, causing the app to crash.
What is the issue here? The broadcasters are registered as singletons. This app only runs on one web server.

Cannot access a disposed object. with SignalR and Timer Manager

I wanna make my function send data as a real time (every 2 seconds or once there is change in the database table ) but the problem is there is Exception keep appread in my below code.
The exception details are:
'Cannot access a disposed object.
public class MyHub : Hub
{
private readonly IRepository<MyTable, long> _repository;
private readonly IUnitOfWorkManager _unitOfWorkManager;
public HCHub(IUnitOfWorkManager unitOfWorkManager,IRepository<MyTable, long> repository)
{
_repository = repository;
_unitOfWorkManager = unitOfWorkManager;
}
public void Get(TestDto testDto)
{
try {
using (var unitOfWork = _unitOfWorkManager.Begin())
{
var result= _repository.GetDbContext().Set<MyTable>()
.Include(x => x.list)
.ThenInclude(x => x.list2)
.ThenInclude(x => x.obj).ToList();
new TimerManager(async () =>
await Clients.All.SendAsync("listen", result) //<====== in this Line the exception occured
);
}
}
catch(Exception ex)
{
throw new UserFriendlyException(ex.InnerException.Message.ToString());
}
}
and TimerManager Code is
public class TimerManager
{
private Timer _timer;
private AutoResetEvent _autoResetEvent;
private Action _action;
public DateTime TimerStarted { get; }
public TimerManager(Action action)
{
_action = action;
_autoResetEvent = new AutoResetEvent(false);
_timer = new Timer(Execute, _autoResetEvent, 1000, 2000);
TimerStarted = DateTime.Now;
}
public void Execute(object stateInfo)
{
_action();
if ((DateTime.Now - TimerStarted).Seconds > 60)
{
_timer.Dispose();
}
}
}
So the problem is in Timer Manager or in myHub or the way that I'm simulate the realtime data by TimerManager is not acceptable ?!
Once you exit the hub method you aren't guaranteed to be able to access the Clients property. If you want to do something like that, you should inject an IHubContext<THub> into your Hubs constructor and use that instead. You can read more about IHubContext in https://learn.microsoft.com/aspnet/core/signalr/hubcontext?view=aspnetcore-3.1#get-an-instance-of-ihubcontext

Resources