How to send constantly updates using .Net Core SignalR? - asp.net-core

I am new to SignalR and I would like to build such app -- every second a hub sends current time to all connected clients.
I found tutorial, but it is for .Net Framework (not Core): https://learn.microsoft.com/en-us/aspnet/signalr/overview/getting-started/tutorial-high-frequency-realtime-with-signalr So on one hand I don't know how to translate it to .Net Core SignalR, on the other hand I don't know how to write it from scratch (the limiting condition is the fact a hub is a volatile entity, so I cannot have state in it).
I need something static (I guess) with state -- let's say Broadcaster, when I create some cyclic action which in turn will send updates to clients. If such approach is OK, how to initialize this Broadcaster?
Currently I added such static class:
public static class CrazyBroadcaster
{
public static void Initialize(IServiceProvider serviceProvider)
{
var scope = serviceProvider.CreateScope();
var hub = scope.ServiceProvider.GetRequiredService<IHubContext<ChatHub>>();
var sub = Observable.Interval(TimeSpan.FromSeconds(1)).Subscribe(_ => hub.Clients.All.SendAsync("Bar", DateTimeOffset.UtcNow));
}
}
Yes, I know it is leaky. I call this method at the end of Startup.Configure, probably tons of violations here, but so far it is my best shot.

The missing piece was hosted service, i.e. the code that runs in the background -- https://learn.microsoft.com/en-US/aspnet/core/fundamentals/host/hosted-services?view=aspnetcore-2.2.
So my crazy class is now transformed into:
public sealed class HostedBroadcaster : IHostedService, IDisposable
{
private readonly IHubContext<ChatHub> hubContext;
private IDisposable subscription;
public HostedBroadcaster(IHubContext<ChatHub> hubContext)
{
this.hubContext = hubContext;
}
public void Dispose()
{
this.subscription?.Dispose();
}
public Task StartAsync(CancellationToken cancellationToken)
{
this.subscription = Observable.Interval(TimeSpan.FromSeconds(1)).Subscribe(_ => hubContext.Clients.All.SendAsync("Bar", DateTimeOffset.UtcNow));
return Task.CompletedTask;
}
public Task StopAsync(CancellationToken cancellationToken)
{
this.subscription?.Dispose();
return Task.CompletedTask;
}
}

Related

How to start an ASP.NET Core BackgroundService on demand?

I want to be able to start fire-and-forget jobs in ASP.NET Core 2.2. I have tried the following:
services.AddHostedService<TestHostedService>();
public class TestHostedService : BackgroundService
{
private readonly ILogger _logger;
public TestHostedService(ILoggerFactory loggerFactory)
{
_logger = loggerFactory.CreateLogger<TestHostedService>();
}
public IBackgroundTaskQueue TaskQueue { get; }
protected async override Task ExecuteAsync(
CancellationToken cancellationToken)
{
_logger.LogInformation("TestHostedService is starting.");
_logger.LogInformation("TestHostedService is stopping.");
}
}
However, this automatically starts and I want to be able to start it on demand, similarly to how Hangfire allows:
BackgroundJob.Enqueue<TestJob>(x => x.DoWork());
This also allows the job to naturally use ASP.NET CORE DI.
Question: How to start an ASP.NET Core BackgroundService on demand?
###Background information
I am dealing with an application that needs to fire-and-forget various methods. The already written code looks like this:
Task.Run(() => RunSomething(_serviceScopeFactory));
This means that each method must explicitly deal with getting a scope and retrieving the dependencies which is quite ugly.
If you want to run the BackgroundService in the MVC controller or other service. You could try to inject the IServiceProvider to that class and then loop all the hosted service and find the background service, at last you could call the startasync method.
More details, you could refer to below codes:
Register the service in Startup.cs
services.AddHostedService<TestHostedService>();
Execute the background service in the controller:
public class HomeController : Controller
{
private readonly IServiceProvider _serviceProdiver;
public HomeController(IServiceProvider serviceProdiver) {
_serviceProdiver = serviceProdiver;
}
public async Task<IActionResult> Index()
{
var allBackgroundServices = _serviceProdiver.GetServices<IHostedService>();
foreach (var hostedService in allBackgroundServices)
{
if (hostedService.GetType() == typeof(TestHostedService))
{
await hostedService.StartAsync(CancellationToken.None);
}
}
return View();
}
}
Result:

how to handle DbContext lifetime in API / backgroundworker

the past days I have been struggling with injecting a DbContext in MY background worker. On the one hand, I want to inject the dbContext in my backgorund worker, but on the other hand I also want to use it in my API.
The injecting in my API seems to work fine, but since my worker is a singleton, I can not follow the standard lifetime of scoped for my dbcontext, and I have to add it as transient.
I have already tried to create a unit of work, in which I can refresh the context myself in my worker, effectively creating some kind of scoped service. I would refresh the context every time the worker went through his loop once again. This worked, and the application was working as I wanted, but I was no longer able to properly test, since I would create a new DbContext myself in the code. I feel like there must be a better way for to handle this.
My project structure looks like the following:
API => contains controlers + models I use for post requests.
The API project needs to use my database, to get and post data. It uses the repositories for this
Core (class library) => contains some core models
Domain(class library) => Contains my domain models + repositories.
All database work goes through here
Worker => Contains some logic.
The worker needs to use my database, to get and post data. It uses the repositories for this
Services (class library) => Some services that contain some logic.
The worker uses my repositories to get to the database
Tests => Tests for all code.
I want to be able to to integrationTesting as well here.
I currently inject all repositories and services in both my API and worker:
Worker configureservices:
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureServices((hostContext, services) =>
{
services.AddDbContext<CatAPIDbContext>(ServiceLifetime.Transient);
services.AddTransient(typeof(IFeedingProfileRepository), typeof(FeedingProfileRepository));
services.AddTransient(typeof(IFeedingTimesRepository), typeof(FeedingTimesRepository));
services.AddTransient(typeof(IFeedHistoryRepository), typeof(FeedHistoryRepository));
services.AddTransient(typeof(IMotorController), typeof(MotorController));
services.AddTransient(typeof(IFoodDispenser), typeof(FoodDispenser));
services.AddTransient(typeof(IGenericRepository<>), typeof(GenericRepository<>));
services.AddTransient(typeof(IFeedingTimeChecker), typeof(FeedingTimeChecker));
services.AddHostedService<Worker>();
});
(EDIT)Worker code:
public class Worker : BackgroundService
{
private readonly ILogger<Worker> _logger;
public IFeedingTimeChecker _feedingTimeChecker { get; }
public Worker(ILogger<Worker> logger, IFeedingTimeChecker feedingTimeChecker)
{
_logger = logger;
_feedingTimeChecker = feedingTimeChecker;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
try
{
_logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now);
_feedingTimeChecker.ResetFeedingTimesGivenIfNeeded();
_feedingTimeChecker.FeedIfNeeded();
}
catch(Exception ex)
{
_logger.LogError(ex.ToString());
}
await Task.Delay(10000, stoppingToken);
}
}
}
(EDIT)FeedingTimeChecker (called by worker)
private FeedingProfile _currentProfile { get; set; }
public DateTime lastResetDataFeedingTimes;
public DateTime lastProfileRefresh;
private readonly ILogger<FeedingTimeChecker> _logger;
private IFeedingProfileRepository _feedingProfileRepository { get; set; }
private IFeedingTimesRepository _feedingTimesRepository { get; set; }
private IFoodDispenser _foodDispenser { get; }
public FeedingTimeChecker(IFeedingProfileRepository feedingProfileRepository, IFeedingTimesRepository feedingTimesRepository,IFoodDispenser foodDispenser, ILogger<FeedingTimeChecker> logger)
{
lastResetDataFeedingTimes = DateTime.MinValue.Date;
lastProfileRefresh = DateTime.MinValue.Date;
_foodDispenser = foodDispenser;
_logger = logger;
_feedingTimesRepository = feedingTimesRepository;
_feedingProfileRepository = feedingProfileRepository;
}
public void UpdateCurrentProfile()
{
if(Time.GetDateTimeNow - TimeSpan.FromSeconds(5) > lastProfileRefresh)
{
_logger.LogInformation("Refreshing current profile");
_currentProfile = _feedingProfileRepository.GetCurrentFeedingProfile();
lastProfileRefresh = Time.GetDateTimeNow;
}
}
API configureServices:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvcCore().SetCompatibilityVersion(CompatibilityVersion.Version_3_0);
services.AddDbContext<CatAPIDbContext>();
services.AddTransient(typeof(IFeedingProfileRepository), typeof(FeedingProfileRepository));
services.AddTransient(typeof(IFeedingTimesRepository), typeof(FeedingTimesRepository));
services.AddTransient(typeof(IFeedHistoryRepository), typeof(FeedHistoryRepository));
services.AddTransient(typeof(IMotorController), typeof(MotorController));
services.AddTransient(typeof(IFoodDispenser), typeof(FoodDispenser));
services.AddTransient(typeof(IGenericRepository<>), typeof(GenericRepository<>));
}
in my repositories I use the dbContext like the following:
public class GenericRepository<T> : IGenericRepository<T> where T : class
{
public CatAPIDbContext _dbContext { get; set; }
public GenericRepository(CatAPIDbContext dbContext)
{
_dbContext = dbContext;
}
public T GetById(object id)
{
return _dbContext.Set<T>().Find(id);
}
}
The result I would expect, is for my worker and API to behave correctly, always getting the lastest data and disposing of the dbContext on every single request, since I use a transient lifetime for my dbContext.
However, in my worker, I always get the following error:
The instance of entity type 'FeedingTime' cannot be tracked because another instance another instance of this type with the same key is already being tracked.
This error occurs when I try to set a column in the FeedingTime table.
A feedingProfile has 0-many feedingTimes, and the feedingProfile constantly retrieved.
Any solution where I can keep a testable clean codebase, but yet not run into this problem would be very welcome.
Thanks in advance

Custom action filter unity dependency injection web api 2

I followed this article and got everything working except dependency inject (partially). In my project I am using unity and I am trying to create a custom Transaction attribute the purpose of which is to start a NHibernate transaction before the execution of an action and commit/rollback the transaction after the method execution.
This is the definition of my attribute:-
public class TransactionAttribute : Attribute
{
}
Following is the definition of my TransactionFilter
public class TransactionFilter : IActionFilter
{
private readonly IUnitOfWork _unitOfWork;
public TransactionFilter(IUnitOfWork uow) {
_unitOfWork = uow;
}
public Task<HttpResponseMessage> ExecuteActionFilterAsync(HttpActionContext actionContext, CancellationToken cancellationToken, Func<Task<HttpResponseMessage>> continuation) {
var transAttribute = actionContext.ActionDescriptor.GetCustomAttributes<TransactionAttribute>().SingleOrDefault();
if (transAttribute == null) {
return continuation();
}
var transaction = uow.BeginTransaction();
return continuation().ContinueWith(t =>
{
try{
transaction.Commit();
return t.Result;
}
catch(Exception e)
{
transaction.Rollback();
return new ExceptionResult(ex, actionContext.ControllerContext.Controller as ApiController).ExecuteAsync(cancellationToken).Result;
}
}
}
}
And I have created a custom filter provider which uses unity to construct this filter.
public class UnityActionFilterProvider
: ActionDescriptorFilterProvider,
IFilterProvider
{
private readonly IUnityContainer container;
public UnityActionFilterProvider(IUnityContainer container)
{
this.container = container;
}
public new IEnumerable<FilterInfo> GetFilters(HttpConfiguration configuration, HttpActionDescriptor actionDescriptor)
{
foreach (IActionFilter actionFilter in container.ResolveAll<IActionFilter>())
{
// TODO: Determine correct FilterScope
yield return new FilterInfo(actionFilter, FilterScope.Global);
}
}
}
I register the UnityActionFilterProvider in UnityWebApiActivator (I am using Unity.AspNet.WebApi package) as follows
public static void Start()
{
var container = UnityConfig.GetConfiguredContainer();
var resolver = new UnityDependencyResolver(container);
var config = GlobalConfiguration.Configuration;
config.DependencyResolver = resolver;
var providers = config.Services.GetFilterProviders();
var defaultProvider = providers.Single(i => i is ActionDescriptorFilterProvider);
config.Services.Remove(typeof(IFilterProvider), defaultProvider);
config.Services.Add(typeof(IFilterProvider), new UnityActionFilterProvider(container));
}
The problem is everything works ok for the first request for any action but subsequent requests for the same action doesn't recreate the TransactionFilter which means it doesn't call the constructor to assign a new UOW. I don't think I can disable the action filter caching.
The only option I have got now is to use the service locator pattern and get UOW instance using container inside ExecuteActionFilterAsync which in my opinion kills the purpose of this and I am better off implementing custom ActionFilterAttribute.
Any suggestions ?
As far as I've been able to tell during the years, what happens in web application startup code essentially has Singleton lifetime. That code only runs once.
This means that there's only a single instance of each of your filters. This is good for performance, but doesn't fit your scenario.
The easiest solution to that problem, although a bit of a leaky abstraction, is to inject an Abstract Factory instead of the dependency itself:
public class TransactionFilter : IActionFilter
{
private readonly IFactory<IUnitOfWork> _unitOfWorkFactory;
public TransactionFilter(IFactory<IUnitOfWork> uowFactory) {
_unitOfWorkFactory = uowFactory;
}
// etc...
Then use the factory in the ExecuteActionFilterAsync method:
var transaction = _unitOfWorkFactory.Create().BeginTransaction();
A more elegant solution, in my opinion, would be to use a Decoraptor that Adapts the TransactionFilter, but the above answer is probably easier to understand.

looking for samples on how to user services.add* in asp.vnext

I would like to know where can I find samples the explains the differences among services.AddInstance, services.AddScoped, services.AddSingleton and service.AddTransient.
I found some articles that explain the point in a generic way, but I think a source sample is much more clear.
The scope of this questions is rather large, but since it seems you are specifically looking for AddScoped information I narrowed the sample down to scoping inside a web application.
Inside a web application AddScoped will mean pretty much the scope of the request. EntityFramework is using scoping internally, but it doesn't affect the user code in most cases so I'm sticking with the user code as shown below.
If you register a DbContext as a service, and also register a scoped service, for each request you will get a single instance of the scoped service where you resolve the DbContext.
The example code below should make it clearer. In general I would recommend just trying it out the way I'm showing it below to familiarize yourself with the behavior, by stepping through the code in the debugger. Start from an empty web application. Note the code I'm showing is from Beta2 (since in Beta2 we added the [FromServices] attribute which makes it easier to demonstrate, the underlying behavior is the same regardless of version.
startup.cs
public void ConfigureServices(IServiceCollection services)
{
// Add EF services to the services container.
services.AddEntityFramework(Configuration)
.AddSqlServer()
.AddDbContext<UserDbContext>();
services.AddScoped<UserService>();
// Add MVC services to the services container.
services.AddMvc();
}
UserDbContext.cs
public class UserDbContext : DbContext
{
public UserService UserService { get; }
public UserDbContext(UserService userService)
{
_userService = userService;
}
}
HomeController.cs
public class HomeController : Controller
{
private UserDbContext _dbContext;
public HomeController(UserDbContext dbContext)
{
_dbContext = dbContext;
}
public string Index([FromServices]UserDbContext dbContext, [FromServices]UserService userService)
{
// [FromServices] is available start with Beta2, and will resolve the service from DI
// dbContext == _ctrContext
// and of course dbContext.UserService == _ctrContext.UserService;
if (dbContext != _dbContext) throw new InvalidOperationException();
if (dbContext.UserService != _dbContext.UserService) throw new InvalidOperationException();
if (dbContext.UserService != userService) throw new InvalidOperationException();
return "Match";
}
}
Alternatively if you resolve the user service from another service, this time registered as transient the transient service will have a new instance everytime it is resolved, but the scoped service will remain the same within the scope of the request.
Create the new service
public class AnotherUserService
{
public UserService UserService { get; }
public AnotherUserService(UserService userService)
{
UserService = userService;
}
}
Add the following lines to startup.cs
services.AddTransient<AnotherUserService>();
And rewrite the HomeController.cs as follows
public class HomeController : Controller
{
private AnotherUserService _anotherUserService;
public HomeController(AnotherUserService anotherUserService)
{
_anotherUserService = anotherUserService;
}
public string Index([FromServices]AnotherUserService anotherUserService,
[FromServices]UserService userService)
{
// Since another user service is tranient we expect a new instance
if (anotherUserService == _anotherUserService)
throw new InvalidOperationException();
// but the scoped service should remain the same instance
if (anotherUserService.UserService != _anotherUserService.UserService)
throw new InvalidOperationException();
if (anotherUserService.UserService != userService)
throw new InvalidOperationException();
return "Match";
}
}

ChannelFactory: creating and disposing

I have written an Sdk that is used by a WPF client, and takes care of calling WCF services and caching. These WCF services are called using the ChannelFactory, so I don't have service references. To do that, I created a factory that handles opening and closing ChannelFactory and ClientChannel as follows:
public class ProjectStudioServiceFactory : IDisposable
{
private IProjectStudioService _projectStudioService;
private static ChannelFactory<IProjectStudioService> _channelFactory;
public IProjectStudioService Instance
{
get
{
if (_channelFactory==null) _channelFactory = new ChannelFactory<IProjectStudioService>("ProjectStudioServiceEndPoint");
_projectStudioService = _channelFactory.CreateChannel();
((IClientChannel)_projectStudioService).Open();
return _projectStudioService;
}
}
public void Dispose()
{
((IClientChannel)_projectStudioService).Close();
_channelFactory.Close();
}
}
And each request I call like:
using (var projectStudioService = new ProjectStudioServiceFactory())
{
return projectStudioService.Instance.FindAllCities(new FindAllCitiesRequest()).Cities;
}
Although this works, it's slow because for every request the client channel and factory is opened and closed. If I keep it open, it's very fast. But I was wondering what the best practise would be? Should I keep it open? Or not? How to handle this in a correct way?
Thanks Daniel, didn't see that post. So I guess that the following may be a good approach:
public class ProjectStudioServiceFactory : IDisposable
{
private static IProjectStudioService _projectStudioService;
private static ChannelFactory<IProjectStudioService> _channelFactory;
public IProjectStudioService Instance
{
get
{
if (_projectStudioService == null)
{
_channelFactory = new ChannelFactory<IProjectStudioService>("ProjectStudioServiceEndPoint");
_projectStudioService = _channelFactory.CreateChannel();
((IClientChannel)_projectStudioService).Open();
}
return _projectStudioService;
}
}
public void Dispose()
{
//((IClientChannel)_projectStudioService).Close();
//_channelFactory.Close();
}
}