I have a lot of services that require access to the current user.
I set up a PrincipalProvider which bound to the current HttpContext, I also registered it per request so that once instantiated, it would never lose the context.
It looks like this:
public class PrincipalProvider : IPrincipalProvider
{
// Readonly fields
private readonly HttpContext _current;
/// <summary>
/// Default constructor
/// </summary>
public PrincipalProvider()
{
_current = HttpContext.Current;
}
/// <summary>
/// Gets the current user
/// </summary>
public IPrincipal User => _current?.User;
}
And I have bound it like this:
builder.RegisterType<PrincipalProvider>().As<IPrincipalProvider>().InstancePerRequest();
Now, any service that has this injected into it should have the user exposed. The issue I have is simple. If I have a controller set up like this:
public OrdersController(IOrderProvider provider)
{
_provider = provider;
}
That IOrderProvider has the IPrincipleProvider lazily injected into it, so in this case the user is available because the parent service is created on the Controller. The issue is if it isn't injected into the service, but instead used on another service that is injected into it.
For example, I have this controller:
public UsersController(IUserProvider provider, IAdvancedEncryptionStandardProvider encyptionProvider, IPhotoManager photoManager)
{
_userProvider = provider;
_encryptionProvider = encyptionProvider;
_photoManager = photoManager;
}
The IUserProvider doesn't have the IPrincipalProvider injected into it, but it does have another service that does:
public TroposSessionRequest(CormarConfig config, IAdvancedEncryptionStandardProvider encryptionProvider, Lazy<IPrincipalProvider> principalProvider)
{
_config = config;
_encryptionProvider = encryptionProvider;
_principalProvider = principalProvider;
}
The problem here, is that when the TroposSessionRequest is created, the HttpContext is not available and therefor is null.
I was hoping there was a way to instantiate the IPrincipleProvider when the context becomes available and then keep it for the entire request.
We can do this for every request as the PrincipalProvider is almost always needed.
Does anyone know how to do this?
The simplest way to workaround the problem is to simply access HttpContext.Current in your User accessor:
public IPrincipal User => HttpContext.Current?.User;
If you're concerned about testability, you can mock HttpContext.Current.User relatively easily.
If you're set on solving it using DI you could abstract HttpContext behind a IHttpContextAccessor dependency (but since you're still depending on the HttpContext type you'd still be in the same boat in terms of testing).
public class PrincipalProvider : IPrincipalProvider
{
// Readonly fields
private readonly IHttpContextAccessor _httpContextAccessor;
/// <summary>
/// Default constructor
/// </summary>
public PrincipalProvider(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
/// <summary>
/// Gets the current user
/// </summary>
public IPrincipal User => _httpContextAccessor.HttpContext?.User;
}
Related
I'm thinking that perhaps my entire idea of how to approach this is wrong, so let me explain what I'm trying to do.
I have a UserId that is a property contained within my JWT token.
On many of my REST endpoints, I need to read that UserId to use it within my DB queries.
I implemented a filter which intercepts all of my calls and decodes my JWT and assigns the UserId value into a static Globals class that I had created.
I just realised now though, that that class is GLOBAL. As in, the values are actually shared across the entire server for anybodies REST requests.
I intended for the value to essentially just be transiently available for the duration of each individual request.
How can I change my implementation so that I can globally access the UserId contained in the JWT token for the current request.
My suggestion is to make some kind of abstraction e.g ICurrentUser and make an implementation, which will take UserId from HttpContext.
// Define in Domain/Application project
public interface ICurrentUser
{
public string? Id { get; set; }
}
// Implement in ASP.NET project
public class CurrentUser : ICurrentUser
{
public CurrentUser(IHttpContextAccessor contextAccessor)
{
var user = contextAccessor.HttpContext?.User;
if (user == null)
{
return;
}
Id = user.FindFirstValue(ClaimTypes.NameIdentifier) ?? user.FindFirstValue(JwtClaimTypes.Subject);
}
public string? Id { get; set; }
}
Also, don't forget to add .AddHttpContextAccessor() call for you services
If you want something to be available for the duration of an individual request I would recommend using a service registered as scoped see Scoped Services
But lets start from the beginning. First implement a service itself like:
public UserService : IUserService
{
private readonly IHttpContextAccessor _accessor;
/// inject the `IHttpContextAccessor` to access the actual
/// request / token / headers etc.
public UserService(IHttpContextAccessor accessor)
{
_accessor = accessor;
}
public async Task<string> GetUserIdAsync()
{
var userId = await GetUserIdFromTokenAsync();
return userId;
}
private Task<string> GetUserIdFromTokenAsync()
{
/// Add your logic here to get or parse the
/// user id from the token or do some other stuff to get the user id.
/// ... or get the user id from the current User object claim
/// depends on your auth settings `_accessor.HttpContext?.User`
var token = _accessor... // from headers?
return userId;
}
}
/// Always use an interface to make it well testable and mockable for unit tests
public interface IUserService
{
Task<string> GetUserIdAsync();
}
Then in your dependency injection part (Startup.cs or Program.cs depends which tempate you have selected).
/// register the `IHttpContextAccessor` to be able to inject it.
services.AddHttpContextAccessor();
/// register your `UserService` as scoped!
services.AddScoped<IUserService, UserService>();
Now you can use this in all your services and controllers (which are at least also registered as scoped). This will resolve the service per request.
/// In a data service
class YourDataService
{
private readonly IUserService _userService;
/// Inject the `IUserService` wherever you need it now to
/// receive the current user Id.
public YourDataService(IUserService service)
{
_userService = service
}
public async Task DoYourQueryStuffAsync()
{
var userId = await _userService.GetUserIdAsync();
/// Your application logic with the provided userId
///
}
}
/// The same applies for a controller
[ApiController]
[Route("values")]
public class ValuesController : ControllerBase
{
private readonly IUserService _userService;
/// Inject the `IUserService` wherever you need it now to
/// receive the current user Id.
public ValuesController(IUserService service)
{
_userService = service
}
[Authorized]
[HttpGet]
public async Task<IActionResult> Query()
{
var userId = await _userService.GetUserIdAsync();
/// Your application logic with the provided userId
///
var queryresult = await ...
return Ok(queryresult);
}
}
Notes at the end:
Do not fall into the trap to consume scoped services from a singleton service this is not working because singletons are persistent without the request context.
Documentation links:
ASP.net Core Dependency Injection
UserId in Bearer
I am using Autofac in my project and for the most part it works fine. A while ago, I needed to get access to the current user and was told the best way to do this was to create a wrapper class like this:
public class PrincipalProvider : IPrincipalProvider
{
public IPrincipal User => HttpContext.Current?.User;
}
This has worked without any issues through my application.
I now have a new provider which looks like this:
public class WmsProvider : IWmsProvider
{
private readonly Lazy<ILogProvider> _logProvider;
private readonly Lazy<IMessageProvider> _messageProvider;
private readonly CormarConfig _config;
private readonly ClaimsIdentity _identity;
public WmsProvider(IPrincipalProvider principalProvider, Lazy<ILogProvider> logProvider, Lazy<IMessageProvider> messageProvider)
{
_messageProvider = messageProvider;
_logProvider = logProvider;
_identity = (ClaimsIdentity)principalProvider.User.Identity;
}
/// <summary>
/// Sends the order to WMS
/// </summary>
/// <param name="model">The order model</param>
public async Task SendAsync(OrderViewModel model)
{
var request = WmsFactory.Create(model);
await _logProvider.Value.TraceAsync($"This is a test", _identity);
await _messageProvider.Value.CreateAsync(request, model.OrderNumber, MessageType.Wms, "ORD", Method.POST, null);
}
}
(I have stripped out the rest of the code for brevity)
In this case, the User is null and throws an error (Object instance not found).
But I have another class with a similar constructor:
public OrderProvider(CormarConfig config, IOrderService orderSerivce, IPrincipalProvider principalProvider, Lazy<IAccountProvider> accountProvider, Lazy<ICollectionManagerProvider> collectionManagerProvider, Lazy<IEmailProvider> emailProvider, Lazy<IJournalProvider> journalProvider, Lazy<IOrderLineProvider> orderLineProvider, Lazy<IStockProvider> stockProvider, Lazy<webServices> webService, Lazy<ITroposOrderLineService> troposOrderLineService, Lazy<ITroposOrderService> troposOrderService, Lazy<ITroposUnitOfWork> troposUnitOfWork, Lazy<IWmsProvider> wmsProvider)
{
//Assign our config
_config = config;
// Add our services to our class
_connectionType = config.ConnectionType;
_orderService = orderSerivce;
// Add our providers to our class
_identity = (ClaimsIdentity)principalProvider.User.Identity;
// Add our optional providers
_accountProvider = accountProvider;
_collectionManagerProvider = collectionManagerProvider;
_emailProvider = emailProvider;
_journalProvider = journalProvider;
_orderLineProvider = orderLineProvider;
_stockProvider = stockProvider;
_webService = webService;
_wmsProvider = wmsProvider;
_troposOrderLineService = troposOrderLineService;
_troposOrderService = troposOrderService;
_troposUnitOfWork = troposUnitOfWork;
}
And this works fine.
Both are registered the same way in my Module:
builder.RegisterType<OrderProvider>().As<IOrderProvider>().InstancePerRequest();
builder.RegisterType<WmsProvider>().As<IWmsProvider>().InstancePerRequest();
builder.RegisterType<PrincipalProvider>().As<IPrincipalProvider>();
One thing to note, is that WmsProvider is injected into OrderProvider, so it is not directly injected into the controller. The controller constructor looks like this:
public OrdersController(IOrderProvider provider)
{
_provider = provider;
}
This might be where the issue lies. In a dependent, is the context not available? If it isn't what is the solution? Is there a way to get to the context from the child?
Any help would be appreciated.
I figured this out. It was as I said, the context was not available in the nested classes, so to fix this I changed the registration of the PrincipalProvider to instance per request:
builder.RegisterType<PrincipalProvider>().As<IPrincipalProvider>().InstancePerRequest();
And I changed the PrincipalProvider to look like this:
public class PrincipalProvider : IPrincipalProvider
{
// Readonly fields
private readonly HttpContext _current;
/// <summary>
/// Default constructor
/// </summary>
public PrincipalProvider()
{
_current = HttpContext.Current;
}
/// <summary>
/// Gets the current user
/// </summary>
public IPrincipal User => _current?.User;
}
And this fixed my issue.
Creating Dependency Injection with ASP.NET Core is fairly easy. The documentation explains it very well here and this guy has a killer video to explain it.
However, I want to do the same thing with my ASP.NET MVC 5 project. How can handle dependency injection with ASP.MVC 5?
Also, is Dependency injection limited to controllers only or can it work with any class?
In ASP.Net MVC you can use the .Net Core DI from NuGet rather than one of the third-party alternatives:-
using Microsoft.Extensions.DependencyInjection
For the MVC Start/Configuration class:-
public void Configuration(IAppBuilder app)
{
// We will use Dependency Injection for all controllers and other classes, so we'll need a service collection
var services = new ServiceCollection();
// configure all of the services required for DI
ConfigureServices(services);
// Configure authentication
ConfigureAuth(app);
// Create a new resolver from our own default implementation
var resolver = new DefaultDependencyResolver(services.BuildServiceProvider());
// Set the application resolver to our default resolver. This comes from "System.Web.Mvc"
//Other services may be added elsewhere through time
DependencyResolver.SetResolver(resolver);
}
My project uses Identity User and I've replaced the OWIN start-up configuration to follow a service-based approach instead. The default Identity User classes use static factory methods to create instances. I've moved that code into the constructors and relied on DI to provide the appropriate injection. It is still work in progress but here is where I am at:-
public void ConfigureServices(IServiceCollection services)
{
//====================================================
// Create the DB context for the IDENTITY database
//====================================================
// Add a database context - this can be instantiated with no parameters
services.AddTransient(typeof(ApplicationDbContext));
//====================================================
// ApplicationUserManager
//====================================================
// instantiation requires the following instance of the Identity database
services.AddTransient(typeof(IUserStore<ApplicationUser>), p => new UserStore<ApplicationUser>(new ApplicationDbContext()));
// with the above defined, we can add the user manager class as a type
services.AddTransient(typeof(ApplicationUserManager));
//====================================================
// ApplicationSignInManager
//====================================================
// instantiation requires two parameters, [ApplicationUserManager] (defined above) and [IAuthenticationManager]
services.AddTransient(typeof(Microsoft.Owin.Security.IAuthenticationManager), p => new OwinContext().Authentication);
services.AddTransient(typeof(ApplicationSignInManager));
//====================================================
// ApplicationRoleManager
//====================================================
// Maps the rolemanager of identity role to the concrete role manager type
services.AddTransient<RoleManager<IdentityRole>, ApplicationRoleManager>();
// Maps the role store role to the implemented type
services.AddTransient<IRoleStore<IdentityRole, string>, RoleStore<IdentityRole>>();
services.AddTransient(typeof(ApplicationRoleManager));
//====================================================
// Add all controllers as services
//====================================================
services.AddControllersAsServices(typeof(Startup).Assembly.GetExportedTypes()
.Where(t => !t.IsAbstract && !t.IsGenericTypeDefinition)
.Where(t => typeof(IController).IsAssignableFrom(t)
|| t.Name.EndsWith("Controller", StringComparison.OrdinalIgnoreCase)));
}
The Account Controller class has the single constructor:-
[Authorize]
public class AccountController : Controller
{
private ApplicationSignInManager _signInManager;
private ApplicationUserManager _userManager;
private RoleManager<IdentityRole> _roleManager;
public AccountController(ApplicationUserManager userManager, ApplicationSignInManager signInManager, RoleManager<IdentityRole> roleManager)
{
UserManager = userManager;
SignInManager = signInManager;
RoleManager = roleManager;
}
}
My Default Dependency Resolver:
/// <summary>
/// Provides the default dependency resolver for the application - based on IDependencyResolver, which hhas just two methods
/// </summary>
public class DefaultDependencyResolver : IDependencyResolver
{
/// <summary>
/// Provides the service that holds the services
/// </summary>
protected IServiceProvider serviceProvider;
/// <summary>
/// Create the service resolver using the service provided (Direct Injection pattern)
/// </summary>
/// <param name="serviceProvider"></param>
public DefaultDependencyResolver(IServiceProvider serviceProvider)
{
this.serviceProvider = serviceProvider;
}
/// <summary>
/// Get a service by type - assume you get the first one encountered
/// </summary>
/// <param name="serviceType"></param>
/// <returns></returns>
public object GetService(Type serviceType)
{
return this.serviceProvider.GetService(serviceType);
}
/// <summary>
/// Get all services of a type
/// </summary>
/// <param name="serviceType"></param>
/// <returns></returns>
public IEnumerable<object> GetServices(Type serviceType)
{
return this.serviceProvider.GetServices(serviceType);
}
}
For this answer I downloaded a Microsoft Example of WebApi project as a basis for the example and added DI services to it as follows,
Update the Target Framework to 4.6.1
NuGet the DI package :- Microsoft.Extensions.DependencyInjection
After the standard MapHttpRoute configuration, add code to register which services you need
using's
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Http;
using Microsoft.Extensions.DependencyInjection;
using System.Web.Http.Dependencies;
using ProductsApp.Controllers;
WebApiConfig
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
// create the DI services and make the default resolver
var services = new ServiceCollection();
services.AddTransient(typeof(DefaultProduct));
services.AddTransient(typeof(ProductsController));
var resolver = new MyDependencyResolver(services.BuildServiceProvider());
config.DependencyResolver = resolver;
}
}
DefaultProduct
public class DefaultProduct : ProductsApp.Models.Product
{
public DefaultProduct()
{
this.Category = "Computing";
this.Id = 999;
this.Name = "Direct Injection";
this.Price = 99.99M;
}
}
MyDependencyResolver
/// <summary>
/// Provides the default dependency resolver for the application - based on IDependencyResolver, which hhas just two methods.
/// This is combined dependency resolver for MVC and WebAPI usage.
/// </summary>
public class MyDependencyResolver : System.Web.Mvc.IDependencyResolver, System.Web.Http.Dependencies.IDependencyResolver
{
protected IServiceProvider serviceProvider;
protected IServiceScope scope = null;
public MyDependencyResolver(IServiceProvider serviceProvider)
{
this.serviceProvider = serviceProvider;
}
public MyDependencyResolver(IServiceScope scope)
{
this.scope = scope;
this.serviceProvider = scope.ServiceProvider;
}
public IDependencyScope BeginScope()
{
return new MyDependencyResolver(serviceProvider.CreateScope());
}
public void Dispose()
{
Dispose(true);
}
protected virtual void Dispose(bool disposing)
{
scope?.Dispose();
}
public object GetService(Type serviceType)
{
return this.serviceProvider.GetService(serviceType);
}
public IEnumerable<object> GetServices(Type serviceType)
{
return this.serviceProvider.GetServices(serviceType);
}
}
ServiceProviderExtensions
public static class ServiceProviderExtensions
{
public static IServiceCollection AddControllersAsServices(this IServiceCollection services, IEnumerable<Type> serviceTypes)
{
foreach (var type in serviceTypes)
{
services.AddTransient(type);
}
return services;
}
}
I then amended the existing controller to take the DI type (note there is just the one ctor)
using ProductsApp.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
namespace ProductsApp.Controllers
{
public class ProductsController : ApiController
{
DefaultProduct _dp = null;
public ProductsController(DefaultProduct dp)
{
_dp = dp;
//
products.Add(dp);
}
List<Product> products = new List<Product>()
{
new Product { Id = 1, Name = "Tomato Soup", Category = "Groceries", Price = 1 },
new Product { Id = 2, Name = "Yo-yo", Category = "Toys", Price = 3.75M },
new Product { Id = 3, Name = "Hammer", Category = "Hardware", Price = 16.99M }
};
public IEnumerable<Product> GetAllProducts()
{
return products;
}
public IHttpActionResult GetProduct(int id)
{
var product = products.FirstOrDefault((p) => p.Id == id);
if (product == null)
{
return NotFound();
}
return Ok(product);
}
}
}
My Default Dependency Resolver
/// <summary>
/// Provides the default dependency resolver for the application - based on IDependencyResolver, which hhas just two methods
/// </summary>
public class DefaultDependencyResolver : IDependencyResolver
{
/// <summary>
/// Provides the service that holds the services
/// </summary>
protected IServiceProvider serviceProvider;
/// <summary>
/// Create the service resolver using the service provided (Direct Injection pattern)
/// </summary>
/// <param name="serviceProvider"></param>
public DefaultDependencyResolver(IServiceProvider serviceProvider)
{
this.serviceProvider = serviceProvider;
}
/// <summary>
/// Get a service by type - assume you get the first one encountered
/// </summary>
/// <param name="serviceType"></param>
/// <returns></returns>
public object GetService(Type serviceType)
{
return this.serviceProvider.GetService(serviceType);
}
/// <summary>
/// Get all services of a type
/// </summary>
/// <param name="serviceType"></param>
/// <returns></returns>
public IEnumerable<object> GetServices(Type serviceType)
{
return this.serviceProvider.GetServices(serviceType);
}
}
I recommend you use Autofac, there are anothers fwk like unity, ninject, the benchmarks autofac has excelent perfomance.
http://www.palmmedia.de/blog/2011/8/30/ioc-container-benchmark-performance-comparison
Here is the integration with MVC (and works with all class)
http://docs.autofac.org/en/latest/integration/mvc.html
The simplest way to implements Dependency Injection in ASP.NET MVC 5 is to use the tool developed by Microsoft itself, called Unity.
You can find many resources on the internet about it, and you can start by reading the official documentation available here: Developer's Guide to Dependency Injection Using Unity
Also, is Dependency injection limited to controllers only or can it work with any class?
It works with any class, in any project, as long as you register the Interface related to the Implementation (if you want to take profit of the IoC pattern), all you have to do then is to add the Interface instantiation in your constructor.
In this video a Microsoft MVP demos dependency injection in MVC5 with AutoFac. Very clear explanation on how to set it up:
Dependency Injection MVC5 Demo
Source code is available on GitHub
From here https://scottdorman.blog/2016/03/17/integrating-asp-net-core-dependency-injection-in-mvc-4/
this line saved me.
services.AddControllersAsServices(typeof(Startup).Assembly.GetExportedTypes()
.Where(t => !t.IsAbstract && !t.IsGenericTypeDefinition)
.Where(t => typeof(IController).IsAssignableFrom(t)
|| t.Name.EndsWith("Controller", StringComparison.OrdinalIgnoreCase)));
I recommend using Windsor, by installing the nuget package Castle Windsor MVC Bootstrapper, then you can create a service that implements IWindsorInstaller, something like this:
public class ServiceRegister : IWindsorInstaller
{
public void Install(Castle.Windsor.IWindsorContainer container,
Castle.MicroKernel.SubSystems.Configuration.IConfigurationStore store)
{
SomeTypeRequiredByConstructor context = new SomeTypeRequiredByConstructor ();
container.Register(
Component
.For<IServiceToRegister>()
.ImplementedBy<ServiceToRegister>().
DependsOn(Dependency.OnValue<SomeTypeRequiredByConstructor>(context))//This is in case your service has parametrize constructoe
.LifestyleTransient());
}
}
Then inside your controller something like this:
public class MyController
{
IServiceToRegister _serviceToRegister;
public MyController (IServiceToRegister serviceToRegister)
{
_serviceToRegister = serviceToRegister;//Then you can use it inside your controller
}
}
And by default the library will handle sending the right service to your controller by calling the install() of ServiceRegister at start up because it implements IWindsorInstaller
Having started from this thread to figure out how to use Microsoft.Extensions.DependencyInjection in my ASP.NET MVC 5 project, and reading and trying and failing, I finally came up with a solution that I wanted to shamelessly offer to the rest of you.
I pieced together a gist from David Fowler, the example code from Scott Dorman, and added in a bit of my own spice to create library that allows you to simulate ASP.NET Core's Startup in ASP.NET MVC "Classic".
For more information, please take a look at the GitHub repository for Arex388.AspNet.Mvc.Startup. If you're interested you can also read through my blog post about it, here (if it doesn't load, refresh until it does, the server's been giving me troubles and I haven't had time to investigate...). Hope it helps someone!
I'm looking at Microsoft Unity injection for a webapi we've based on the full stack code i found here;
Full Stack WebApi GitHub Repo
Now when I run the code and hit the patients controller in this case, the
[Dependency]
public PatientService PatientService { get; set; }
is null.
Now the patient service is the entity having the context injected into its constructor.
public class PatientService : BaseService
{
private readonly Func<IUCareDataContext> _contextFactory;
/// <summary>
/// Initializes a new instance of the <see cref="PatientService"/> class.
/// </summary>
/// <param name="contextFactory">The context factory.</param>
public PatientService(Func<IUCareDataContext> contextFactory)
{
this._contextFactory = contextFactory;
}
From looking at the Unity documentation and UnityConfig.cs I can see that the Register is completed, but no Resolve?
namespace CCS.Services.WebApi
{
using CCS.Data.UCare;
using Data;
using Microsoft.Practices.Unity;
using System.Web.Http;
using Unity.WebApi;
public static class UnityConfig
{
public static void RegisterComponents()
{
var container = new UnityContainer();
container.RegisterType<IUCareDataContext, UCareDataContext>();
GlobalConfiguration.Configuration.DependencyResolver = new UnityDependencyResolver(container);
}
}
}
Unity [Dependency] Attribute MSDN
Is this implementation missing something or should the dependency injection just work automatically when the controller is instantiated?
As it was pointed out I could inject the service. But this would then mean I would have to register every service to unity, with all services ultimately having the same context interface being injected to it. I would have thought there is a way around this?
I know the question of session management has been brought up in the past, but I could not find anything that helps me overcome my problem..
I have a number of repository classes (e.g CustomerRepository, ProductRepository etc.) which I resolve through Castle Windsor (Note: I am trying to apply the three calls pattern as outlined here). I figure I'd best have a session per Presenter (in my case, this is equivalent to one per form), however, the repository classes need to access the session for the currently active form.. I am not sure how I incorporate this with the fact that these repositories are resolved through windsor, since presenters are not singletons..
For example:
public class SomePresenter
{
private ISomeView view;
private ISession session;
private ICustomerRepository customerRepository;
private IOrderRepository orderRepository;
public SomePresenter(ISomeView view, ISessionFactory sessionFactory, ICustomerRepository customerRepository, IOrderRepository orderRepository)
{
this.view = view;
this.session = sessionFactory.OpenSession();
this.customerRepository = customerRepository;
this.orderRepository = orderRepository;
}
}
The repositories needs access to the session... How do I go about this using Windsor? Am I forced to manually set the session on the repositories through a property, or is there a clever Windsor trick that I'm unfamiliar with?
Why not just inject an ISession into your repositories instead of an ISessionFactory?
Here is the similar code that I use with Autofac, a different IoC container:
containerBuilder
.Register(c => NHibernateContext.GetSessionFactory().OpenSession())
.As<ISession>()
.InstancePerLifetimeScope();
where NHibernateContext is my one and only static class that configures NHibernate and holds onto an ISessionFactory singleton.
So my repository/lookup object asks for a session:
public MyRepository(ISession session)
{
this.session = session;
}
Then my Presenter/View Model/Superivsing Controller/Whatever-The-Heck-We're-Calling-It-This-Month just gets the repository or lookup object:
public MyPresenter(IWhateverRepository repository)
{
// Look ma, the repository has an ISession and I'm none the wiser!
}
For Windsor, I think (I'm not terribly familiar with its API, you may have to tweak this but it should give you an idea) it would be something like
container.Register(
Component.For<ISession>
.UsingFactoryMethod(
x => x.Resolve<ISessionFactory>().OpenSession())
.LifeStyle.Transient);
That is, you tell the container, "When somebody asks for an ISession, run this little delegate that gets the ISessionFactory and opens a session, then give them that ISession instance."
But who closes the ISession? It's up to you: you could have the repository explicitly close the ISession in its own Dispose() method. Or you could rely on your container to do the closing and disposing; in Autofac, I do this with ILifetimeScope and InstancePerLifetimeScope(); in Windsor, I believe you need to look up nested containers, such that when you dispose a child container, all of the components it created are also disposed.
In my experience, this usually means that the container leaks into at least the "main form" of my application: when it's time to create a form, it creates a new lifetime scope/nested container and shows the form. But nothing below this level knows about the container; it's just to throw a lasso around a set of components and say "get rid of all of these when the form is closed."
(This is to prevent just one big honking ISession from being used throughout most of the application. That works fine in ASP.NET, one session per request, but in Windows Forms, as you note, it is like a ticking time bomb for stale object exceptions. Better for each "unit of work" (typically, each form or service) to have its own ISession.)
You could alternatively design your repositories such that each method requires an ISession to be passed in, but that seems like it'd get tedious.
Hope that gives you some ideas. Good luck!
Why not just have one SessionProvider with individual Data Access Objects (DAO) for each presenter/controller? Your model is accessed through each Data Access Object.
public sealed class SessionProvider
{
static readonly SessionProvider provider = new SessionProvider();
private static NHibernate.Cfg.Configuration config;
private static ISessionFactory factory;
static ISession session = null;
/// <summary>
/// Initializes the <see cref="SessionProvider"/> class.
/// </summary>
static SessionProvider() { }
/// <summary>
/// Gets the session.
/// </summary>
/// <value>The session.</value>
public static ISession Session
{
get
{
if (factory == null)
{
config = new NHibernate.Cfg.Configuration();
config.Configure();
factory = config.BuildSessionFactory();
}
if (session == null)
{
if (config.Interceptor != null)
session = factory.OpenSession(config.Interceptor);
else
session = factory.OpenSession();
}
return session;
}
}
}
public sealed class OrderDataControl
{
private static ILog log = LogManager.GetLogger(typeof(OrderDataControl));
private static OrderDataControl orderDataControl;
private static object lockOrderDataControl = new object();
/// <summary>
/// Gets the thread-safe instance
/// </summary>
/// <value>The instance.</value>
public static OrderDataControl Instance
{
get
{
lock (lockOrderDataControl)
{
if (orderDataControl == null)
orderDataControl = new OrderDataControl();
}
return orderDataControl;
}
}
/// <summary>
/// Gets the session.
/// </summary>
/// <value>The session.</value>
private ISession Session
{
get
{
return SessionProvider.Session;
}
}
/// <summary>
/// Saves the specified contact.
/// </summary>
/// <param name="contact">The contact.</param>
/// <returns></returns>
public int? Save(OrderItems contact)
{
int? retVal = null;
ITransaction transaction = null;
try
{
transaction = Session.BeginTransaction();
Session.SaveOrUpdate(contact);
if (transaction != null && transaction.IsActive)
transaction.Commit();
else
Session.Flush();
retVal = contact.Id;
}
catch (Exception ex)
{
log.Error(ex);
if (transaction != null && transaction.IsActive)
transaction.Rollback();
throw;
}
return retVal;
}