Hangfire per-job correlationId/state - asp.net-core

I run Hangfire on ASP.NET Core.
For our other projects we have CorrelationIds that we pass when making API calls to be able to link the caller and callee.
We use the IHttpContextAccessor's TraceIdentifier for this in ASP.NET Core.
Unfortunately it looks like the trick used by ASP.NET Core to get a scoped CorrelationId in the Transient IHttpContextAccessor doesn't work for Hangfire job execution.
Using a Scoped state correlation object doesn't work because it must be Transient to be able to work with the rest of the system (logging etc.)
I used to be able to get away using the ServiceLocator anti-pattern and resolve a scoped state object in a transient service.
In the latest ASP.NET Core that is no longer supported and an exception is thrown making the system too slow because of the huge number of exceptions thrown.
Is there something that Hangfire provides already that would give me a unique ID per job execution?
Cheers.

Thanks to jbl's comment I looked at what I was doing again and managed to get it working through a kludge.
I've got the transient state holder
(basically it's the HttpContextAccessor class renamed):
public class StateHolder
{
private static AsyncLocal<ContextHolder> _contextCurrent = new AsyncLocal<ContextHolder>();
public string State {
get {
return _contextCurrent.Value?.Context;
}
set {
var holder = _contextCurrent.Value;
if (holder != null)
{
holder.Context = null;
}
if (value != null)
{
_contextCurrent.Value = new ContextHolder { Context = value };
}
}
}
private class ContextHolder
{
public string Context;
}
}
and then in Hangfire I hook it up to the activation with
public class LoggingActivator : JobActivator
{
private readonly IServiceScopeFactory _serviceScopeFactory;
private readonly ContextAccessor _contextAccessor;
public LoggingActivator([NotNull] IServiceScopeFactory serviceScopeFactory, ContextAccessor contextAccessor)
{
_serviceScopeFactory = serviceScopeFactory ?? throw new ArgumentNullException(nameof(serviceScopeFactory));
_contextAccessor = contextAccessor;
}
public override JobActivatorScope BeginScope(JobActivatorContext context)
{
return new LoggingActivatorScope(_serviceScopeFactory.CreateScope(), _contextAccessor);
}
}
and
public class LoggingActivatorScope : JobActivatorScope
{
private readonly IServiceScope _serviceScope;
private readonly ContextAccessor _contextAccessor;
public LoggingActivatorScope(
[NotNull] IServiceScope serviceScope,
ContextAccessor contextAccessor)
{
_serviceScope = serviceScope ?? throw new ArgumentNullException(nameof(serviceScope));
_contextAccessor = contextAccessor;
}
public override object Resolve(Type type)
{
_contextAccessor.Context = Guid.NewGuid().ToString();
return ActivatorUtilities.GetServiceOrCreateInstance(_serviceScope.ServiceProvider, type);
}
public override void DisposeScope()
{
_serviceScope.Dispose();
}
}
That seems to work fine.

Related

How to inject dependency into NServiceBus pipeline behavior?

I've been following the NServiceBus samples, specifically for how to use an entity framework (core) DbContext integrated with Sql Persistence so that I can save dbcontext state changes along with the outbox messages. This is the sample: https://docs.particular.net/samples/entity-framework-core/
I've modified the unit of work code a little to support creation of an aspnet core DI scoped DbContext. The relevant code follows:
public class UnitOfWork<TDbContext>
where TDbContext : DbContext
{
private Func<SynchronizedStorageSession, IServiceProvider, TDbContext> _contextFactory;
private TDbContext _context;
private IServiceProvider _serviceProvider;
public UnitOfWork(Func<SynchronizedStorageSession, IServiceProvider, TDbContext> contextFactory, IServiceProvider serviceProvider)
{
_contextFactory = contextFactory;
_serviceProvider = serviceProvider;
}
public TDbContext GetDataContext(SynchronizedStorageSession storageSession)
{
if (_context == null)
{
_context = _contextFactory(storageSession, _serviceProvider);
}
return _context;
}
}
public class UnitOfWorkSetupBehavior<TDbContext> : Behavior<IIncomingLogicalMessageContext>
where TDbContext : DbContext
{
private readonly Func<SynchronizedStorageSession, IServiceProvider, TDbContext> _contextFactory;
private readonly IServiceScopeFactory _serviceScopeFactory;
public UnitOfWorkSetupBehavior(Func<SynchronizedStorageSession, IServiceProvider, TDbContext> contextFactory, IServiceScopeFactory serviceScopeFactory)
{
_contextFactory = contextFactory;
_serviceScopeFactory = serviceScopeFactory;
}
public override async Task Invoke(IIncomingLogicalMessageContext context, Func<Task> next)
{
using (var scope = _serviceScopeFactory.CreateScope())
{
var uow = new UnitOfWork<TDbContext>(_contextFactory, scope.ServiceProvider);
context.Extensions.Set(uow);
await next().ConfigureAwait(false);
context.Extensions.Remove<UnitOfWork<TDbContext>>();
}
}
}
public static class EndpointConfigurationExtensions
{
public static void RegisterUnitOfWork<TDbContext>(this EndpointConfiguration endpointConfiguration, IServiceScopeFactory serviceScopeFactory)
where TDbContext : DbContext
{
var pipeline = endpointConfiguration.Pipeline;
pipeline.Register(new UnitOfWorkSetupBehavior<TDbContext>((storageSession, serviceProvider) =>
{
var dbConnection = storageSession.SqlPersistenceSession().Connection;
var dbContextFactory = serviceProvider.GetService<IDbContextConnectionFactory<TDbContext>>();
var dbContext = dbContextFactory.GetDbContext(dbConnection);
//Use the same underlying ADO.NET transaction
dbContext.Database.UseTransaction(storageSession.SqlPersistenceSession().Transaction);
//Call SaveChanges before completing storage session
storageSession.SqlPersistenceSession().OnSaveChanges(x => dbContext.SaveChangesAsync());
return dbContext;
}, serviceScopeFactory), "Sets up unit of work for the message");
}
}
public static class UnitOfWorkContextExtensions
{
public static TDbContext DataContext<TDbContext>(this IMessageHandlerContext context)
where TDbContext : DbContext
{
var uow = context.Extensions.Get<UnitOfWork<TDbContext>>();
return uow.GetDataContext(context.SynchronizedStorageSession);
}
}
For this to work the behavior needs an injected IServiceScopeFactory.
Now all examples I've been able to find of behavior registration only show the type manually instantiated and passed in to the endpointconfiguration's pipeline.
Is there a way to either gain access to an IServiceScopeFactory via the behavior's Invoke method (maybe by the context via some extension perhaps?), or is it possible to register the behavior itself such that I can construct it with services created by the DI container?
FYI I took a look at this Q&A which gave me the idea of injecting the IServiceScopeFactory. Unfortunately, the answer doesn't show how to actually get an instance of the interface.
You would use context.builder.Build<T>(); within the Invoke method to resolve any objects like IServiceScopeFactory.
https://docs.particular.net/samples/multi-tenant/di/
Make sure that the IServiceScopeFactory is registered in the DI container. For example, during your endpoint initialization:
endpointConfiguration.RegisterComponents(registration: x =>
{
x.ConfigureComponent<IServiceScopeFactory>(yourServiceScopeFactory);
});
https://docs.particular.net/nservicebus/dependency-injection/
You can also do this by creating a Feature
https://docs.particular.net/nservicebus/pipeline/features

Scope issues when using Ninject BindHttpFilter

I have a WebApi service that I am trying to add authentication to using Ninject BindHttpFilter.
Using the BindHttpFilter allows me to bind the authentication filter to a specific attribute. The AuthenticationFilter takes a constructor parameter (IAuthenticationService) which itself is created by Ninject.
kernel.BindHttpFilter<AuthenticationHttpFilter>(System.Web.Http.Filters.FilterScope.Action)
.WhenActionMethodHas<AuthenticationFilterAttribute>()
.WithConstructorArgument("service", x => x.Kernel.Get<IAuthenticationService>());
The concrete implementation of AuthenticationService takes a constructor parameter INonceRepository which is injected via Ninject:
public AuthenticationService(INonceRepository nonceRepository, ...)
The concrete implementation of NonceRepository takes a constructor ISession which is injected via Ninject:
public NonceRepository(ISession session)
Here is what the Ninject bindings look like:
kernel.Bind<INonceRepository>().To<NonceRepository>();
kernel.Bind<IAuthenticationService>().To<AuthenticationService>()
var session = sessionFactory.OpenSession();
Bind<ISession>().ToMethod(c => session).InRequestScope();
When the code runs the concrete implementation of AuthenticationService is only instantiated once and therefore NonceRepositiory is only instantiated once. This means that the ISession is valid and Open the first request but the ISession is closed on the second call and the constructor of AuthenticationService is never called the second time. It seems like it is a scoping issue but I can't figure out what doesn't have the correct scoping to make AuthenticationService get recreated per request.
I've tried to change the BindHttpScope request from FilterScope.Controller to FilterScope.Action (thinking that would cause the scope of AuthenticationService to be that it was create per Action call) but that didn't solve it.
Here is what the the interesting points of code looks like:
public class AuthenticationHttpFilter : IAuthenticationFilter
{
private readonly IAuthenticationService authenticationService;
public AuthenticationHttpFilter(IAuthenticationService service)
{
this.authenticationService = service;
}
public bool AllowMultiple { get; private set; }
public Task AuthenticateAsync(HttpAuthenticationContext authenticationContext, CancellationToken cancellationToken)
{
authenticationService.DoAuth();
return Task.FromResult(0);
}
public Task ChallengeAsync(HttpAuthenticationChallengeContext authenticationChallengeContext, CancellationToken cancellationToken)
{
...
}
}
public class AuthenticationService : IAuthenticationService
{
private readonly INonceRepository nonceRepo;
public AuthenticationService(INonceRepository nonceRepo){...}
public void DoAuth()
{
this.nonceRepo.Add(...);
}
}
public class NonceRepository : INonceRepository
{
private readonly ISession _session;
public NonceRepository(ISession session)
{
this._session = session;
}
public void Add(Nonce nonce)
{
this._session.Save(nonce);
}
}
I found that that when I am using WebApi 2, everything works fine. It behaves in RequestScope like I would expect. However, when using MVC 5, the filter stays cached in the pipeline, which keeps the constructor argument in a "Singleton" scope.
I fixed this problem by reaching into the configuration of the actionContext as such:
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = false, Inherited = true)]
public class EventGridFilterAttribute : ActionFilterAttribute
{
private readonly Type serviceType = typeof(IEventGridService);
public EventGridFilterAttribute()
{
}
public override async Task OnActionExecutedAsync(HttpActionExecutedContext actionExecutedContext, CancellationToken cancellationToken)
{
var service = actionExecutedContext?.ActionContext?.ControllerContext?.Configuration?.DependencyResolver?.GetService(serviceType) as IEventGridService;
if (service != null && actionExecutedContext.Exception == null)
{
await service.PublishEventsAsync();
}
await base.OnActionExecutedAsync(actionExecutedContext, cancellationToken);
}
}
My dependency injector is saved here.
GlobalConfiguration.Configuration.DependencyResolver = new NinjectDependencyResolver(kernel);
I also do not use the BindHttpFilter, but register my filter like a normal filter.
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new EventGridFilterAttribute());
}

Ninject Property Injection in WebAPI custom ExceptionFilterAttribute Not Working

I am trying to use Ninject to inject an EventLogger instance into a custom ExceptionFilterAttribute. Whenever I run the code, the EventLogger instance is null. I have implemented an IFilterProvider to resolve dependencies in a similar manner for my custom AuthorizationFilterAttribute, and that works fine. Any ideas?
Not Working
public class ErrorHandlingAttribute : ExceptionFilterAttribute
{
[Inject]
public IEventLogger EventLogger { get; set; }
public override void OnException(HttpActionExecutedContext actionExecutedContext)
{
EventLogger.LogException(actionExecutedContext.Exception);
actionExecutedContext.Response = actionExecutedContext.Request.
CreateResponse(HttpStatusCode.BadRequest,
new ServiceErrorResponseDTO("An unhandled exception occurred while calling " +
actionExecutedContext.Request.RequestUri.ToString() +
". This event has been logged. If you continue to receive this error contact Weichert"));
}
}
Working
public class RequireAuthorizationAttribute : AuthorizationFilterAttribute
{
[Inject]
public IServiceRepository ServiceRepository { get; set; }
public override void OnAuthorization(HttpActionContext actionContext)
{
#region Header Authentication
var authHeader = actionContext.Request.Headers.Authorization;
if (authHeader != null)
{
Custom IFilterProvider
public class NinjectWebApiFilterProvider : IFilterProvider
{
private IKernel _kernel;
public NinjectWebApiFilterProvider(IKernel kernel)
{
_kernel = kernel;
}
public IEnumerable<FilterInfo> GetFilters(HttpConfiguration configuration, HttpActionDescriptor actionDescriptor)
{
var controllerFilters = actionDescriptor.ControllerDescriptor.GetFilters().Select(instance => new FilterInfo(instance, FilterScope.Controller));
var actionFilters = actionDescriptor.GetFilters().Select(instance => new FilterInfo(instance, FilterScope.Action));
var filters = controllerFilters.Concat(actionFilters);
foreach(var filter in filters)
{
_kernel.Inject(filter.Instance);
}
return filters;
}
}
NinjectWebCommon CreateKernel Method
private static IKernel CreateKernel()
{
var kernel = new StandardKernel();
kernel.Bind<Func<IKernel>>().ToMethod(ctx => () => new Bootstrapper().Kernel);
kernel.Bind<IHttpModule>().To<HttpApplicationInitializationHttpModule>();
// Ad Ninject support for Web API.
GlobalConfiguration.Configuration.DependencyResolver = new NinjectDependencyResolver(kernel);
GlobalConfiguration.Configuration.Services.Add(typeof(IFilterProvider),
new NinjectWebApiFilterProvider(kernel));
RegisterServices(kernel);
return kernel;
}
NinjectWebCommon Bindings
private static void RegisterServices(IKernel kernel)
{
kernel.Bind<ServiceDbContext>().To<ServiceDbContext>();
kernel.Bind<IServiceRepository>().To<ServiceRepository>();
kernel.Bind<CareerDevelopmentDbContext>().To<CareerDevelopmentDbContext>();
kernel.Bind<ICareerDevelopmentRepository>().To<CareerDevelopmentRepository>();
kernel.Bind<ICareerDevelopmentService>().To<CareerDevelopmentService>();
kernel.Bind<IEventLogger>().To<ServiceEventLogger>();
kernel.Bind<IFilterProvider>().To<NinjectWebApiFilterProvider>().WithConstructorArgument("kernel", kernel);
}
I had the same problem and was configuring my error handler the same way by adding it to the filter collection in WebApiConfig.cs which meant it wasn't getting handled by the FilterProvider implementation I had added. So I did this instead:
public class LoggingExceptionFilterAttribute : ExceptionFilterAttribute, IExceptionFilter
{
// this is what I wanted injected
private IEmailService emailService;
public LoggingExceptionFilterAttribute(IEmailService service)
{
emailService = service;
}
public override void OnException(HttpActionExecutedContext actionExecutedContext)
{
// my implementation here
}
}
Then I registered this in NinjectWebCommon like so:
kernel.Bind<System.Web.Http.Filters.IExceptionFilter>().To<LoggingExceptionFilterAttribute>().InSingletonScope();
And then in WebApiConfig I realized that I could get a hold of the DependencyResolver so I did this:
config.Filters.Add((IFilter)config.DependencyResolver.GetService(typeof(IExceptionFilter)));
Now Ninject handles constructing my exception filter and I can even do constructor injection instead of needing [Inject] attributes and I don't have to add my ExceptionFilterAttribute to every API controller.
Ok, you have to make sure you are binding your custom IFilterProvider as well. As of writing the Ninject.Web.WebApi Nuget package is unstable and would do that automatically for you, if you were using it. Just in the same fashion Ninject.MVC3 does this for your regular controllers.
Just make sure you have this binding, and the replaced DependencyResolver will look for IFilterProvider implementation via your Ninject kernel as well:
kernel.Bind<IFilterProvider>().To<NinjectWebApiFilterProvider>();
Then your NinjectWebApiFilterProvider will kick in and inject dependencies into your filters as per your code.

SignalR, WebAPI and MVC sharing the same dependency resolver kernel

I have an ASP.NET MVC app with SignalR and WebAPI. The app uses Ninject for dependency injection, but apparently SignalR and WebAPI are getting different kernels, so it fails to share a singleton object that should be shared for all the application.
I can see clearly in the log how an instance is created when SignalR gets a connection request, and other when WebAPI gets a request.
I want to have the same Ninject kernel shared among these three elements, so I can have unique singletons.
This is what I have done so far:
The first thing I have done is creating a NinjectModule declaring the binding:
public class MyDependencyModule: NinjectModule
{
public override void Load()
{
var binding = Bind<MustBeSingleton>().ToSelf();
binding.OnActivation((ctx, o) =>
{
Debug.Print("Registering item " + o.GetHashCode());
HostingEnvironment.RegisterObject(o);
});
binding.OnDeactivation(o =>
{
Debug.Print("Unregistering game connection " + o.GetHashCode());
});
binding.InSingletonScope();
}
}
I have also created a wrapper for Ninject in order to plug it in WebAPI:
public class NinjectDependencyScope : IDependencyScope
{
private IResolutionRoot resolver;
internal NinjectDependencyScope(IResolutionRoot resolver)
{
this.resolver = resolver;
}
public void Dispose()
{
IDisposable disposable = resolver as IDisposable;
if (disposable != null)
disposable.Dispose();
resolver = null;
}
public object GetService(Type serviceType)
{
if (resolver == null)
throw new ObjectDisposedException("this", "This scope has already been disposed");
return resolver.TryGet(serviceType);
}
public IEnumerable<object> GetServices(Type serviceType)
{
if (resolver == null)
throw new ObjectDisposedException("this", "This scope has already been disposed");
return resolver.GetAll(serviceType);
}
}
public class NinjectDependencyResolver : NinjectDependencyScope, IDependencyResolver
{
private IKernel kernel;
public NinjectDependencyResolver(IKernel kernel)
: base(kernel)
{
this.kernel = kernel;
}
public IDependencyScope BeginScope()
{
return new NinjectDependencyScope(kernel.BeginBlock());
}
}
Also, I have created another wrapper for SignalR:
public class SignalRNinjectDependencyResolver : DefaultDependencyResolver
{
private readonly IKernel _kernel;
public SignalRNinjectDependencyResolver(IKernel kernel)
{
_kernel = kernel;
}
public override object GetService(Type serviceType)
{
return _kernel.TryGet(serviceType) ?? base.GetService(serviceType);
}
public override IEnumerable<object> GetServices(Type serviceType)
{
return _kernel.GetAll(serviceType).Concat(base.GetServices(serviceType));
}
}
Then I have created a Ninject kernel that does all the config:
public class ApplicationDependencies:StandardKernel
{
public ApplicationDependencies()
:base(new MyDependencyModule())
{
System.Web.Http.GlobalConfiguration.Configuration.DependencyResolver = new NinjectDependencyResolver(this);
Microsoft.AspNet.SignalR.GlobalHost.DependencyResolver = new SignalRNinjectDependencyResolver(this);
}
}
The MVC application, uses NinjectHttpApplication as base class, so I indicate the kernel that must be used this way:
public class MvcApplication : Ninject.Web.Common.NinjectHttpApplication
{
protected override Ninject.IKernel CreateKernel()
{
return new ApplicationDependencies();
}
}
Also, in the SignalR configuration I specify the Resolver:
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.MapSignalR<MyPersistentConnection>("/updates", new ConnectionConfiguration()
{
Resolver = GlobalHost.DependencyResolver
});
}
}
(I have tried also without specifying the resolver, and it does not work either).
Any idea?
Cheers.
I found the answer in another post: Singleton Scope binding not working as intended
Rather than binding as a singleton, "ToConstant" must be used:
var binding = Bind<MustBeSingleton>().ToConstant(new MustBeSingleton());
I have created a simple demo project with ASP.NET MVC, WebAPI and SignalR using the same dependency injection context.
https://drive.google.com/file/d/0B52OsuSSsroNX0I5aWFFb1VrRm8/edit?usp=sharing
The web app, contains a single page that shows the AppDomain and GetHashCode of an object that is supposed to be unique across the three frameworks, giving a result similar to:
Dependency Test
Framework IMySingletonService instance
MVC AppDomainId:2 / HashCode:5109846
WebAPI AppDomainId:2 / HashCode:5109846
SignalR AppDomainId:2 / HashCode:5109846
Other problem was, that Ninject was disposing my singleton because was IDisposable. I don't really understand why this happens, but that is another war.
Cheers.
In order keep this 3 things working.. you should check these references out:
Web API + Ninject
http://www.peterprovost.org/blog/2012/06/19/adding-ninject-to-web-api/
SignalR + Ninject https://github.com/SignalR/SignalR/wiki/Extensibility (last part:
When using ASP.NET MVC, configure SignalR first, then ASP.NET MVC)
For the second one, I refactored a little bit, since I need the kernel for SignalR Dependency Resolver
// Route SignalR.
GlobalHost.DependencyResolver = NinjectWebCommon.GetSignalrResolver();
RouteTable.Routes.MapHubs();
I defined GetSignalrResolver inside of NinjectWebCommon like this:
public static Microsoft.AspNet.SignalR.Ninject.NinjectDependencyResolver GetSignalrResolver()
{
return new Microsoft.AspNet.SignalR.Ninject.NinjectDependencyResolver(bootstrapper.Kernel);
}
Note: There are 2 different DependencyResolver: one for Web API (1) assigned to GlobalConfiguration.Configuration.DependencyResolver and the other for SignalR (2) assigned to GlobalHost.DependencyResolver
in order to use a dependency resolver for both WebApi and SignalR you need to implement a class that looks like this:
public class NinjectDependencyResolver : Microsoft.AspNet.SignalR.DefaultDependencyResolver,
System.Web.Http.Dependencies.IDependencyResolver
{
public readonly IKernel Kernel;
public NinjectDependencyResolver(string moduleFilePattern)
: base()
{
Kernel = new StandardKernel();
Kernel.Load(moduleFilePattern);
}
public override object GetService(Type serviceType)
{
var service = Kernel.TryGet(serviceType) ?? base.GetService(serviceType);
return service;
}
public override IEnumerable<object> GetServices(Type serviceType)
{
IEnumerable<object> services = Kernel.GetAll(serviceType).ToList();
if (services.IsEmpty())
{
services = base.GetServices(serviceType) ?? services;
}
return services;
}
public System.Web.Http.Dependencies.IDependencyScope BeginScope()
{
return this;
}
public void Dispose()
{ }
}
then in your startup class you should register NinjectDependencyResolver for both WebApi and SignalR, like this:
public void Configuration(IAppBuilder app)
{
var dependencyResolver = new NinjectDependencyResolver("*.dll");
var httpConfiguration = new HttpConfiguration();
httpConfiguration.DependencyResolver = dependencyResolver;
app.UseWebApi(httpConfiguration);
var hubConfig = new HubConfiguration { Resolver = dependencyResolver };
app.MapSignalR(hubConfig);
}

Quartz.NET, NH ISession & Ninject Scope

I'm trying to implement a service that will run jobs based on Quartz.Net. The jobs may have dependencies like IRepository<> and the repository implementation will have a NHibernate ISession injected into it. (Quartz will be hosted in a Windows Service). Jobs are resolved via a IJob factory implementation that uses Ninject to resolve (currently wrapped in a IServiceLocator implementation).
Job Scope
I would like to be able to use Ninject to scope the ISession per Job so that there is one session created per job that may be used in multiple IRepository<>'s .
Not sure if this is possible but am wondering if anyone has experience with this?
Can I somehow use the Job context to create a Scope that is used by Kernel.InScope(???).
Quartz.Net IJobFactory:
public class JobFactory : IJobFactory
{
readonly IServiceLocator locator;
public JobFactory(IServiceLocator locator)
{
this.locator = locator;
}
public IJob NewJob(TriggerFiredBundle bundle, IScheduler scheduler)
{
try
{
var jobDetail = bundle.JobDetail;
var jobType = jobDetail.JobType;
return (IJob)locator.Resolve(jobType);
}
catch (Exception e)
{
var se = new SchedulerException("Problem instantiating class", e);
throw se;
}
}
}
Ninject Bindings:
//Service Locator
Bind<IServiceLocator>().To<NinjectAdapter>();
//Quartz Bindings
Bind<IJobFactory>().To<JobFactory>();
//NHibernate Bindings
Bind<ISessionFactory>().ToMethod(ctx => ctx.Kernel.Get<NHibernateConfiguration>().BuildSessionFactory()).InSingletonScope();
Bind<ISession>().ToMethod(ctx => ctx.Kernel.Get<ISessionFactory>().OpenSession());// ToDo: Figure out how to scope session
//Repository Bindings
Bind(typeof (IRepository<>)).To(typeof (ReadWriteRepository<>));
Main Execution:
InitializeIoC();
scheduler = schedulerFactory.GetScheduler();
scheduler.JobFactory = ServiceLocator.Resolve<IJobFactory>();
InitializeJobs();
scheduler.Start();
Example Job:
public class TestJob3 : IJob
{
private readonly IRepository<Customer> repo;
private readonly IRepository<Order> orderRepo;
public TestJob3(IRepository<Customer> repo, IRepository<Order> orderRepo)
{
//orderRepo and repo should have the same ISession
this.repo = repo;
this.oderRepo = orderRepo;
System.Diagnostics.Debug.WriteLine("Job 3 Created");
}
#region Implementation of IJob
public void Execute(IJobExecutionContext context)
{
System.Diagnostics.Debug.WriteLine("Job 3 Executing");
using (var scope = new TransactionScope())
{
var customer = repo.GetById(1);
customer.Name = "Blue Goats";
repo.Save(customer);
scope.Complete();
}
}
#endregion
}
** Repository Snippet: **
public class ReadWriteRepository<TEntity> : IRepository<TEntity> where TEntity : class, IRootEntity
{
private readonly ISession session;
public ReadWriteRepository(ISession session)
{
this.session = session;
}
public virtual TEntity GetById(int id)
{
var entity = session.Get<TEntity>(id);
return entity;
}
public virtual TEntity Save(TEntity entity)
{
session.SaveOrUpdate(entity);
return entity;
}
}
Thanks for taking the time!
Update
I ended up using Remo's suggestion and am using InCallScope():
Bind<ISession>().ToMethod(ctx => ctx.Kernel.Get<ISessionFactory>().OpenSession()).InCallScope();
The way I like to think of it (correct or not?) is everything from the "initial" get reuses the same items throughout the dependency tree
Use InCallScope
https://github.com/ninject/ninject.extensions.namedscope/wiki/InCallScope