I created a web api only project with asp.net 5 and trying to set up my NHibernate session management. Everything seems to be fine but inside my controller when I try to use the session, I get a null exception although I mark it with NHibernateSession attribute.
I realized that the method does not use the session created by the attribute. What should I do to make it use that one ? Any suggestions are greatly appreciated.
public class ValuesController : Controller
{
private readonly IRepository _repository;
public ISessionContainer NHibernateSession { get; set; }
public ValuesController(IRepository repository)
{
_repository = repository;
}
// GET: api/values
[HttpGet, NHibernateSession]
public IEnumerable<string> Get()
{
return _repository.GetProducts(); // session is null exception
}
}
public class NHibernateSessionAttribute: ActionFilterAttribute
{
protected ISessionContainer sessionContainer;
public NHibernateSessionAttribute()
{
sessionContainer = new NHibernateSessionContainer();
}
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
sessionContainer.OpenSession();
}
public override void OnResultExecuted(ResultExecutedContext filterContext)
{
sessionContainer.CloseSession();
}
}
public class NHibernateSessionContainer : ISessionContainer
{
public static ISessionFactory SessionFactory = CreateSessionFactory();
private ISession _session;
private static ISessionFactory CreateSessionFactory()
{
return Fluently.Configure()
.Database(OracleClientConfiguration.Oracle10
.ConnectionString(ConfigurationManager.ConnectionStrings["Main.ConnectionString"].ConnectionString))
.Mappings(m => m.FluentMappings.AddFromAssembly(Assembly.GetExecutingAssembly()))
.BuildConfiguration().BuildSessionFactory();
}
public ISession Session
{
get { return _session; }
}
public void OpenSession()
{
_session = SessionFactory.OpenSession();
}
public void CloseSession()
{
Dispose();
}
public void Dispose()
{
_session.Dispose();
}
}
services.AddSingleton<ISessionContainer, NHibernateSessionContainer>();
services.AddTransient<IRepository, Repository>();
session is not enabled by default, did you add it via nuget and configure it for example:
app.UseSession();
app.UseInMemorySession(configure: s => s.IdleTimeout = TimeSpan.FromMinutes(20));
see also
Related
I have a FilterAttribute that has two parameters, one defined in dependency injection and one defined on method of controller as as string
public controller : ControllerBase
{
[MyFilter("Parameter1", FromDependency)]
public ActionResult MyMethod()
{
....
}
}
and the filter
public MyFilter : Attribute
{
MyFilter(string parameter1, context fromDependency)
{
}
}
How can I inject the parameter from dependency injection?
You can implement an IFilterFactory for this purpose. The runtime checks for this interface when creating filters and calls the CreateInstance method that gets an IServiceProvider as a parameter. You can use this provider to create services and inject them into the filter.
The following sample is taken from the docs:
public class ResponseHeaderFilterFactory : Attribute, IFilterFactory
{
public bool IsReusable => false;
public IFilterMetadata CreateInstance(IServiceProvider serviceProvider) =>
new InternalResponseHeaderFilter();
private class InternalResponseHeaderFilter : IActionFilter
{
public void OnActionExecuting(ActionExecutingContext context) =>
context.HttpContext.Response.Headers.Add(
nameof(OnActionExecuting), nameof(InternalResponseHeaderFilter));
public void OnActionExecuted(ActionExecutedContext context) { }
}
}
If you need to both use services from DI and values defined on the attribute, you can use the following approach:
public class ResponseHeaderFilterFactory : Attribute, IFilterFactory
{
private readonly string _attrParam;
public ResponseHeaderFilterFactory(string attrParam)
{
_attrParam = attrParam;
}
public bool IsReusable => false;
public IFilterMetadata CreateInstance(IServiceProvider serviceProvider)
{
var svc = serviceProvider.GetRequiredService<IMyService>();
return new InternalResponseHeaderFilter(_attrParam, svc);
}
private class InternalResponseHeaderFilter : IActionFilter
{
private readonly string _attrParam;
private readonly IMyService _service;
public InternalResponseHeaderFilter(string attrParam, IMyService service)
{
_attrParam = attrParam;
_service = service;
}
public void OnActionExecuting(ActionExecutingContext context) =>
context.HttpContext.Response.Headers.Add(
nameof(OnActionExecuting), nameof(InternalResponseHeaderFilter));
public void OnActionExecuted(ActionExecutedContext context) { }
}
}
You can then apply the filter like this:
public controller : ControllerBase
{
[ResponseHeaderFilterFactory("Parameter1")]
public ActionResult MyMethod()
{
....
}
}
You can implement ActionFilterAttribute to get DI dependencies from HttpContext.RequestServices:
public sealed class MyAttr : ActionFilterAttribute
{
MyAttr(string parameter1)
{
}
public override void OnActionExecuting(ActionExecutingContext context)
{
//Get dependency from HttpContext services
var myDependency = context.HttpContext.RequestServices.GetService<MyDependency>();
//Use it
myDependency.DoSomething();
//....
}
}
Injecting components into action filter attributes directly is not possible but there are various workarounds to allow us to effectively accomplish the same thing. Using ServiceFilter is a relatively clean way to allow dependency injection into individual action filters.
The ServiceFilter attribute can be used at the action or controller level. Usage is very straightforward:
[ServiceFilter(typeof(MyFilter))]
And our filter:
public class MyFilter: IActionFilter
{
MyFilter(string parameter1, context fromDependency)
{
}
}
Obviously, as we are resolving our filter from the IoC container, we need to register it:
public void ConfigureServices(IServiceCollection services)
{
...
services.AddScoped<MyFilter>(x =>
new Service(x.GetRequiredService<IOtherService>(),
"parameter1"));
...
}
more details in Paul Hiles article: here
I want to know if there is a better to way to handle this.
I've set up Unity for dependency injection for our project. The project itself is an ASP.NET application that uses Web API.
I have the following packages installed.
Unity
Unity.ASPNet.WebAPI
I see no option to close/dispose the DBContext right after fetching the data.
My controller
public class NinjasController : ApiController
{
public Ninja Get(int id)
{
INinjaRepository repository = UnityConfig.Container.Resolve(typeof(INinjaRepository), null) as INinjaRepository;
Ninja ninja = repository.GetNinjaById(id);
repository.CanBeDisposed = true;
repository = null;
UnityConfig.PerRequestLifetimeManager.Dispose();
return ninja;
}
}
UnityConfig
public static class UnityConfig
{
private static Lazy<IUnityContainer> container =
new Lazy<IUnityContainer>(() =>
{
var container = new UnityContainer();
RegisterTypes(container);
return container;
});
public static IUnityContainer Container => container.Value;
public static PerRequestLifetimeManager PerRequestLifetimeManager;
public static void RegisterTypes(IUnityContainer container)
{
PerRequestLifetimeManager = new PerRequestLifetimeManager();
container.RegisterType<INinjaRepository, NinjaRepository>(PerRequestLifetimeManager);
}
}
Lifetime Manager
public class PerRequestLifetimeManager : TransientLifetimeManager, IDisposable
{
private static List<IBaseRepository> list = new List<IBaseRepository>();
public override void SetValue(object newValue, ILifetimeContainer container = null)
{
base.SetValue(newValue, container);
IBaseRepository disposable = newValue as IBaseRepository;
if (disposable != null)
list.Add(disposable);
}
public void Dispose()
{
foreach (IBaseRepository item in list.FindAll(item => item.CanBeDisposed))
{
if (item != null)
{
try
{
item.Dispose();
}
catch (Exception)
{
// log exception and continue
}
}
}
list.RemoveAll(item => item.CanBeDisposed);
}
}
Repository
public class GenericRepository<TEntity> : IGenericRepository<TEntity> where TEntity : class
{
internal DbContext _context;
internal DbSet<TEntity> _dbSet;
public bool CanBeDisposed { get; set; }
public GenericRepository(DbContext context)
{
_context = context;
_dbSet = context.Set<TEntity>();
}
protected void Dispose(bool disposing)
{
if (disposing)
{
if (_context != null)
{
_context.Dispose();
_context = null;
}
}
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
First you might want to add one more Unity bootstrapper to your project Unity.AspNet.Mvc
https://msdn.microsoft.com/en-us/library/dn507440(v=pandp.30).aspx
To use the PerRequestLifetimeManager class in an ASP.NET Web API application, you must also add the the Unity bootstrapper for ASP.NET MVC NuGet package to your project.
Unity.Mvc and Unity.AspNet.WebApi will register your controllers for DI.
UnityConfig.cs
public static void RegisterTypes(IUnityContainer container)
{
container.RegisterType<INinjaContext, NinjaContext>(new PerRequestLifetimeManager());
container.RegisterType<INinjaRepository, NinjaRepository>(new PerRequestLifetimeManager());
}
UnityWebApiActivator.cs Uncomment the line...
public static void Start()
{
// Use UnityHierarchicalDependencyResolver if you want to use
// a new child container for each IHttpController resolution.
var resolver = new UnityHierarchicalDependencyResolver(UnityConfig.Container);
...
}
UnityMvcActivator.cs Uncomment the line...
public static void Start()
{
...
// TODO: Uncomment if you want to use PerRequestLifetimeManager
Microsoft.Web.Infrastructure.DynamicModuleHelper.DynamicModuleUtility.RegisterModule(typeof(UnityPerRequestHttpModule));
}
Your controller is simply
public class NinjasController : ApiController
{
private readonly INinjaRepository repository;
public NinjasController(INinjaRepository repository)
{
this.repository = repository;
}
public Ninja Get(int id)
{
var ninja = repository.GetNinjaById(id);
return ninja;
}
}
With PerRequestLifetimeManager Unity will take care of disposal after the request is complete.
I have an example here https://github.com/jasenhk/MovieStar
If you are using OWIN see Unity IoC does not inject dependency into Web API Controller
I am using FluentNHibernate and Unity to use in my asp.net webapi. I am using Unit of work pattern to configure the ISession.
I am getting error as "Session is closed! Object name: 'ISession'. Can anyone help what am i doing wrong here? Please find below my code.
unityconfig:
container.RegisterType<IUnitOfWork, UnitOfWork>(new ContainerControlledLifetimeManager());
public class UnitOfWork : IUnitOfWork
{
private static readonly ISessionFactory _sessionFactory;
private ITransaction _transaction;
public ISession Session { get; set; }
static UnitOfWork()
{
_sessionFactory = Fluently.Configure()
.Database(MsSqlConfiguration.MsSql2008.ConnectionString(x => x.FromConnectionStringWithKey("UnitOfWorkExample")))
.Mappings(x => x.AutoMappings.Add(
AutoMap.AssemblyOf<Product>(new AutomappingConfiguration()).UseOverridesFromAssemblyOf<ProductOverrides>()))
.ExposeConfiguration(config => new SchemaUpdate(config).Execute(false, true))
.BuildSessionFactory();
}
public UnitOfWork()
{
Session = _sessionFactory.OpenSession();
}
public void BeginTransaction()
{
_transaction = Session.BeginTransaction();
}
public void Commit()
{
try
{
if (_transaction != null && _transaction.IsActive)
_transaction.Commit();
}
catch
{
if (_transaction != null && _transaction.IsActive)
_transaction.Rollback();
throw;
}
finally
{
Session.Dispose();
}
}
public void Rollback()
{
try
{
if (_transaction != null && _transaction.IsActive)
_transaction.Rollback();
}
finally
{
Session.Dispose();
}
}
}
WebApi Action filter:
public class UnitOfWorkActionFilter : ActionFilterAttribute
{
public IUnitOfWork UnitOfWork { get; set; }
public override void OnActionExecuting(HttpActionContext actionContext)
{
UnitOfWork = actionContext.Request.GetDependencyScope().GetService(typeof(IUnitOfWork)) as IUnitOfWork;
UnitOfWork.BeginTransaction();
}
public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
{
UnitOfWork = actionExecutedContext.Request.GetDependencyScope().GetService(typeof(IUnitOfWork)) as IUnitOfWork;
if (actionExecutedContext.Exception == null)
{
// commit if no exceptions
UnitOfWork.Commit();
}
else
{
// rollback if exception
UnitOfWork.Rollback();
}
}
}
Use PerRequestLifetimeManager instead of ContainerControlledLifetimeManager and see. ContainerControlledLifetimeManager registers an existing object as a singleton instance which is not what you want.
see this for more info MSDN
I am trying to use the Unity IoC with the unit of work & repository patterns, however I'm struggling to work out how my repository would get it's Nhibernate session to perform it's work...
Below I have my IUnityContainer which does my IoC bindings.. I then have my NHibernate repo which needs to be provided with a ISession, which should be provided from the NHibernate unit of work, but I am now sure how I can tell Unity to provide it in it's constructor..
I have made a binding for the IUnitOfWork to a Nhib... one, but how does one pass that value in the IRepository RegisterType?
private static IUnityContainer BuildUnityContainer()
{
var container = new UnityContainer();
NHibernateHelper helper = new NHibernateHelper();
// register all your components with the container here
// e.g. container.RegisterType<ITestService, TestService>();
container.RegisterControllers();
container.RegisterType<IUnitOfWork, NHibernateUnitOfWork>(new HierarchicalLifetimeManager());
container.RegisterInstance<ISessionFactory>(helper.SessionFactory);
container.RegisterType<IRepository, NHibernateRepository>() ;
return container;
}
public class NHibernateRepository : IRepository
{
public NHibernateRepository(ISession session)
{
_session = session;
}
}
public class NHibernateUnitOfWork : IUnitOfWork
{
private readonly ISessionFactory _sessionFactory;
private readonly ITransaction _transaction;
public ISession Session { get; private set; }
public NHibernateUnitOfWork(ISessionFactory sessionFactory)
{
_sessionFactory = sessionFactory;
Session = _sessionFactory.OpenSession();
Session.FlushMode = FlushMode.Auto;
_transaction = Session.BeginTransaction(IsolationLevel.ReadCommitted);
}
}
Register your ISession with your IUnityContainer like so:
container.RegisterType<ISession>(new InjectionFactory(c => c.Resolve<ISessionFactory>().OpenSession());
Then when an ISession is required, an ISessionFactory will be resolved and a session started.
In the applications I work on, we define our repositories and unit of work like so:
public class NhUnitOfWork : IUnitOfWork
{
readonly ISession _session;
public IRepository<T> GetRepository<T>() where T : class
{
return new NhRepository<T>(_session);
}
public NhUnitOfWork(ISession session)
{
_session = session;
}
public void Dispose()
{
// Dispose logic, i.e. save/rollback
}
}
public class NhRepository<T> : IRepository<T> where T : class
{
readonly ISession _session;
public void Add(T item)
{
_session.Save(item);
}
public void Delete(T item)
{
_session.Delete(item);
}
public void Update(T item)
{
_session.Update(item);
}
public NhRepository(ISession session)
{
_session = session;
}
}
Then your services use it something like this:
public class MyService
{
readonly Func<IUnitOfWork> _unitOfWorkFactory;
public MyService(Func<IUnitOfWork> unitOfWorkFactory)
{
_unitOfWorkFactory = unitOfWorkFactory;
}
public void DoServiceStuff()
{
using(var uow = _unitOfWorkFactory())
{
var newUser = new User() { Username = "My User" };
var userRepo = uow.GetRepository<User>();
userRepo.Add(newUser);
uow.Save();
}
}
}
You want to create instance of "IRepository", you need to initialize mapping for all it dependencies. It's single: "ISession"
I am using NHibernate, and I have a BootStrapper class that has a method that returns an ISession. In my Repository class I am passing it an ISession Object. In my Controller, I am passing its constructor an IRepository object.
I have been successful in binding my IRepository to Repository class, but I can't figure out how to bind/register my Repository class so that it receives a ISession object from the BootStrapper class when it is instantiated and bind my controller to recieve a IRepository object when it is created.
My Code:
public interface IProductsRepository
{
IQueryable<Product> Products { get; }
void SaveProduct(Product product);
}
public class MySqlProductsRepository : IProductsRepository
{
private readonly ISession _session;
public MySqlProductsRepository(ISession session)
{
_session = session;
}
public IQueryable<Product> Products
{
get
{
return _session.CreateCriteria(typeof (Product)).List<Product>().AsQueryable();
}
}
public class BootStrapper
{
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();
}
}
public class AdminController : Controller
{
private readonly IProductsRepository _productsRepository;
public AdminController(IProductsRepository productsRepository)
{
_productsRepository = productsRepository;
}
}
public class NinjectControllerFactory : DefaultControllerFactory
{
private readonly IKernel _kernel = new StandardKernel(new DaisyBlossomsServices());
protected override IController GetControllerInstance(System.Web.Routing.RequestContext requestContext, Type controllerType)
{
if (controllerType == null)
return null;
return (IController) _kernel.Get(controllerType);
}
public class DaisyBlossomsServices : NinjectModule
{
public override void Load()
{
Bind<IProductsRepository>().To<MySqlProductsRepository>();
}
}
}
How do I bind a ISession to MySqlProductsRepository so it receives an ISession object for its constructor, and how do I bind it so my controller receives an IProductsRepository in its constructor?
you can bind ISession to a Provider. In this case you can change the BootStrapper class to inherit from Provider and implement the CreateInstance method.
That would look like that:
public class BootStrapper : Provider<ISession>
{
.....
protected override ISession CreateInstance(IContext context)
{
return GetSession();
}
....
}
After that just add the binding to your modul:
public class DaisyBlossomsServices : NinjectModule
{
public override void Load()
{
Bind<IProductsRepository>().To<MySqlProductsRepository>();
Bind<ISession>().ToProvider<BootStrapper>();
}
}
That should do it.