Ok - I've got an interesting issue We are building a CMS6R2 site which is actually an MVC / WebForm hybrid and we're using StructureMap as an IoC conatiner - fun eh :)
As part of the MVC build - we've produced our own custom attribute to mark whether a user has access to a particular Controller / ActionResult via our own authentication mechanism. We want our attribute to be flexible too which means that it too is dependent on the conatiner for resolving its dependencies. As we don't have control over the creation of attributes - the constructor directly makes a call to the StructureMap ObjectFactory to resolve its dependencies when instantiated.The issue I'm having is with the EPiServer Initialization process. This is started by the EPiServer.Global class (in its constructor) - and runs before our bootstrapper that sets up the container. During the assembly scanning process it touches our security atrribute class which tries to resolve its dependency from the uninitialised container. Bang - Exception!I thought that adding Framework.config settings or assembly attributes [assembly: PreventAssemblyScan] set on the relevant project to remove the selected assemblies from the scan would resolve this - but either I'm missing something or its still performing some sort of scan.If this is impossible; I guess my only option is to reflect out the EPiServer Global class and implement all the functionality in my own Global.asax and make sure the conatiner is initialized before the call to InitializationModule.FrameworkInitialization. That feels like cheating though.
Could you post a stack trace?
[StructureMapException: StructureMap Exception Code: 202
No Default Instance defined for PluginFamily Banana.Security.IMembershipLoginService, Banana.Security, Version=18.104.22.168, Culture=neutral, PublicKeyToken=null]
StructureMap.BuildSession.<.ctor>b__0(Type t) in c:\code\structuremap\Source\StructureMap\BuildSession.cs:31
StructureMap.Util.Cache`2.get_Item(KEY key) in c:\code\structuremap\Source\StructureMap\Util\Cache.cs:83
StructureMap.BuildSession.CreateInstance(Type pluginType) in c:\code\structuremap\Source\StructureMap\BuildSession.cs:192
StructureMap.Container.GetInstance() in c:\code\structuremap\Source\StructureMap\Container.cs:155
Banana.Security.MembershipLoginServiceFactory.get_Instance() in E:\New\Projects\Banana\trunk\Banana.Security\MembershipLoginServiceFactory.cs:19
Banana.Security.SecurePageAttribute..ctor() in E:\New\Projects\Banana\trunk\Banana.Security\SecurePageAttribute.cs:26
System.RuntimeTypeHandle.CreateCaInstance(RuntimeType type, IRuntimeMethodInfo ctor) +0
System.Reflection.CustomAttribute.GetCustomAttributes(RuntimeModule decoratedModule, Int32 decoratedMetadataToken, Int32 pcaCount, RuntimeType attributeFilterType, Boolean mustBeInheritable, IList derivedAttributes, Boolean isDecoratedTargetSecurityTransparent) +1320
System.Reflection.CustomAttribute.GetCustomAttributes(RuntimeType type, RuntimeType caType, Boolean inherit) +235
EPiServer.PlugIn.AssemblyTypeInfo.TypeHasPlugInAttributes(Type type) +50
EPiServer.PlugIn.PlugInRuntime.InitRunTimePlugins(Boolean reset) +63
EPiServer.PlugIn.PlugInRuntime.Start(Boolean reset) +67
EPiServer.Web.InitializationModule.Initialize(InitializationEngine context) +46
EPiServer.Framework.Initialization.InitializationEngine.Initialize(HostType hostType) +115
EPiServer.Framework.Initialization.InitializationModule.Initialize(HostType hostType) +199
EPiServer.Framework.Initialization.InitializationModule.FrameworkInitialization(HostType hostType) +64
Maybe a stupid question, but do you have to use ObjectFactory in the attribute's ctor?
Ideally I need some way of injecting the dependency into the attribute. What other options are there?
I can't really do that - as the dependency is in an attribute which is applied to ActionResults / controllers etc. The issue here is that the attribute gets instantiated before the container has been setup by the EPiServer initialization scanning from custom attributes. I guess I could refactor my code to bring that logic into every controller action that requires it. Not very nice though.Anyway Mr Forsberg has come up with a very sensible suggestion - which was using ObjectFactory.TryGetInstance<> - knowing that the only time it wouldn't resolve was during the Initialization where it doesn't really matter.Ideally though - EPiServer should:
Just for the record though, I was thinking you'd overide the OnAuthorization or AuthorizeCore methods in which you could request the dependency from ObjectFactory. Sorry for not saying that more clearly before, I was posting from my phone :)
I agree regarding the assembly scanning. Although from the little research I did it should work. But to me, these issues are on Microsoft for relying so heavily on attributes for authorization as that's not very flexible.
Yea, I agree with what Joel is saying. If you implement IAuthorizationFilter you should be able to call you container in the OnAuthorization method and do nothing regarding the container in the constructor.
Even thought this doesn't help with the current issue, I would like to mention that all different assembly scannings that is done in the 6 R2 products has since been refactored to use a single scan. There will also be another method available for setting up any IoC containers before the main framework initialization cycle is run. These improvements will be available in the next major release.