I have a problem with Ninject in a MVC project using Owin.
I have a generic class for UnitOfWork that is not specific to my project :
public class UnitOfWork : IUnitOfWork
{
public UnitOfWork(DbContext context)
{...}
}
I define two repositories using my custom DbContext :
public UserRepository : IUserRepository
{
public UserRepository(MyEntities context)
{...}
}
public OrderRepository : IOrderRepository
{
public OrderRepository(MyEntities context)
{...}
}
Then I have a ApiController which use the unit of work and the repositories.
public OrderController : ApiController
{
public OrderController(IUnitOfWork uow, IUserRepository userRepository, IOrderRepository orderRepository)
{...}
}
I configure my Ninject kernel within a module. My bindings are with a request scope.
public class MyModule : Ninject.Modules.NinjectModule
{
public override void Load()
{
// Bind all the repositories
this.Bind(x =>
x.FromAssembliesMatching("*.Repositories")
.SelectAllClasses()
.BindDefaultInterface()
.Configure(c => c.InRequestScope()));
// Bind the DbContext of the application
this.Bind<MyEntities>()
.ToSelf()
.InRequestScope();
// To bind the UnitOfWork, I need to specify the real DbContext to use. For that I use a callback which provide argument to constructor :
this.Bind<IUnitOfWork>()
.To<UnitOfWork>()
.InRequestScope()
.WithConstructorArgument("context", GetContext);
}
private Object GetContext(IContext context, ITarget target)
{
IResolutionRoot resolver;
ActivationBlock scope;
scope = context.Request.GetScope() as ActivationBlock;
resolver = scope ?? (IResolutionRoot)context.Kernel;
var o = resolver.Get<MyEntities>();
var o2 = resolver.Get<MyEntities>();
var same = Object.ReferenceEquals(o, o2);
return o;
}
}
Then I activate Ninject with Owin like this in the Startup class :
public class Startup
{
public void Configuration(IAppBuilder app)
{
...
app.UseNinjectMiddleware(Startup.CreateKernel);
var config = new HttpConfiguration();
...
app.UseNinjectWebApi(config);
}
private static IKernel CreateKernel()
{
var kernel = new StandardKernel();
kernel.Load(new MyModule());
return kernel;
}
}
It seems good but there is a big problem. The repositories share the same DbContext, but the DbContext in the UnitOfWork is a different instance.
In the function GetContext, the scope is always null, so the MyContext instance is retrieved from the kernel. The boolean variable same is always false. The problem is here. The Get function of the kernel return a new instance, instead of the instance of the request scope.
Not sure if you still need this... but you can bind the dbcontext to self and then ask for it when you want to use it.
Bind<ApplicationDbContext>().ToSelf();
Bind<IUserStoreGuid<User>>().To<UserStoreGuid<User>>().WithConstructorArgument("context", Kernel.GetService(typeof(ApplicationDbContext)));
Although the connection string in the app is called "DefautConnection", you need to use "context" because that is how it is called in the constructor argument. I got this from here
Related
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
I have a TypeFilterAtrribute which instantiates and ActionFilter. The ActionFilter needs two services injected.
public class ValidateUserAttribute : TypeFilterAttribute
{
public ValidateUserAttribute() : base(typeof(AuthenticationFilter))
{
}
private class AuthenticationFilter : ActionFilterAttribute
{
private readonly IActiveDirectoryService ActiveDirectoryService;
private readonly MessageService MessageSerivce;
public AuthenticationFilter(IActiveDirectoryService activeDirectoryService, MessageService messageSerivce)
{
ActiveDirectoryService = activeDirectoryService;
MessageSerivce = messageSerivce;
}
I have it working with the default IOC container of dot net core 2 but I could not use Funq container to do it.
I am reading from appsettings.json (I think I read on the docs ServiceStack does not support this) and registering
var config = Configuration.GetSection("LdapAuth");
services.Configure<LdapAuthenticationOptions>(Configuration.GetSection("LdapAuth"));
services.AddActiveDirectoryService(options =>
Configuration.GetSection("LdapAuth"));//uses collection.AddTransient<IActiveDirectoryService, ActiveDirectoryService>()
services.AddMessageService(); //same as above
I can't think of a way to inject into the filter a parameterized service.
So this does not work at all because I don't have a default constructor.
public class AuthenticationFilter : ActionFilterAttribute
{
public IActiveDirectoryService ActiveDirectoryService {get; set;};
But this below at least should have worked. I'm not using an interface here though.
public class AuthenticationFilter : ActionFilterAttribute
{
public MessageService MessageService{get; set}; //notice not using interface here although this as default constructor.
Where this gets really ugly is then I have controllers that inherit ServiceStackController and I inject services using ResolveService from the Funq container.
public class MessageController : ServiceStackController
{
...
var messageService = ResolveService<MessageService>()
I re-register them.. like below.
public override void Configure(Funq.Container container)
{
SqlServerDialect.Provider.RegisterConverter<TimeSpan>(new ServiceStack.OrmLite.SqlServer.Converters.SqlServerTimeConverter
{
Precision = 7
});
var connectionString = GetConnectionString(AppSettings);
container.Register<IDbConnectionFactory>(
new OrmLiteConnectionFactory(connectionString, new SqlServerOrmLiteDialectProvider()));
LdapAuthenticationOptions options = GetLdapAuthenticationOptions(AppSettings); //notice now I have to read from AppSetting.. which is appsettings.txt file.
container.Register(c => new ActiveDirectoryService(options));
container.Register(c => new MessageService());
}
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());
}
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.
Im very new but on a quest to learn nhibernate and DI with structuremap so am making an app to test everything out.
I am having some problems injecting my unitofwork into my base repository. I create a unitofwork on every request then either commit or rollback at the end. But when tryin to inject into my base repository its always null when i go to use it in my methods. More specifically i get this error: "Object reference not set to an instance of an object". Any ideas what im doing wrong and if this is the correct route i should be going?
Many thanks in advance.
Base Repository:
public abstract class Repository<T> : IRepository<T>
{
public readonly IUnitOfWork _unitOfWork;
public Repository(IUnitOfWork UnitOfWork)
{
UnitOfWork = _unitOfWork;
}
public Repository() {}
public void Save(T obj)
{
_unitOfWork.CurrentSession.Save(obj);
}
}
StructureMap registry thats set in my application_start:
public class NHibernateRegistry : Registry
{
public NHibernateRegistry()
{
For<IUnitOfWork>().HybridHttpOrThreadLocalScoped().Use<UnitOfWork>();
For<ISession>().HybridHttpOrThreadLocalScoped().Use(context => context.GetInstance<ISessionFactory>().OpenSession());
For<ISessionFactory>().Singleton().Use(NHibernateSessionFactory.GetSessionFactory());
}
}
Update:
The baserepository is inherited by specific repositorys ie ArticleRepository then im injecting that into my aspx pages on application_start. Then my aspx page inherits a basepage where the buildUp takes place. And i access the repository like this:
public IArticleRepository ArticleRepo { get; set; }
public void SaveThing()
{
ArticleRepo.Save(object);
}
This gets called on application_start:
public class Bootstrapper
{
public static void BootStrap()
{
ObjectFactory.Configure(x =>
{
x.AddRegistry<NHibernateRegistry>();
x.AddRegistry<WebRegistry>();
});
}
}
And in the webregistry:
For<IArticleRepository>().Use<ArticleRepository>();
There is a good chance your ArticleRepository doesn't have a constructor that takes an IUnitOfWork as a parameter make sure you have the following
public class ArticleRepository : Repository<Article>
{
public ArticleRepository(IUnitOfWork unitOfWork) : base(unitOfWork)
{
}
...
}
Remember Strcuture map uses the greediest constructor on the class that it instantiates. Since your abstract repository class will never be instantiated it will not use its constructors. I would also suggest not having any default constructors if your classes need to have their dependencies injected that way. Less chance for errors that way.
public readonly IUnitOfWork _unitOfWork;
public Repository(IUnitOfWork UnitOfWork)
{
UnitOfWork = _unitOfWork;
}
I think you mean to write
public readonly IUnitOfWork _unitOfWork;
public Repository(IUnitOfWork UnitOfWork)
{
_unitOfWork = UnitOfWork;
}
You were assigning the local variable to the parameter instead of the parameter to the local variable.
Edit: Also you should write your parameter with a lowercase U 'unitOfWork' instead of 'UnitOfWork'