For ASP.Net MVC development I've been working for a while with a setup using Entity Framework Code First and Castle Windsor for providing IoC container services.
I recently ran into a weird bug that may be obvious to some... but given I wasted an hour or so working it out figured it was worth a blog post.
The issue was that for a certain operation I was getting stale data out of Entity Framework. With the help of the very useful Entity Framework Profiler which was reporting an ObjectContext being shared across threads I tracked it down to my implementation of a custom membership and role provider.
For most use of the IoC container I have a custom controller factory, that handles resolving the dependent types. But there are some parts of the application that need access to classes managed through the container, but that aren't within the context of a controller. Custom membership and role providers are examples of these.
With Castle Windsor (and other IoC containers) you can manage the lifetime of the objects they create - options including Singleton, PerWebRequest and Transient. PerWebRequest is the appropriate one for web applications when using Entity Framework classes. However it seemed that this wasn't being respected within the custom membership and role providers.
The reason for this turned out to be that I was resolving the classes within the constructor (as you would with controllers), which meant the framework was holding on to them between web requests.
So this is wrong:
private IEditorService _editorService; public AppRoleProvider() { _editorService = InstantiateEditorServiceObject(); } private IEditorService InstantiateEditorServiceObject() { var container = WindsorHelpers.GetWindsorContainer(); return container.Resolve<IEditorservice>(); } public override string[] GetRolesForUser(string username) { var editor = _editorService.GetEditorByEmail(username); return new string[] { editor.Role.GetDescription() }; }
Instead, by instantiating only within the method itself, I successfully could have just one per web request.
public AppRoleProvider() { } private IEditorService InstantiateEditorServiceObject() { var container = WindsorHelpers.GetWindsorContainer(); return container.Resolve<IEditorservice>(); } public override string[] GetRolesForUser(string username) { var editorService = InstantiateEditorServiceObject(); var editor = editorService.GetEditorByEmail(username); return new string[] { editor.Role.GetDescription() }; }
Comments
Post a Comment