IOC and Silverlight - c#

In mvc asp.net, I can override a factory to create my controllers, and so put a reference to my IOC just here. Doing So every interface needed by the constructor of my controllers will be feeded by my IOC .
Is there some common way to do it using Silverlight?
At the moment I only found to use the kernel of Ninject everywhere :
public partial class MyUserControlSL
{
public MyUserControlSL()
{
DataContext = new MyViewModel(Kernel.Get<IMyRepository>());
InitializeComponent();
}
}
eg using StructureMap and MVC:
public class ControllerFactory : DefaultControllerFactory
{
protected override IController GetControllerInstance(
RequestContext requestContext, Type controllerType)
{
IController result = null;
try
{
if (controllerType != null)
{
result = ObjectFactory.GetInstance(controllerType)
as Controller;
}
else
{
return base.GetControllerInstance(
requestContext, controllerType);
}
}
catch (StructureMapException)
{
System.Diagnostics.Debug.WriteLine(
ObjectFactory.WhatDoIHave());
throw;
}
return result;
}
}
public AController(IServiceA serviceA)
{
if (serviceA == null)
{
throw new Exception("IServiceA cannot be null");
}
_ServiceA = serviceA;
}
public ServiceA(IRepositoryA repository)
{
if (repository == null)
{
throw new Exception(
"the repository IRepositoryA cannot be null");
}
_Repository = repository;
}
Thanks for your help, please ask if it is not clear..

In Silverlight you should use a bootstrapper at composition root to wire up your entire object graph. It could be the Application class app.xml.cs and look similar to
public partial class App : Application
{
private void Application_Startup(object sender, StartupEventArgs e)
{
Bootstrapper bootstrapper = new Bootstrapper();
bootstrapper.Run();
}
}
In general this should be sufficant but if you need a separate Factory class for your views, take a look at
Keeping the DI-container usage in the composition root in Silverlight and MVVM.

For Silverlight you may use PRISM framework with custom IoC container.

Autofac has built in support for silverlight: http://weblogs.asp.net/dwahlin/archive/2010/01/03/using-autofac-as-an-ioc-container-in-silverlight-applications.aspx

Related

Implementing CastleWindsor with Model View Presenter (MVP)

I would like to implement CastleWindsor with the MVP pattern, but I keep getting an 'Object Reference Not Set to an Object reference on the Presenter when the repository is called to obtain some data.
This is how I did it and I am wondering if there is anything wrong, so please let me know if you can:
Presenter:
public class CategoryPresenter
{
ICategoryRepository categoryRepository;
ICategoryView categoryView;
public CategoryPresenter(ICategoryView _categoryView, ICategoryRepository _categoryRepository)
{
categoryView = _categoryView;
categoryRepository = _categoryRepository;
}
//public CategoryPresenter(ICategoryView _categoryView) : this (_categoryView, new CategoryRepository())
//{ }
public CategoryPresenter(ICategoryView _view)
{
categoryView = _view;
}
public IEnumerable<object> GetActiveCategories()
{
return categoryRepository.GetActiveCategories();
}
}
IoC Class:
public static class IoC
{
public static IWindsorContainer windsorContainter { get; set; }
}
IoCConfig Class:
class IoCConfig
{
public static IWindsorContainer RegisterCastleWindsorContainer()
{
IWindsorContainer windsorContainer = new WindsorContainer()
.Install(new RepositoryInstaller())
IoC.windsorContainter = windsorContainer;
return windsorContainer;
}
}
Installer Class:
public class RepositoryInstaller: IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.Register(Component.For<ICategoryRepository>().ImplementedBy<CategoryRepository>).LifestyleTransient());
}
}
Finally in Global.ascx file I am doing this at App_Start:
void Application_Start(object sender, EventArgs e)
{
// Code that runs on application startup
IoCConfig.RegisterCastleWindsorContainer();
}
With this, the error message is as said above; the error happens at the presenter's method: GetActiveCategories();
As you see at no where in code I invoke the resolve method on the container.
Please let me know if if you have any suggestions.
Thank you.
I have resolved this to the IoC Class
public static T Resolve<T>()
{
try
{
return windsorContainer.Resolve<T>();
}
catch (Exception e)
{
throw e;
}
}
And then add this to the presenter:
ICategoryRepository categoryRepository = IoC.Resolve<ICategoryRepository>();
ICategoryView categoryView = IoC.Resolve<ICategoryView>();

Controller does not have a default constructor 500 internal server error

This is my controller
public class SuggestionController : ApiController
{
public ISuggestionRepository Repository { get; private set; }
public SuggestionController(ISuggestionRepository repository)
{
this.Repository = repository;
}
// to post suggestion
[HttpPost]
[ActionName("PostSuggestion")]
public HttpResponseMessage PostSuggestion(Suggestion suggestion)
{
var answerCorrect = this.Repository.CreateSuggestion(suggestion);
if (answerCorrect == true)
return Request.CreateResponse(HttpStatusCode.OK);
else
return Request.CreateResponse(HttpStatusCode.Conflict);
}
}
and this is my RegisterServices method in NinjectWebCommon.cs
private static void RegisterServices(IKernel kernel)
{
kernel.Bind<ICompetitionRepository>().To(typeof(CompetitionRepository))
.WithConstructorArgument("serviceContext", new InMemoryDataContext<Competition>());
kernel.Bind<ISubmissionRepository>().To(typeof(SubmissionRepository))
.WithConstructorArgument("serviceContext", new InMemoryDataContext<Submission>());
kernel.Bind<IUserRepository>().To(typeof(UserRepository))
.WithConstructorArgument("serviceContext", new InMemoryDataContext<User>());
kernel.Bind<ISuggestionRepository>().To(typeof(SuggestionRepository))
.WithConstructorArgument("serviceContext", new InMemoryDataContext<Suggestion>());
}
But am getting an exception that my suggestion controller does not have a default constructor and its showing a 500 internal server when am hitting the controller from a client app
I know that we get the exception of controller not having default constructor if the ninject dependency is not working properly but the below is another controller i have implemeneted similar to suggestion controller and its working absolutely fine.
public IUserRepository Repository { get; private set; }
public SSOController(IUserRepository repository)
{
this.Repository = repository;
}
[HttpPost]
[ActionName("PostUser")]
public HttpResponseMessage PostUser([FromBody]string id)
{
var accessToken = id;
var client = new FacebookClient(accessToken);
dynamic result = client.Get("me", new { fields = "name,email" });
string name = result.name;
string email = result.email;
var existingUser = this.Repository.FindByUserIdentity(name);
if (existingUser == null)
{
var newUser = new User
{
Username = name,
Email = email,
};
var success = this.Repository.CreateAccount(newUser);
if (!success)
{
return Request.CreateResponse(HttpStatusCode.InternalServerError);
}
//return created status code as we created the user
return Request.CreateResponse<User>(HttpStatusCode.Created, newUser);
}
return Request.CreateResponse(HttpStatusCode.OK);
}
}
I have no idea where am going wrong. Please let me know if u have any suggestions.
EDIT:
my Global.asax
public class WebApiApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
AuthConfig.RegisterAuth();
GlobalConfiguration.Configuration.IncludeErrorDetailPolicy =
IncludeErrorDetailPolicy.Always;
}
Dependency resolver am using
// Provides a Ninject implementation of IDependencyScope
// which resolves services using the Ninject container.
public class NinjectDependencyScope : IDependencyScope
{
IResolutionRoot resolver;
public NinjectDependencyScope(IResolutionRoot resolver)
{
this.resolver = resolver;
}
public object GetService(Type serviceType)
{
if (resolver == null)
throw new ObjectDisposedException("this", "This scope has been disposed");
return resolver.TryGet(serviceType);
}
public System.Collections.Generic.IEnumerable<object> GetServices(Type serviceType)
{
if (resolver == null)
throw new ObjectDisposedException("this", "This scope has been disposed");
return resolver.GetAll(serviceType);
}
public void Dispose()
{
IDisposable disposable = resolver as IDisposable;
if (disposable != null)
disposable.Dispose();
resolver = null;
}
}
// This class is the resolver, but it is also the global scope
// so we derive from NinjectScope.
public class NinjectDependencyResolver : NinjectDependencyScope, IDependencyResolver
{
IKernel kernel;
public NinjectDependencyResolver(IKernel kernel)
: base(kernel)
{
this.kernel = kernel;
}
public IDependencyScope BeginScope()
{
return new NinjectDependencyScope(kernel.BeginBlock());
}
}
and calling it in CreateKernel() method in NinjectWebCommon
private static IKernel CreateKernel()
{
var kernel = new StandardKernel();
kernel.Bind<Func<IKernel>>().ToMethod(ctx => () => new Bootstrapper().Kernel);
kernel.Bind<IHttpModule>().To<HttpApplicationInitializationHttpModule>();
RegisterServices(kernel);
// Install our Ninject-based IDependencyResolver into the Web API config
GlobalConfiguration.Configuration.DependencyResolver = new NinjectDependencyResolver(kernel);
return kernel;
}
Suggestion Repository
public class SuggestionRepository : Repository<Suggestion>, ISuggestionRepository
{
public SuggestionRepository(IServiceContext<Suggestion> servicecontext)
: base(servicecontext)
{ }
public bool CreateSuggestion(Suggestion suggestion)
{
this.ServiceContext.Create(suggestion);
this.ServiceContext.Save();
return true;
}
}
ISuggestionRepository
public interface ISuggestionRepository
{
bool CreateSuggestion(Suggestion suggestion);
}
Repository
public abstract class Repository<T>
{
public IServiceContext<T> ServiceContext { get; private set; }
public Repository(IServiceContext<T> serviceContext)
{
this.ServiceContext = serviceContext;
}
}
IserviceContext
public interface IServiceContext<T>
{
IQueryable<T> QueryableEntities { get; }
void Create(T entity);
void Update(T entity);
void Delete(T entity);
void Save();
}
Since you're using WebApi, you will need to use the WebApi extension for Ninject. Unfortunately, the current Ninject.WebApi nuget package is out of date, and doesn't work with the released version of WebApi.
Temporarily, until Remo gets around to updating Ninject.WebApi to the release version, you can use Ninject.WebApi-RC http://nuget.org/packages/Ninject.Web.WebApi-RC
http://www.eyecatch.no/blog/2012/06/using-ninject-with-webapi-rc/
EDIT:
To recap the information discussed in comments, Here are the recommendations:
1) Use Ninject.MVC3 and Ninject.Web.WebApi (but use Ninject.Web.WebApi-RC until the official is updated) as discussed above. Do not use a custom DependencyResolver, and let Ninject.Web.Mvc and .WebApi do their job.
2) Change your bindings to this:
kernel.Bind<ICompetitionRepository>().To<CompetitionRepository>();
... similar bindings
3) Add a generic binding for your ServiceContext
kernel.Bind(typeof(IServiceContext<>)).To(typeof(InMemoryDataContext<>));
I think the problem is you're using the ApiController.
Controllers and apiControllers are using a different dependancy injection container.
Both of them however expose the same methods.
If the working controller is inheriting the Controller class then that's your cause.
For a work around take a look at
this topic
I have faced the same issue.
This is how I rectified:
I created a WebContainerManager which is just a static wrapper around container.
Static container wrappers useful when you don't control instantiation and can't rely on injection - e.g. action filter attributes
public static class WebContainerManager
{
public static IKernel GetContainer()
{
var resolver = GlobalConfiguration.Configuration.DependencyResolver as NinjectDependencyResolver;
if (resolver != null)
{
return resolver.Container;
}
throw new InvalidOperationException("NinjectDependencyResolver not being used as the MVC dependency resolver");
}
public static T Get<T>()
{
return GetContainer().Get<T>();
}
}
Inside your controller, call your empty constructor like this with no parameters:
public SuggestionController() : this(WebContainerManager.Get<ISuggestionRepository>())
{
}
This should work.
This technique i got from the book on MVC4 by Jamie Kurtz #jakurtz.
You probably need to do some dependency injection so you can inject the ISuggestionRepository parameter on your SuggestionController constructor. To do that you need to override methods in the DefaultControllerFactory class to customize the creation of controllers. Since you are using NInject, you can have something like:
public class NInjectControllerFactory : DefaultControllerFactory
{
private IKernel kernel = new StandardKernel(new CustomModule());
protected override IController GetControllerInstance(System.Web.Routing.RequestContext requestContext, Type controllerType)
{
return controllerType == null ? null : (IController)kernel.Get(controllerType);
}
public class CustomModule : NinjectModule
{
public override void Load()
{
this.Bind<ICompetitionRepository>().To(typeof(CompetitionRepository))
.WithConstructorArgument("serviceContext", new InMemoryDataContext<Competition>());
this.Bind<ISubmissionRepository>().To(typeof(SubmissionRepository))
.WithConstructorArgument("serviceContext", new InMemoryDataContext<Submission>());
this.Bind<IUserRepository>().To(typeof(UserRepository))
.WithConstructorArgument("serviceContext", new InMemoryDataContext<User>());
this.Bind<ISuggestionRepository>().To(typeof(SuggestionRepository))
.WithConstructorArgument("serviceContext", new InMemoryDataContext<Suggestion>());
}
}
}
Then in your Global.asax.cs, you can add a line to swap out the controller factory
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
RegisterRoutes(RouteTable.Routes);
ControllerBuilder.Current.SetControllerFactory(new NInjectControllerFactory());
}

HttpConfiguration.get_ServiceResolver() Missing

I am wiring up Ninject with MVC4 and have it working to the point it's trying to actually resolve dependencies. However, I am getting the following exception:
Method not found: 'System.Web.Http.Services.DependencyResolver System.Web.Http.HttpConfiguration.get_ServiceResolver()'.
Anyone ran into this and have a work around?
GlobalConfiguration.Configuration.ServiceResolver was replaced with GlobalConfiguration.Configuration.DependencyResolver in the RC. So I guess the Ninject package you are using is simply not designed for the RC. It was one of the breaking changes.
So here are the steps to make Ninject work with ASP.NET MVC 4 Web API RC:
Create a new ASP.NET MVC 4 application using the Empty template
Declare an interface:
public interface IFoo
{
string GetBar();
}
Then an implementation:
public class Foo : IFoo
{
public string GetBar()
{
return "the bar";
}
}
Then add an API controller:
public class ValuesController : ApiController
{
private readonly IFoo _foo;
public ValuesController(IFoo foo)
{
_foo = foo;
}
public string Get()
{
return _foo.GetBar();
}
}
Install the Ninject.Mvc3 NuGet package (Install-Package Ninject.Mvc3)
Define a custom API dependency resolver as shown in this gist:
public class NinjectDependencyScope : IDependencyScope
{
private IResolutionRoot resolver;
internal NinjectDependencyScope(IResolutionRoot resolver)
{
Contract.Assert(resolver != null);
this.resolver = resolver;
}
public void Dispose()
{
IDisposable disposable = resolver as IDisposable;
if (disposable != null)
disposable.Dispose();
resolver = null;
}
public object GetService(Type serviceType)
{
if (resolver == null)
throw new ObjectDisposedException("this", "This scope has already been disposed");
return resolver.TryGet(serviceType);
}
public IEnumerable<object> GetServices(Type serviceType)
{
if (resolver == null)
throw new ObjectDisposedException("this", "This scope has already been disposed");
return resolver.GetAll(serviceType);
}
}
public class NinjectDependencyResolver : NinjectDependencyScope, IDependencyResolver
{
private IKernel kernel;
public NinjectDependencyResolver(IKernel kernel)
: base(kernel)
{
this.kernel = kernel;
}
public IDependencyScope BeginScope()
{
return new NinjectDependencyScope(kernel.BeginBlock());
}
}
In your ~/App_Start/NinjectWebCommon.cs/CreateKernel method that was created when you installed the NuGet add the following line after the RegisterServices(kernel); line:
GlobalConfiguration.Configuration.DependencyResolver = new NinjectDependencyResolver(kernel);
Configure your kernel:
private static void RegisterServices(IKernel kernel)
{
kernel.Bind<IFoo>().To<Foo>();
}
Hit F5 and navigate to /api/values
You are presented with the bar.
Obviously when the RC hits RTM I hope there will be a Ninject.Mvc4 NuGet that will shorten those 10 steps to maximum 5.

What is the right way to implement Unity in ASP.Net MVC?

Ok, there are so many results in Google when you search for this topic; NerdDinner,CodeClimber,CodeProject etc.. but they all seems to be not working as expected! Either they give error during build or runtime! What is the right way to implement Unity 2.0 in ASP.Net MVC 2? I am simply not able to get this working!
Your help and thoughts are highly appreciated. Thanks!
Try writing a simple controller factory using Unity which is capable of resolving the controller instances:
public class UnityControllerFactory : DefaultControllerFactory
{
private readonly IUnityContainer _container;
public UnityControllerFactory(IUnityContainer container)
{
_container = container;
}
protected override IController GetControllerInstance(
RequestContext requestContext,
Type controllerType
)
{
if (controllerType == null)
{
throw new ArgumentNullException("controllerType");
}
if (!typeof(IController).IsAssignableFrom(controllerType))
{
throw new ArgumentException("Type requested is not a controller", "controllerType");
}
return _container.Resolve(controllerType) as IController;
}
}
and then hook it up in the Application_Start event in Global.asax:
protected void Application_Start()
{
...
var container = new UnityContainer();
// TODO: Configure the container here with your controllers
var factory = new UnityControllerFactory(container);
ControllerBuilder.Current.SetControllerFactory(factory);
}
This is my version of the UnityControllerFactory. It uses reflection to get the controllers from the calling assembly and register them in the container.
using System;
using System.Configuration;
using System.Linq;
using System.Reflection;
using System.Web.Mvc;
using Microsoft.Practices.Unity;
using Microsoft.Practices.Unity.Configuration;
namespace WebProveidorsMVC.DI.ControllerFactories
{
public class UnityControllerFactory : DefaultControllerFactory
{
private readonly IUnityContainer _container;
public UnityControllerFactory()
{
_container=new UnityContainer();
((UnityConfigurationSection) ConfigurationManager.GetSection("unity")).Configure(_container);
var controllerTypes = from t in Assembly.GetCallingAssembly().GetTypes()
where typeof(IController).IsAssignableFrom(t)
select t;
foreach (var t in controllerTypes)
_container.RegisterType(t, t.FullName);
}
protected override IController GetControllerInstance(System.Web.Routing.RequestContext requestContext, Type controllerType)
{
if (controllerType != null)
return (IController)_container.Resolve(controllerType);
return null;
}
public override void ReleaseController(IController controller)
{
_container.Teardown(controller);
base.ReleaseController(controller);
}
}
}
Then in my ApplicationStart method I register it like this:
ControllerBuilder.Current.SetControllerFactory(new UnityControllerFactory());

Castle project per session lifestyle with ASP.NET MVC

I'm really new to Castle Windsor IoC container. I wanted to know if theres a way to store session variables using the IoC container. I was thinking something in the line of this:
I want to have a class to store search options:
public interface ISearchOptions{
public string Filter{get;set;}
public string SortOrder{get;set;}
}
public class SearchOptions{
public string Filter{get;set;}
public string SortOrder{get;set;}
}
And then inject that into the class that has to use it:
public class SearchController{
private ISearchOptions _searchOptions;
public SearchController(ISearchOptions searchOptions){
_searchOptions=searchOptions;
}
...
}
then in my web.config, where I configure castle I want to have something like:
<castle>
<components>
<component id="searchOptions" service="Web.Models.ISearchOptions, Web" type="Web.Models.SearchOptions, Web" lifestyle="PerSession" />
</components>
</castle>
And have the IoC container handle the session object without having to explicitly access it myself.
How can I do this?
Thanks.
EDIT: Been doing some research. Basically, what I want is to have the a session Scoped component. I come from Java and Spring Framework and there I have session scoped beans which I think are very useful to store session data.
this might be what your looking for.
public class PerSessionLifestyleManager : AbstractLifestyleManager
{
private readonly string PerSessionObjectID = "PerSessionLifestyleManager_" + Guid.NewGuid().ToString();
public override object Resolve(CreationContext context)
{
if (HttpContext.Current.Session[PerSessionObjectID] == null)
{
// Create the actual object
HttpContext.Current.Session[PerSessionObjectID] = base.Resolve(context);
}
return HttpContext.Current.Session[PerSessionObjectID];
}
public override void Dispose()
{
}
}
And then add
<component
id="billingManager"
lifestyle="custom"
customLifestyleType="Namespace.PerSessionLifestyleManager, Namespace"
service="IInterface, Namespace"
type="Type, Namespace">
</component>
This solution will work for Windsor 3.0 and above. It;s based on the implementation of PerWebRequest Lifestyle and makes use of the new Scoped Lifestyle introduced in Windsor 3.0.
You need two classes...
An implementation of IHttpModule to handle session management. Adding the ILifetimeScope object into session and disposing it again when the session expires. This is crucial to ensure that components are released properly. This is not taken care of in other solutions given here so far.
public class PerWebSessionLifestyleModule : IHttpModule
{
private const string key = "castle.per-web-session-lifestyle-cache";
public void Init(HttpApplication context)
{
var sessionState = ((SessionStateModule)context.Modules["Session"]);
sessionState.End += SessionEnd;
}
private static void SessionEnd(object sender, EventArgs e)
{
var app = (HttpApplication)sender;
var scope = GetScope(app.Context.Session, false);
if (scope != null)
{
scope.Dispose();
}
}
internal static ILifetimeScope GetScope()
{
var current = HttpContext.Current;
if (current == null)
{
throw new InvalidOperationException("HttpContext.Current is null. PerWebSessionLifestyle can only be used in ASP.Net");
}
return GetScope(current.Session, true);
}
internal static ILifetimeScope YieldScope()
{
var context = HttpContext.Current;
if (context == null)
{
return null;
}
var scope = GetScope(context.Session, true);
if (scope != null)
{
context.Session.Remove(key);
}
return scope;
}
private static ILifetimeScope GetScope(HttpSessionState session, bool createIfNotPresent)
{
var lifetimeScope = (ILifetimeScope)session[key];
if (lifetimeScope == null && createIfNotPresent)
{
lifetimeScope = new DefaultLifetimeScope(new ScopeCache(), null);
session[key] = lifetimeScope;
return lifetimeScope;
}
return lifetimeScope;
}
public void Dispose()
{
}
}
The second class you need is an implementation of IScopeAccessor. This is used to bridge the gap between your HttpModule and the built in Windsor ScopedLifestyleManager class.
public class WebSessionScopeAccessor : IScopeAccessor
{
public void Dispose()
{
var scope = PerWebSessionLifestyleModule.YieldScope();
if (scope != null)
{
scope.Dispose();
}
}
public ILifetimeScope GetScope(CreationContext context)
{
return PerWebSessionLifestyleModule.GetScope();
}
}
Two internal static methods were added to PerWebSessionLifestyleModule to support this.
That's it, expect to register it...
container.Register(Component
.For<ISometing>()
.ImplementedBy<Something>()
.LifestyleScoped<WebSessionScopeAccessor>());
Optionally, I wrapped this registration up into an extension method...
public static class ComponentRegistrationExtensions
{
public static ComponentRegistration<TService> LifestylePerSession<TService>(this ComponentRegistration<TService> reg)
where TService : class
{
return reg.LifestyleScoped<WebSessionScopeAccessor>();
}
}
So it can be called like this...
container.Register(Component
.For<ISometing>()
.ImplementedBy<Something>()
.LifestylePerSession());
It sounds like you are on the right track, but your SearchOptions class needs to implement ISearchOptions:
public class SearchOptions : ISearchOptions { ... }
You also need to tell Windsor that your SearchController is a component, so you may want to register that in the web.config as well, although I prefer to do it from code instead (see below).
To make Windsor pick up your web.config, you should instantiate it like this:
var container = new WindsorContainer(new XmlInterpreter());
To make a new instance of SearchController, you can then simply do this:
var searchController = container.Resolve<SearchController>();
To register all Controllers in a given assembly using convention-based techniques, you can do something like this:
container.Register(AllTypes
.FromAssemblyContaining<MyController>()
.BasedOn<IController>()
.ConfigureFor<IController>(reg => reg.LifeStyle.Transient));
My experience has been that Andy's answer does not work, as the SessionStateModule.End is never raised directly:
Though the End event is public, you can only handle it by adding an event handler in the Global.asax file. This restriction is implemented because HttpApplication instances are reused for performance. When a session expires, only the Session_OnEnd event specified in the Global.asax file is executed, to prevent code from calling an End event handler associated with an HttpApplication instance that is currently in use.
For this reason, it becomes pointless to add a HttpModule that does nothing. I have adapted Andy's answer into a single SessionScopeAccessor class:
public class SessionScopeAccessor : IScopeAccessor
{
private const string Key = "castle.per-web-session-lifestyle-cache";
public void Dispose()
{
var context = HttpContext.Current;
if (context == null || context.Session == null)
return;
SessionEnd(context.Session);
}
public ILifetimeScope GetScope(CreationContext context)
{
var current = HttpContext.Current;
if (current == null)
{
throw new InvalidOperationException("HttpContext.Current is null. PerWebSessionLifestyle can only be used in ASP.Net");
}
var lifetimeScope = (ILifetimeScope)current.Session[Key];
if (lifetimeScope == null)
{
lifetimeScope = new DefaultLifetimeScope(new ScopeCache());
current.Session[Key] = lifetimeScope;
return lifetimeScope;
}
return lifetimeScope;
}
// static helper - should be called by Global.asax.cs.Session_End
public static void SessionEnd(HttpSessionState session)
{
var scope = (ILifetimeScope)session[Key];
if (scope != null)
{
scope.Dispose();
session.Remove(Key);
}
}
}
}
It is important to call the SessionEnd method from your global.asax.cs file:
void Session_OnEnd(object sender, EventArgs e)
{
SessionScopeAccessor.SessionEnd(Session);
}
This is the only way to handle a SessionEnd event.

Categories