Ninject UOW pattern, new ConnectionString after user is authenticated - c#

I wonder if any one can point me in the right direction?
I am using the UOW repository pattern and have my dependencies injected via Ninject. I have a UnitOfWorkMapping class which inherits from NinjectModule, which I use to bind my IUnitOfWork to a concrete implementation of Dbcontext, see below
public class UnitOfWorkMapping : NinjectModule
{
public override void Load()
{
Bind<IUnitOfWork>()
.To<WebsiteDbContext>()
.InRequestScope()
.WithConstructorArgument(
"connectionString",
ConfigurationManager.ConnectionStrings[ConnectionStringKeys.UnauthorisedUser]
.ConnectionString);
// Objects that explicitly need a DB context for the life of the request
Bind<IUnitOfWorkInRequestScope>()
.To<WebsiteDbContext>()
.InRequestScope()
.WithConstructorArgument(
"connectionString",
ConfigurationManager.ConnectionStrings[ConnectionStringKeys.UnauthorisedUser]
.ConnectionString);
// Objects that specificall need a DB context for the life of the application
Bind<IUnitOfWorkInApplicationScope>()
.To<WebsiteDbContext>()
.InSingletonScope()
.WithConstructorArgument(
"connectionString",
ConfigurationManager.ConnectionStrings[ConnectionStringKeys.UnauthorisedUser]
.ConnectionString);
}
}
So this gets called when the application starts and provides my site user with a context for an unauthorised user. This context has a database connection which connects to a database User which has limited access to the database objects.
Once the user has logged in to site I would like to switch to another connection which will give the context access to a Database user with wider access to the database objects.
So by the time the code execution reaches the true condition block for "if (Request.IsAuthenticated)" it is already using the "authorised" database connection for the context.
/// <summary>
/// Handles the PostAuthenticateRequest event of the Application control.
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">The <see cref="EventArgs"/> instance containing the event data.</param>
protected void Application_PostAuthenticateRequest(Object sender, EventArgs e)
{
String[] roles;
var applicationConfiguration =
(IApplicationConfiguration)
DependencyResolver.Current.GetService(typeof(IApplicationConfiguration));
var identity = HttpContext.Current.User.Identity;
if (Request.IsAuthenticated)
{
var roleRepository =
(IRoleRepository)DependencyResolver.Current.GetService(typeof(IRoleRepository));
roles = roleRepository.GetRolesForUser(identity.Name);
}
else
{
roles = new[] { applicationConfiguration.UnknownUserRoleName };
}
var webIdentity = new WebIdentity(identity, roles);
var principal = new WebsitePrincipal(webIdentity)
{
ApplicationConfiguration = applicationConfiguration
};
HttpContext.Current.User = principal;
}
I haven't been able to find a code example on the net that is close enough to my code to adapt and implement. Please can any one advise? Thank you in advance.
A link to the complete code can be provided if required.
Solution:
Ok, so with the tireless help of Erik we have got there. The solution I am using is as so..
public class UnitOfWorkMapping : NinjectModule
{
public override void Load()
{
// Bind the IUnitOfWork for a user that is not logged in.
Bind<IUnitOfWork>()
.To<WebsiteDbContext>()
.When(request => IsUserAuthenticated(request))
.WithConstructorArgument(
"connectionString", ConfigurationManager.ConnectionStrings[ConnectionStringKeys.MainUserConnectionString]
.ConnectionString);
// Bind the IUnitOfWork for a user that is not logged in.
Bind<IUnitOfWork>()
.To<WebsiteDbContext>()
.When(request => !IsUserAuthenticated(request))
.WithConstructorArgument(
"connectionString", ConfigurationManager.ConnectionStrings[ConnectionStringKeys.UnauthorisedUser]
.ConnectionString);
}
/// <summary>
/// Determines if the user authenticated.
/// </summary>
/// <param name="request">The Ninject Activation request.</param>
/// <returns>
/// returns <c>true</c> if the user exists and is authenticated
/// </returns>
public Boolean IsUserAuthenticated(IRequest request)
{
return (
(HttpContext.Current.User != null) &&
HttpContext.Current.User.Identity.IsAuthenticated);
}
}
I hope that helps someone not spend days trying to get to the bottom of this issue.

If you want to use different bindings for whether a user is logged in or out, it's very simple.
Bind<IUnitOfWork>()
.To<WebsiteDbContext>()
.When(x => !HttpContext.Current.Request.IsAuthenticated)
.InRequestScope()
.WithConstructorArgument(
"connectionString",
ConfigurationManager.ConnectionStrings[ConnectionStringKeys.UnauthorisedUser]
.ConnectionString);
Bind<IUnitOfWork>()
.To<WebsiteDbContext>()
.When(x => HttpContext.Current.Request.IsAuthenticated)
.InRequestScope()
.WithConstructorArgument(
"connectionString",
ConfigurationManager.ConnectionStrings[ConnectionStringKeys.AuthorisedUser]
.ConnectionString);
Note that these both have the same IUnitOfWork binding, it will simply return the correct one based on whether the user is logged in or not.
I would also do this:
Bind<IIdentity>()
.To<WebIdentity>()
...
Bind<IIdentity>()
.ToMethod(x => HttpContext.Current.User.Identity)
.WhenInjectedInto(typeof(WebIdentity))
...
Bind<IPrincipal>()
.To<WebsitePrincipal>()
...
Then, configure your WebsitePrincipal's constructor to take IIdentity and IApplicationConfiguration parameters, make WebIdentity's constructor take IIdentity and IRoleRepository as parameters (note the .WhenInjectedInto uses the existing Identity). Have your constructors do the work.
Then you need only write the following (DI takes care of the rest):
protected void Application_PostAuthenticateRequest(Object sender, EventArgs e)
{
HttpContext.Current.User = DependencyResolver.Current.GetService(typeof(IPrincipal));
}

Related

Autofac and the HttpContext

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;
}

autofac and owned instances

I have been reasing about owned instances and I have a need to set one up.
http://docs.autofac.org/en/latest/advanced/owned-instances.html
I have 2 methods in the same class that use this owned instance. I have set it up like this:
private readonly Func<SessionDetails, Owned<ITroposUnitOfWork>> _paramatizedTroposUnitOfWork;
My unit of work class constructor looks like this:
/// <summary>
/// Used for creating manual sessions
/// </summary>
/// <param name="sessionDetails">The details of the session to be created</param>
public TroposUnitOfWork(SessionDetails sessionDetails)
{
// Throw if we don't supply any details
ThrowIf.ArgumentIsNull(() => sessionDetails);
// Start the session
StartSession(sessionDetails);
}
So, my understanding is that if I use a using block, then the unit of work will be disposed of at the end of the call. But it isn't.
Like I mentioned before, I have 2 methods that use this owned instance. They are:
/// <summary>
/// Creates the Tropos user
/// </summary>
/// <param name="model">The user to be created</param>
/// <param name="password">The password to set</param>
private async Task CreateTroposUserAsync(User model, string password)
{
// If there is no password, throw an error
ThrowIf.ArgumentIsNull(() => password);
// Get our master user
var user = await base.FindByNameAsync(model.Master);
// If we have no master user, throw an error
if (user == null) throw new ObjectNotFoundException();
// Create our session details
var sessionDetails = _troposSession.Value.Create(user);
// User our new user
using (var troposUnitOfWork = _paramatizedTroposUnitOfWork(sessionDetails))
{
try
{
// Create our tropos user service
var userService = new TroposUserService(troposUnitOfWork.Value);
// Create our user
var transaction = userService.Create(model);
// Save our changes (Don't throw an error if the user already exists)
troposUnitOfWork.Value.RunTransaction(transaction);
} catch (Exception ex)
{
// Display human readable messages
throw new Exception(ex.Message);
}
}
// Sets the new users password
SetTroposPassword(model, password);
// Update the flag
model.HasTroposLogin = true;
}
And the other one is:
/// <summary>
/// Sets the tropos password
/// </summary>
/// <param name="model">The user that needs the password setting</param>
/// <param name="password"></param>
private void SetTroposPassword(User model, string password)
{
// Create our session details
var sessionDetails = _troposSession.Value.Create(model.UserName);
// Create our anonymous session
using (var troposUnitOfWork = _paramatizedTroposUnitOfWork(sessionDetails))
{
// Create our tropos user service
var userService = new TroposUserService(troposUnitOfWork.Value);
// Set our password
var transaction = userService.ChangePassword(password);
// Save our changes
troposUnitOfWork.Value.RunTransaction(transaction);
}
}
The first method does invoke the second method, but outside the using block. I put a breakpoint in the TroposUnitOfWork dispose method and it only gets hit once. The constructor is only hit once too.
Does anyone know why?
We need to see the initialization of _paramatizedTroposUnitOfWork.
What class has the CreateTroposUserAsync method? We need to see the constructor of that class. I suppose your overall goal is to get a unit of work implementation.
The reason why you only hit constructor once, might be due to the lifetime you used when registering. If its Owned. Then the two methods probably execute in the same lifetime scope and the dependency is only resolved once. Or put differently the _paramatizedTroposUnitOfWork(sessionDetails) returns the same instance.
I've solved similar problem using decorators and factories
public interface IEventHandlerFactory<in TNotification> where TNotification
: class, IAsyncNotification
{
IAsyncNotificationHandler<TNotification> Create( ILifetimeScope
lifetimeScope );
}
public class EventHandlerFactory<TNotification, TEventHandler> :
IEventHandlerFactory<TNotification>
where TNotification : class, IAsyncNotification
where TEventHandler : class, IAsyncNotificationHandler<TNotification>
{
public IAsyncNotificationHandler<TNotification> Create( ILifetimeScope
lifetimeScope )
{
return lifetimeScope.ResolveNamed<TEventHandler>( "EventHandler" )
as IAsyncNotificationHandler<TNotification>;
}
}
full .net fiddle is here https://dotnetfiddle.net/fw4IBw

Autofac module registration tree across layers

So I have a typical three tiered application layered as below
DAL -> Repository -> Business -> Web.UI/API
I have been reading this article about registering dependencies by centralizing them via modules.
The web layer only has a reference to Business which only has a reference to the Repo which only has a reference to the lowest DAL layer. In this topology since the UI/API layer knows nothing about the Repository and has no reference to it, I can't register the modules in the Repository in the UI/API layer. Similarly I can't register the modules present in the DAL in the Business layer. What I want to do is start the registration process in the top most layer which then sets off a cascading effect of registrations in subsequent layers.
Typically what this would look like is each layer exposing a RegisterAllModules method and somehow trigger the RegisterAllModules method from the layer below it. Has something like this been done? Or is there another way to do this? At this point I don't know if I should roll my own logic out as I mentioned here above, since I don't know if there is a documented way to do something like this or not. Thoughts on how to best go forward here is what I am looking for.
Thanks.
Mmmm... I don't know if what follows is a proper response, but I'm going to try to give you the tools for a solution that suits your exact requirementes.
have you looked into json/xml module configuration? You do not need to know the assemblies through cross reference, you just need to know the name of the assemblies in app.config (or web.config). E.g: you can register one module for Repositories in the Repo assembly and one module for Business services in the Business.dll. This completely removes the need of cross-referencing the various assemblies (for Module scanning, you will still need references for method calls, but that is expected anyway). See here for details: http://docs.autofac.org/en/latest/configuration/xml.html#configuring-with-microsoft-configuration
if you want to enforce no call is done from (say) UI to Repo, you can leverage the "Instance Per Matching Lifetime Scope" function (see http://docs.autofac.org/en/latest/lifetime/instance-scope.html#instance-per-matching-lifetime-scope). You can use that registration method in order to enforce a Unit-of-work approach. E.g: a Repository can only be resolved in a "repository" LifetimeScope, and only Business components open scopes tagged "repository".
an alternative approach to tagged scopes is in using the "Instance per Owned<>" pattern. In this way, each Business service would require an Owned<Repository>.
Something like:
var builder = new ContainerBuilder();
builder.RegisterType();
builder.RegisterType().InstancePerOwned();
AFAICT, a correct approach would be to register the components through Modules, referenced by the Json/Xml config, and each Module should target specific LifetimeScopes.
When you a class calls the underlying layer, it should open a new LifetimeScope("underlying layer").
I will elaborate further, if you want advice on implementation strategies.
Best,
Alberto Chiesa
Edit:
I didn't knew the "composition root" meaning. Well, thanks for the info!
I favor a SIMPLE configuration file (be it the .config file or a separate .json or .xml file), because I feel that a list of modules to be imported is simpler done through a list than through a class. But this is opinion.
What is not an opinion is that you can import modules from assembly that are not referenced by the "Composition Root" assembly, in a simple and tested way.
So, I would go for Modules for every component registration, but for a textual configuration file for Module registration. YMMV.
Now, let me show you an example of the Unit of Work pattern that I'm using in many live projects.
In our architecture we make heavy use of a Service Layer, which holds responsibility for opening connections to the db and disposing them when finished, etc.
It's a simpler design than what you're after (I prefer shallow other than deep), but the concept is the same.
If you are "out" of the Service Layer (e.g. in an MVC Controller, or in the UI), you need a ServiceHandle in order to access the Service layer. The ServiceHandle is the only class that knows about Autofac and is responsible for service resolution, invocation and disposal.
The access to the Service Layer is done in this way:
non service classes can require only a ServiceHandle
invocation is done through _serviceHandle.Invoke(Func)
Autofac injects the ready to use handles via constructor injection.
This is done through the use of BeginLifetimeScope(tag) method, and registering services (in a module) in this way:
// register every service except for ServiceBase
Builder.RegisterAssemblyTypes(_modelAssemblies)
.Where(t => typeof(IService).IsAssignableFrom(t) && (t != typeof(ServiceBase)))
.InstancePerDependency();
// register generic ServiceHandle
Builder.RegisterGeneric(typeof(ServiceHandle<>))
.AsSelf()
.AsImplementedInterfaces()
.InstancePerDependency();
And registering every shared resource as InstancePerMatchingLifetimeScope("service")
So, an example invocation would be:
... in the constructor:
public YourUiClass(ServiceHandle<MyServiceType> myserviceHandle)
{
this._myserviceHandle = myserviceHandle;
}
... in order to invoke the service:
var result = _myserviceHandle.Invoke(s => s.myServiceMethod(parameter));
This is the ServiceHandle implementation:
/// <summary>
/// Provides a managed interface to access Model Services
/// </summary>
/// <typeparam name="TServiceType">The Type of the parameter to be managed</typeparam>
public class ServiceHandle<TServiceType> : IServiceHandle<TServiceType> where TServiceType : IService
{
static private readonly ILog Log = LogManager.GetLogger(typeof(ServiceHandle<TServiceType>));
private readonly ILifetimeScope _scope;
/// <summary>
/// True if there where Exceptions caught during the last Invoke execution.
/// </summary>
public bool ErrorCaught { get; private set; }
/// <summary>
/// List of the errors caught during execution
/// </summary>
public List<String> ErrorsCaught { get; private set; }
/// <summary>
/// Contains the exception that was thrown during the
/// last Invoke execution.
/// </summary>
public Exception ExceptionCaught { get; private set; }
/// <summary>
/// Default constructor
/// </summary>
/// <param name="scope">The current Autofac scope</param>
public ServiceHandle(ILifetimeScope scope)
{
if (scope == null)
throw new ArgumentNullException("scope");
_scope = scope;
ErrorsCaught = new List<String>();
}
/// <summary>
/// Invoke a method to be performed using a
/// service instance provided by the ServiceHandle
/// </summary>
/// <param name="command">
/// Void returning action to be performed
/// </param>
/// <remarks>
/// The implementation simply wraps the Action into
/// a Func returning an Int32; the returned value
/// will be discarded.
/// </remarks>
public void Invoke(Action<TServiceType> command)
{
Invoke(s =>
{
command(s);
return 0;
});
}
/// <summary>
/// Invoke a method to be performed using a
/// service instance provided by the ServiceHandle
/// </summary>
/// <typeparam name="T">Type of the data to be returned</typeparam>
/// <param name="command">Action to be performed. Returns T.</param>
/// <returns>A generically typed T, returned by the provided function.</returns>
public T Invoke<T>(Func<TServiceType, T> command)
{
ErrorCaught = false;
ErrorsCaught = new List<string>();
ExceptionCaught = null;
T retVal;
try
{
using (var serviceScope = GetServiceScope())
using (var service = serviceScope.Resolve<TServiceType>())
{
try
{
retVal = command(service);
service.CommitSessionScope();
}
catch (RollbackException rollbackEx)
{
retVal = default(T);
if (System.Web.HttpContext.Current != null)
ErrorSignal.FromCurrentContext().Raise(rollbackEx);
Log.InfoFormat(rollbackEx.Message);
ErrorCaught = true;
ErrorsCaught.AddRange(rollbackEx.ErrorMessages);
ExceptionCaught = rollbackEx;
DoRollback(service, rollbackEx.ErrorMessages, rollbackEx);
}
catch (Exception genericEx)
{
if (service != null)
{
DoRollback(service, new List<String>() { genericEx.Message }, genericEx);
}
throw;
}
}
}
catch (Exception ex)
{
if (System.Web.HttpContext.Current != null)
ErrorSignal.FromCurrentContext().Raise(ex);
var msg = (Log.IsDebugEnabled) ?
String.Format("There was an error executing service invocation:\r\n{0}\r\nAt: {1}", ex.Message, ex.StackTrace) :
String.Format("There was an error executing service invocation:\r\n{0}", ex.Message);
ErrorCaught = true;
ErrorsCaught.Add(ex.Message);
ExceptionCaught = ex;
Log.ErrorFormat(msg);
retVal = default(T);
}
return retVal;
}
/// <summary>
/// Performs a rollback on the provided service instance
/// and records exception data for error retrieval.
/// </summary>
/// <param name="service">The Service instance whose session will be rolled back.</param>
/// <param name="errorMessages">A List of error messages.</param>
/// <param name="ex"></param>
private void DoRollback(TServiceType service, List<string> errorMessages, Exception ex)
{
var t = new Task<string>
service.RollbackSessionScope();
}
/// <summary>
/// Creates a Service Scope overriding Session resolution:
/// all the service instances share the same Session object.
/// </summary>
/// <returns></returns>
private ILifetimeScope GetServiceScope()
{
return _scope.BeginLifetimeScope("service");
}
}
Hope it helps!

With MVC 2, how do I stop one app logout from logging out the user in a second app?

In VS2010 I have two MVC 2 based web apps within the same solution that also share a common Setup project. One app is a configuration utility for setting up users and variables in the opposing app. Upon installation, the two IIS directories look like this on the user's browser:
App1: http://localhost/App1/Auth/Login
App2: http://localhost/App1/App2/Auth/Login
The problem I'm having is when the user has both apps open at the same time, and logs out of one of them they are also logged out of the opposing app. This is a minor issue, but I've been tasked with correcting it.
From what I can tell, the two apps must be sharing the same Session object, because the logout command method in each controller invokes Session.Abandon() .
Only two controllers have the ability to log out a user; here's the constructor's from those controller's:
App1: namespace App1.Controllers
/// <summary>
/// Functionality related to Assets
/// </summary>
public class AssetsController : Controller
{
private IConfig _config = null;
private IProfileRepository _profiles = null;
private IInspectionRepository _inspections = null;
private ICustomLabelsFactory _labels = null;
private IValidateRepository _validator = null;
/// <summary>
/// Create an instance of the AssetsController which uses the db.
/// </summary>
public AssetsController() : this(Config.Current, new ProfileRepository(Config.Current), new InspectionRepository(), new CustomLabelFactory(), new ValidateRepository()) { }
/// <summary>
/// Create an instance of the AssetsController with the given
/// IInspectionRepository implementation.
/// </summary>
/// <param name="inspections">IInspectionRepository implementation.</param>
public AssetsController(IConfig config, IProfileRepository profiles, IInspectionRepository inspections, ICustomLabelsFactory labels, IValidateRepository validator)
: base()
{
ViewData["_Module"] = "Assets";
_config = config;
_profiles = profiles;
_profiles.ModelState = ModelState;
_inspections = inspections;
_inspections.ModelState = ModelState;
_labels = labels;
_labels.ModelState = ModelState;
_validator = validator;
_validator.CustomLabels = _labels.Assets;
_validator.ModelState = ModelState;
}
App2: namespace App1.App2.Controllers
/// <summary>
/// Handles login/logout functionality
/// </summary>
public class AuthController : Controller
{
private ILoginService _login;
private IUtilityRepository _utility;
/// <summary>
/// Creates the Auth controller using the default User Repository which
/// uses the database.
/// </summary>
public AuthController() : this(new LoginService(), new UtilityRepository()) { }
/// <summary>
/// Creates the Auth controller with the given User Repository.
/// </summary>
/// <param name="userRepository">IUserRepository implementation.</param>
public AuthController(ILoginService loginService, IUtilityRepository utility)
: base()
{
ViewData["_Module"] = "Login";
_login = loginService;
_login.ModelState = ModelState;
_utility = utility;
_utility.ModelState = ModelState;
}
I might be barking up the wrong tree on where to start looking at the code, but I'm hoping someone can see something obvious here that I can't. Or, maybe someone can tell me how to do this differently so there is not a shared Session object involved. I've been working on this on and off for the better part of this week, so any help offered will be greatly appreciated.
You could configure each application to use a different session database in your web.config
EDIT: something like
<sessionState mode="SQLServer" sqlConnectionString="Data Source=.\SQLEXPRESS;User Id=test;Password=test;Application Name=AppName" />
<machineKey
validationKey="SOMEKEY"
validation="SHA1" decryption="AES"
/>
Where somekey is different for each application
A simple, lazy, IIS settings avoiding solution is to open each in a different browser.

Nhibernate, WinForms, Castle Windsor: Session Management

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;
}

Categories

Resources