问题
I am trying to create a UserService that I can inject in my classes, that will hold the user currently logged in to my system. I am using CastleWindsor as my container.
Now my problem is that I am trying to make my UserService disposable, so that the databaseconnection fetching the user on creating will also be disposed when the object is destroyed.
I added the following setup in my Global.asax.cs:
private static void BootstrapContainer()
{
_container = new WindsorContainer().Install(FromAssembly.This());
var controllerFactory = new WindsorControllerFactory(_container.Kernel);
ControllerBuilder.Current.SetControllerFactory(controllerFactory);
GlobalConfiguration.Configuration.DependencyResolver = new WindsorDependencyResolver(_container.Kernel);
_container.Register(Component.For<IUserService>()
.LifestylePerWebRequest()
.ImplementedBy<UserService>());
_container.Register(Component.For<IPrincipal>()
.LifeStyle.PerWebRequest
.UsingFactoryMethod(() => HttpContext.Current.User));
}
Which is called in my Application_Start
.
My UserService code is as follows:
public interface IUserService
{
OrganisationBruger User { get; }
int UserId { get; }
}
public class UserService : IUserService, IDisposable
{
private readonly IPrincipal _principal;
private OrganisationBruger _user;
private readonly DatabaseDataContext _db;
public UserService(IPrincipal principal, IDatabaseDataContextFactory dataContextFactory)
{
_principal = principal;
_db = dataContextFactory.GetDataContext();
}
public OrganisationBruger User => _user ?? (_user = GetUser());
public int UserId => Convert.ToInt32(_principal.Identity.Name);
private OrganisationBruger GetUser()
{
return _db.OrganisationBrugers.Single(u => u.ID == UserId);
}
public void Dispose()
{
_db.Dispose();
}
}
When I Debug my code I can see on the very first request I fire it correctly creates the class UserService.cs
and then disposes it after the webrequest. Now my problem is the second web request does not seem to call the constructor anymore thus just reusing the formerly created object. This leads to the DatabaseContext
already being disposed of.
I thought that LifestylePerWebRequest
meant that the UserService
would get recreated on every request. Can anyone help me understand this?
回答1:
So first of all I had overlook the "registration of the module"-part in the documentation. You need to add the following to your web.config:
<httpModules>
<add name="PerRequestLifestyle" type="Castle.MicroKernel.Lifestyle.PerWebRequestLifestyleModule, Castle.Windsor"/>
</httpModules>
Second of all I was not a hundred percent sure how the dependency resolver worked. The problem was that one of the modules using my UserService
as a dependency had its lifecycle set to Singleton
which is default behavior when you specify nothing about the lifecycle when registering your module with the container.
I fixed the problem by making sure that every module that is using my UserService as dependency is also registered with a lifecycle of LifestylePerWebRequest()
or LifestyleTransient()
.
回答2:
You should double check if you have any other interface which use IUserService overwrites the lifestyle. In that case, Castle windsor will not resolve IUserService for each request because ITest is set to singleton.
For example
_container.Register(Component.For<ITest>()
.LifestyleSingleton()
.ImplementedBy<Test>());
public interface ITest
{
}
public class Test: ITest
{
private readonly IUserService _ser;
public Test(IUserService ser)
{
_ser= ser;
}
}
回答3:
I have a WindsorHttpControllerActivator that implements IHttpControllerActivator. It registers the controller for disposal which ensures a new controller gets created on each request by destroying the old one. This occurs when .LifestylePerWebRequest() completes each request.
public IHttpController Create(
HttpRequestMessage request,
HttpControllerDescriptor controllerDescriptor,
Type controllerType)
{
var controller =
(IHttpController)_container.Resolve(controllerType);
// Controller disposal ensures new controller for each request, hence DbContexts are fresh and pull fresh data from the DB.
request.RegisterForDispose(
new Release(
() => _container.Release(controller)));
return controller;
}
private class Release : IDisposable
{
private readonly Action _release;
public Release(Action release)
{
_release = release;
}
public void Dispose()
{
_release();
}
}
来源:https://stackoverflow.com/questions/34899924/castlewindsor-lifestyle-perwebrequest-behaves-like-singleton