I am pretty new to castle and Ninject . how ever i have a statement where an Interface is initialised as factory method as like this
public class LazySessionContext
{
private readonly ISessionFactoryImplementor factory;
private const string CurrentSessionContextKey = "NHibernateCurrentSession";
public LazySessionContext(ISessionFactoryImplementor factory)
{
this.factory = factory;
}
}
Now when injecting ISessionFactoryImplementor as factory method we have done like this
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.Register(Component.For<ISessionFactoryProvider>().AsFactory());
container.Register(Component.For<IEnumerable<ISessionFactory>>()
.UsingFactoryMethod(k => k.ResolveAll<ISessionFactory>()));
}
can someone please mention how to achieve the same in ninject ? so as the ISessionFactoryProvider is provided and initialised as a factory method in ninject ?
kernel.Bind<ISessionFactoryProvider>().ToFactory();
is the equivalent. to this configuration. You have to use Ninject.Extensions.Factory. The IEnumerable configuration exists by default.
Related
Trying to use dependency injection with custom NHibernate implementation classes, specifically an IConnectionProvider. The issue is that I need my implementation of IConnectionProvider to use some dependency components ("services" in the .Net DI terminology). If the provider were managed by the DI container, I would just make the dependencies constructor args and be done.
For example:
public class MyConnectionProvider : ConnectionProvider
 {
public MyConnectionProvider(IServiceA serviceA, IServiceB serviceB) {
this.serviceA = serviceA;
this.serviceB = serviceB;
}
public override DbConnection GetConnection() {
// Use serviceA and serviceB to return a DbConnection...
}
}
But the mechanism by which NHibernate instantiates things like IConnectionProvider is reflection, not DI-aware. How can I make a custom impl like this, which requires cooperating services that are managed by the DI container?
The key is to implement the NHibernate interface IObjectsFactory There are some DI-aware implementations of that for Autofac, Spring, and Castle Windsor (discussed here and here). I ended up writing an implementation for Microsoft default DI that looks like this
public class MicrosoftDIObjectsFactory : IObjectsFactory
{
private Lazy<IServiceProvider> sp;
public MicrosoftDIObjectsFactory(Func<IServiceProvider> serviceProviderProvider)
{
sp = new Lazy<IServiceProvider>(serviceProviderProvider);
}
public object CreateInstance(Type type)
{
object instance = sp.Value.GetService(type);
if (instance != null) { return instance; }
return Activator.CreateInstance(type);
}
}
It would be pretty easy to add a cache of un-resolvable types to help performance (avoid repeated lookups in the IServiceProvider).
To set this up, during IServiceCollection initialization, I use code like this:
Environment.ObjectsFactory = new MicrosoftDIObjectsFactory(services.BuildServiceProvider)
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 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
I keep on getting an error when trying to resolve my repositories.
None of the constructors found with 'Public binding flags' on type can be invoked with the available services and parameters:
Cannot resolve parameter 'Repository.Data.INHibernateSession nHibernateSession' of constructor 'Void .ctor(Repository.Data.INHibernateSession)'.
Global.asax
builder.RegisterAssemblyTypes(assembly).Where(t => typeof(IDependency).IsAssignableFrom(t)).
AsImplementedInterfaces().InstancePerLifetimeScope();
builder.RegisterAssemblyTypes(assembly).Where(t => typeof(ISingletonDependency).IsAssignableFrom(t)).
AsImplementedInterfaces().SingleInstance();
builder.RegisterAssemblyTypes(assembly).Where(t => typeof(ITransientDependency).IsAssignableFrom(t)).
AsImplementedInterfaces().InstancePerDependency();
builder.RegisterGeneric(typeof(Repository<>)).As(typeof(IRepository<>)).InstancePerDependency();
then i have my singleton factory
public interface INhibernateFactory : ISingletonDependency
{
ISessionFactory SessionFactory { get; }
}
then my instance per lifetime
public interface INHibernateSession : IDependency
{
ISession Session { get; }
}
public class NHibernateSession : IDependency
{
private ISession _session;
private readonly ISessionFactory _sessionFactory;
public NHibernateSession(ISessionFactory sessionFactory)
{
_sessionFactory = sessionFactory;
}
then in my Generic repository
public class Repository<T> : IRepository<T> where T : class
{
private readonly INHibernateSession _nHibernateSession;
public Repository(INHibernateSession nHibernateSession)
{
_nHibernateSession = nHibernateSession;
}
public ISession Session
{
get { return _nHibernateSession.Session; }
}
It seems all i'm doing is creating a singleton, inject that to the session, then inject into repository. (all my other dependencies work fine)
Appreciate if someone could point me in the right direction why this wont resolve, I'm quite stumped?
The errors you get speaks for them selves, they state some type that cannot be resolved. After your latest change, this is the the 'ISessionFactory' interface which is missing.
To further elaborate: the current problem is that Autofac, when asked to build NHibernateSession instances, doesn't know how to provide an ISessionFactory instance. To "learn" the container how to build session factories it is not enough to provide the NHibernateFactory instance alone. Consider this registration:
builder.RegisterType<NhibernateFactory>().As<INhibernateFactory>().SingleInstance();
builder.Register(c => c.Resolve<INhibernateFactory>().SessionFactory);
The last line there registers a lambda that knows how to provide the session factory instance.
You'll need to provide the container with every type involved. Use the "no constructors..." error to see which types are missing.
To dig further into Autofac and NHibernate you'll have to search around the net. E.g. this question discusses NHibernate sessions:
Managing NHibernate ISession with Autofac
I am not familiar with AutoFac; but does your NHibernateSession class have to implement your INHibernateSession interface to resolve correctly?
Matt
In my MVC project, I have setup my MvcApplication_start() :
ControllerBuilder.Current.SetControllerFactory(new NinjectControllerFactory());
And have successfully bound an .To regarding my IProductsRepository to MySqlProductsRepository:
public class NinjectControllerFactory : DefaultControllerFactory
{
private readonly IKernel _kernel = new StandardKernel(new MyServices());
protected override IController GetControllerInstance(System.Web.Routing.RequestContext requestContext, Type controllerType)
{
if (controllerType == null)
return null;
return (IController) _kernel.Get(controllerType);
}
public class MyServices: NinjectModule
{
public override void Load()
{
Bind<IProductsRepository>().To<MySqlProductsRepository>();
}
}
}
But I am using NHibernate, and have a separate Session Factory class that has a GetSession() method that returns an ISession.
public static ISessionFactory SessionFactory = CreateSessionFactory();
private static ISessionFactory CreateSessionFactory()
{
var cfg = new Configuration().Configure(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "nhibernate.config"));
cfg.SetProperty(NHibernate.Cfg.Environment.ConnectionStringName, System.Environment.MachineName);
NHibernateProfiler.Initialize();
return cfg.BuildSessionFactory();
}
public static ISession GetSession()
{
return SessionFactory.GetCurrentSession();
}
I wanted to set it up so that my MySqlProductsRepository would be passed and ISession object by Ninject when it was created:
public class MySqlProductsRepository : IProductsRepository
{
private readonly ISession _session;
public MySqlProductsRepository(ISession session)
{
_session = session;
}
And my Controller would be handed a IProductsRepository instance:
public class AdminController : Controller
{
private readonly IProductsRepository _productsRepository;
public AdminController(IProductsRepository productsRepository)
{
_productsRepository = productsRepository;
}
MY PROBLEM:
I can't seem to figure out in my IoC container where I bind my IProductsRepository to my Repository, how to register an ISession, how to hand an ISession to my MyProductsRepository object when it is created, and hand an MyProductsRepository object to my Controller?
I have a couple of blog post I wrote that explain how to use Ninject in and ASP.NET MVC application. The application in the blog post uses the same technologies that you are using: Ninject, NHibernate, and MySql. I also am using a repository pattern. There are a lot of parallels between what you are doing and these posts.
http://blog.bobcravens.com/2010/07/using-nhibernate-in-asp-net-mvc/
http://blog.bobcravens.com/2010/06/the-repository-pattern-with-linq-to-fluent-nhibernate-and-mysql/
http://blog.bobcravens.com/2010/09/the-repository-pattern-part-2/
http://blog.bobcravens.com/2010/11/using-ninject-to-manage-critical-resources/
Take a look. If you have questions, feel free to contact me.