How to wrap a Hangfire cancellation token? - domain-driven-design

I'm building up a .net core web app that requires background tasks to be run. To avoid using an external cron triggers we've decided to go with Hangfire; which is a beautiful package to use, does exactly whats needed then gets out of the way ;-)
To keep things clean I'm trying to stick to Uncle Bob's Clean architecture principles and seperate my ApplicationCore from the Infrastructure as much as possible. As Hangfire is an implementation detail it should ideally sit in the Infrastructure project, alongside database access, message queues, etc. with an Interface in the ApplicationCore, that my domain can use.
For the basic client running recurring and background jobs this has been fairly simple to do, and I've ended up with this as my Interface
namespace ApplicationCore.Interfaces
{
using System;
using System.Linq.Expressions;
using System.Threading.Tasks;
public interface IBackgroundJobClient
{
void AddOrUpdate<T>(
string recurringJobId,
Expression<Func<T, Task>> methodCall,
string cronExpression);
void RemoveIfExists(string recurringJobId);
}
}
Implementation is a simple wrapper for these methods which uses RecurringJob
namespace Infrastructure.BackgroundJobs
{
using System;
using System.Linq.Expressions;
using System.Threading.Tasks;
using Hangfire;
using IBackgroundJobClient = ApplicationCore.Interfaces.IBackgroundJobClient;
public class HangfireBackgroundJobClient : IBackgroundJobClient
{
public void AddOrUpdate<T>(
string recurringJobId,
Expression<Func<T, Task>> methodCall,
string cronExpression)
{
RecurringJob.AddOrUpdate<T>(recurringJobId, methodCall, cronExpression);
}
public void RemoveIfExists(string recurringJobId)
{
RecurringJob.RemoveIfExists(recurringJobId);
}
}
The issue that I have is needing to setup a RecurringJob with a supplied cancellationToken. However, I can't see an easy way to do this without exposing the underlying IJobCancellationToken and JobCancellationToken objects in my ApplicationCore code...?
What I've got at present is a wrapper for the JobCancellationToken in my Infrastructure.
namespace Infrastructure.BackgroundJobs
{
public class BackgroundJobCancellationToken : JobCancellationToken
{
public BackgroundJobCancellationToken(bool canceled): base(canceled)
{
}
}
}
An Interface in my ApplicationCore, which replicates the Hangfire one.
namespace ApplicationCore.Interfaces
{
using System.Threading;
public interface IJobCancellationToken
{
CancellationToken ShutdownToken { get; }
void ThrowIfCancellationRequested();
}
}
This is then used by the Method I want to execute as job, making use of the cancellationToken.ShutdownToken to pass into other methods requiring a cancellationToken.
public async Task GenerateSubmission(Guid SetupGuidId, IJobCancellationToken cancellationToken)
{
try
{
// Sort out the entities that we'll need
var setup = await this.SetupRepository.GetByGuidIdAsync(SetupGuidId);
var forecast = await this.GetCurrentForecastForSetup(setup, DateTime.UtcNow, cancellationToken.ShutdownToken);
// Other Code
}
catch (Exception e)
{
Console.WriteLine(e);
throw;
}
}
Which in turn is enabled elsewhere by calling
public async Task<Setup> EnableSetup(Setup setup)
{
setup.Enable();
this.jobClient
.AddOrUpdate<IForecastService>(
setup.GuidId.ToString(),
f => f.GenerateSubmission(setup.GuidId, null),
"45 */2 * * *");
await this.setupRepository.UpdateAsync(setup);
return setup;
}
This should be done with DomainEvents and Handlers, but one step at a time :-)
Is there a cleaner, better, easier way of doing this without taken a direct dependency on Hangfire in my ApplicationCore?
If the above setup works, I'll leave a comment on this question.

Since Hangfire 1.7 you no longer have to rely on IJobCancellationToken. You can simply use the standard .NET CancellationToken instead.

Related

how to do something when liferay module stop

i am making cron job like loop to do something using new thread.
when module stop, this thread keeps running, so when i deployed updated module, i'm afraid it will make duplicate thread doing similar task
#Component(immediate = true, service = ExportImportLifecycleListener.class)
public class StaticUtils extends Utils{
private StaticUtils() {}
private static class SingletonHelper{
private static final StaticUtils INSTANCE = new StaticUtils();
}
public static StaticUtils getInstance() {
return SingletonHelper.INSTANCE;
}
}
public class Utils extends BaseExportImportLifecycleListener{
public Utils() {
startTask();
}
protected Boolean CRON_START = true;
private void startTask() {
new Thread(new Runnable() {
public void run() {
while (CRON_START) {
System.out.println("test naon bae lah ");
}
}
}).start();
}
#Deactivate
protected void deactivate() {
CRON_START = false;
System.out.println(
"cron stop lah woooooooooooooooooy");
}
}
i'm using liferay 7
I have populated task that i store from db, so this thread is checking is there a task that it must do, then if it exist execute it.
I'm quite new in osgi and liferay. i've try to use scheduler and failed and also exportimportlifecycle listener but dont really get it yet
think again: Do you really need something to run all the time in the background, or do you just need some asynchronous processing in the background, when triggered? It might be better to start a background task as a one-off, that automatically terminates
Liferay provides an internal MessageBus, that you can utilize to listen to events and implement background processing, without the need for a custom thread
You're in the OSGi world, so you can utilize #Activate, #Modified, #Deactivate (from org.osgi.service.component.annotations) or use a org.osgi.framework.BundleActivator.
But, in general, it's preferable if you don't start your own thread

Access SignalR Hub without Constructor Injection

With AspNetCore.SignalR (1.0.0 preview1-final) and AspNetCore.All (2.0.6), how can I invoke a method on a hub in server code that is not directly in a Controller and is in a class that cannot be made via Dependency Injection?
Most examples assume the server code is in a Controller and should 'ask' for the hub via an injectable parameter in a class that will created by DI.
I want to be able to call the hub's method from server code at any time, in code that is not injected. The old SignalR had a GlobalHost that enabled this approach. Basically, I need the hub to be a global singleton.
Now, everything seems to be dependent on using Dependency Injection, which is introducing a dependency that I don't want!
I've seen this request voiced in a number of places, but haven't found a working solution.
Edit
To be more clear, all I need is to be able to later access the hubs that I've registered in the Configure routine of the Startup class:
app.UseSignalR(routes =>
{
routes.MapHub<PublicHubCore>("/public");
routes.MapHub<AnalyzeHubCore>("/analyze");
routes.MapHub<ImportHubCore>("/import");
routes.MapHub<MainHubCore>("/main");
routes.MapHub<FrontDeskHubCore>("/frontdesk");
routes.MapHub<RollCallHubCore>("/rollcall");
// etc.
// etc.
});
If I register them like this:
services.AddSingleton<IPublicHub, PublicHubCore>();
it doesn't work, since I get back an uninitiated Hub.
No It's not possible. See "official" answer from david fowler https://github.com/aspnet/SignalR/issues/1831#issuecomment-378285819
How to inject your hubContext:
Best solution is to inject your hubcontext like IHubContext<TheHubWhichYouNeedThere> hubcontext
into the constructor.
See for more details:
Call SignalR Core Hub method from Controller
Thanks to those who helped with this. Here's what I've ended up on for now...
In my project, I can call something like this from anywhere:
Startup.GetService<IMyHubHelper>().SendOutAlert(2);
To make this work, I have these extra lines in Startup.cs to give me easy access to the dependency injection service provider (unrelated to SignalR):
public static IServiceProvider ServiceProvider { get; private set; }
public static T GetService<T>() { return ServiceProvider.GetRequiredService<T>(); }
public void Configure(IServiceProvider serviceProvider){
ServiceProvider = serviceProvider;
}
The normal SignalR setup calls for:
public void Configure(IApplicationBuilder app){
// merge with existing Configure routine
app.UseSignalR(routes =>
{
routes.MapHub<MyHub>("/myHub");
});
}
I don't want all my code to have to invoke the raw SignalR methods directly so I make a helper class for each. I register that helper in the DI container:
public void ConfigureServices(IServiceCollection services){
services.AddSingleton<IMyHubHelper, MyHubHelper>();
}
Here's how I made the MyHub set of classes:
using Microsoft.AspNetCore.SignalR;
using System.Threading.Tasks;
public class MyHub : Hub { }
public interface IMyHubHelper
{
void SendOutAlert(int alertNumber);
}
public class MyHubHelper : IMyHubHelper
{
public IHubContext<MyHub> HubContext { get; }
public MyHubHelper(IHubContext<MyHub> hubContext)
{
HubContext = hubContext;
}
public void SendOutAlert(int alertNumber)
{
// do anything you want to do here, this is just an example
var msg = Startup.GetService<IAlertGenerator>(alertNumber)
HubContext.Clients.All.SendAsync("serverAlert", alertNumber, msg);
}
}
This is a nice solution. In .NET Core 2.1 the service provider is disposed and you get cannot access disposed object. The fix is to create a scope:
public void Configure(IApplicationBuilder app, IHostingEnvironment env, IServiceProvider serviceProvider)
{
ServiceProvider = serviceProvider.CreateScope().ServiceProvider;

What is the recommended way to run asp.net identity functions in transaction?

Using asp.net identity RTW version.
I need to perform several actions in a transaction, including both UserMananger function calls and other operations on my DbContext (example: create new user, add it to group and perform some business-logic operations).
How should I do this?
My thoughts follow.
TransactionScope
using (var scope = new TransactionScope(TransactionScopeOption.Required))
{
// Do what I need
if (everythingIsOk) scope.Complete();
}
The problem is: UserManager functions are all async, and TransactionScope was not designed to work with async/await. It seems to be solved in .Net Framework 4.5.1. But I use Azure Web Sites to host my project builds, so I cannot target 4.5.1 yet.
Database transaction
public class SomeController : Controller
{
private MyDbContext DbContext { get; set; }
private UserManager<User> UserManager { get; set; }
public AccountController()
{
DbContext = new MyDbContext()
var userStore = new UserStore<IdentityUser>(DbContext);
UserManager = new UserManager<IdentityUser>(userStore);
}
public async ActionResult SomeAction()
{
// UserManager uses the same db context, so they can share db transaction
using (var tran = DbContext.Database.BeginTransaction())
{
try
{
// Do what I need
if (everythingIsOk)
tran.Commit();
else
{
tran.Rollback();
}
}
catch (Exception)
{
tran.Rollback();
}
}
}
}
That seems to work, but how can I unit-test it?
UserManager<> constructor accepts IUserStore<>, so I can easily stub it.
UserStore<> constructor accepts DbContext, no idea how I can stub this.
You can implement your own test user store that can be stubbed out for your unit test.
If you want to use the actual EF UserStore in your tests, that also will work, but it will be creating a database using the DefaultConnection string by default. You could specify a DatabaseInitializer to always drop/recreate your tables in your tests if you wanted to ensure a clean db for every test.

Thread safe issue with Castle.Facilities.NHibernateIntegration ISessionManager in Web context

So based on this question (here), of which I asked last week, I decided to go and have a look into the Castle project and use the Castle.Facilities.NHibernateIntegration facility.
I spent the best part of two days messing around with it and have come to the same issue: NHibernate Thread-Safe Sessions. I was hoping, out of the box, the built in ISessionManager was smart enough to handle threading, which is the reason why I decided to implement it.
In the very sparse documentation on that particular project it mentions that calling ISessionManager.OpenSession is much the same as calling session.GetCurrentSession. From this I gather there is no way for me to, force open, a new seperate session.
So has anyone the solution for me or any ideas how I can work with this issue?
(I know most people are going to say only work with one thread, but honestly think outside the box, some tools and routines automatically spawn a new thread. For instance, log4net and sessionstatestore. You can't just assume there will only be one thread, associated, with the current request.)
Notes:
I'm working on the web model with .NET 4 web application.
I invoke and resolve the Windsor container in the usual, documented way and let the container resolve the session manager. I do this in both threads.
Here is my Castle NHibernate config:
Code:
<facility id="nhibernate" isWeb="true" type="Castle.Facilities.NHibernateIntegration.NHibernateFacility, Castle.Facilities.NHibernateIntegration">
<factory id="nhibernate.factory">
<settings>
<item key="connection.connection_string">#{NHibernateConnectionString}</item>
<item key="connection.driver_class">#{NHibernateDriver}</item>
<item key="connection.provider">NHibernate.Connection.DriverConnectionProvider</item>
<item key="dialect">#{NHibernateDialect}</item>
<item key="generate_statistics">true</item>
<item key="proxyfactory.factory_class">NHibernate.ByteCode.Castle.ProxyFactoryFactory, NHibernate.ByteCode.Castle</item>
<item key="show_sql">true</item>
</settings>
<assemblies>
<assembly>Gigastence.Base.Common</assembly>
</assemblies>
</factory>
Here is my example DAO
Code:
public class NHibernateDao : INHibernateDao
{
private ISessionManager sessionManager;
public NHibernateDao(ISessionManager sessionManager)
{
this.sessionManager = sessionManager;
}
public void Append(LoggingEvent loggingEvent)
{
using (IStatelessSession session = sessionManager.OpenStatelessSession())
{
using (ITransaction tran = session.BeginTransaction())
{
Log data = new Log
{
Id = Guid.NewGuid(),
Date = loggingEvent.TimeStamp,
Level = loggingEvent.Level.ToString(),
Logger = loggingEvent.LoggerName,
Thread = loggingEvent.ThreadName,
Message = loggingEvent.MessageObject.ToString()
};
if (loggingEvent.ExceptionObject != null)
{
data.Exception = loggingEvent.ExceptionObject.ToString();
}
session.Insert(data);
tran.Commit();
}
}
}
}
And how I call the DAO. Note: This is on the newly spawned thread which is out of my hands.
Code:
public class NHibenateAppender : AppenderSkeleton
{
protected override void Append(LoggingEvent loggingEvent)
{
if(IoC.IsInitialized)
{
var NHibernateLogger = IoC.Resolve<INHibernateDao>();
NHibernateLogger.Append(loggingEvent);
}
}
}
If you want full control of the session, I believe that the NHibernateFacility actually registers the underlying ISessionFactory to the Windsor kernel.
From that, you can invoke sessionFactory.OpenSession() which I think should always return a new session.
I honestly don't really see what ISessionManager brings to the party...
Have a look at this link!
https://github.com/haf/Castle.Facilities.NHibernate/wiki
It might solve your multi-threaded problems, as it differs in intention to the previous facility; this one lets you keep a session-per-transaction rather than one per request. As such, the multi-threaded issue is avoided and it would work equally well from your appender.
In the code, this is because the .Net has a CallContext static class that knows about what thread you're on (but ties it to your call context rather than thread-static).
We were running into this problem a lot when using the SessionPerWebRequest pattern and then forking worker threads, which as you say, cannot be helped in some situations.
The trick is as jishi says; instead of pulling the session from Func<ISession> or ISessionManager, you need to get access to ISessionFactory.
Unfortunately for me, this wasn't as simple as injecting it through the constructor and having Windsor resolve it - it isn't registered as part of the installer as jishi said (unless I'm missing something). However, it is accessible through an installer callback:
public class NHibernateInstaller : INHibernateInstaller, IDatabaseInstaller
{
...
public void Registered(ISessionFactory factory)
{
SessionFactoryStore.Set(SessionFactoryKey, factory);
}
}
Where SessionFactoryStore is a singleton repository for storing your factories (in the case where you may have multiple factories, spread across clients, like me).
[Singleton]
public class SessionFactoryStore: ISessionFactoryStore
{
Dictionary<string, ISessionFactory> SessionFactories { get; set; }
public SessionFactoryStore()
{
SessionFactories = new Dictionary<string, ISessionFactory>();
}
public void Set(string key, ISessionFactory factory)
{
lock (SessionFactories)
{
if (!SessionFactories.ContainsKey(key)) SessionFactories.Add(key, factory);
}
}
public ISessionFactory Get(string key)
{
return SessionFactories.ContainsKey(key) ? SessionFactories[key] : null;
}
}
Then wherever you implement your unit of work pattern, or similar, just perform a test to see if you are running in a normal, or threaded state:
[PerWebRequest]
public class UnitOfWork : IUnitOfWork
{
private IGenericFactory GenericFactory { get; set; }
private Func<ISession> Session { get; set; }
private ISessionFactoryStore SessionFactoryStore { get; set; }
private ISession GetSession(bool isThreaded)
{
if (!isThreaded)
return Session();
else
return SessionFactoryStore.Get("YourFactoryKey").OpenSession();
}
public UnitOfWork(Func<ISession> session, ISessionFactoryStore sessionFactoryStore) {
Session = session;
SessionFactoryStore = sessionFactoryStore;
}
...
}
Hey presto, thread-safe ISession using NHibernateIntegration.

Is it possible to mock NLog log methods?

Is it possible/easy to mock NLog log methods, using Rhino Mocks or similar?
Using Nuget : install-package NLog.Interface
Then: ILogger logger = new LoggerAdapter([logger-from-NLog]);
You can only mock virtual methods. But if You create some interface for logging and then implement it using NLog You can use dependency injection and in Your tests use mocked interface to see if system under test (SUT) is logging what You expect it to log.
public class SUT
{
private readonly ILogger logger;
SUT(ILogger logger) { this.logger = logger;}
MethodUnderTest() {
// ...
logger.LogSomething();
// ...
}
}
// and in tests
var mockLogger = new MockLogger();
var sut = new SUT(mockLogger);
sut.MethodUnderTest();
Assert.That("Expected log message", Is.Equal.To(mockLogger.LastLoggedMessage));
The simple answer, is 'no'. Looking at the code, dependency-injection is not supported, which seems rather an oversight, especially as it doesn't look difficult to implement (at first glance).
The only interfaces in the project are there to support COM interop objects and a few other things. The main Logger concrete class neither implements an interface, nor provides virtual methods.
You could either provide an interface yourself, or use Moles/TypeMock/ another isolation framework to mock the dependency.
I've used code like this to stub out the NLog logging code. You can make use of NLog's MemoryTarget which just keeps messages in memory until it's disposed of. You can query the content of the log using Linq or whatever (this example uses FluentAssertions)
using FluentAssertions
using Microsoft.VisualStudio.TestTools.UnitTesting;
using NLog;
using NLog.Config;
using NLog.Targets;
...
private MemoryTarget _stubLogger;
[TestInitialize]
public void Setup()
{
ConfigureTestLogging();
}
protected virtual LoggingConfiguration GetLoggingConfiguration()
{
var config = new NLog.Config.LoggingConfiguration();
this._stubLogger = new MemoryTarget();
_stubLogger.Layout = "${level}|${message}";
config.AddRule(LogLevel.Debug, LogLevel.Fatal, this._stubLogger);
return config;
}
protected virtual void ConfigureTestLogging()
{
var config = GetLoggingConfiguration();
NLog.LogManager.Configuration = config;
}
[TestMethod]
public void ApiCallErrors_ShouldNotThrow()
{
// arrange
var target = new Thing();
// act
target.DoThing();
// assert
this._stubLogger.Logs.Should().Contain(l =>
l.Contains("Error|") &&
l.Contains("Expected Message"));
}

Resources