Monday 8 August 2011

Nhibernate 3.2 and medium trust environments

Note: There is a new blog post based on this one, however you should read this one first to get an idea of the problems.
I am trying out NHibernate in a medium trust environment using the built in DefaultProxy. However I get the following error:-
Operation could destabilize the runtime. 

Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code. 

Source File: ProxyFactory.cs Line: 51 
Stack Trace: 
 [VerificationException: Operation could destabilize the runtime.] CmsContentProxy..ctor() +27 
 [TargetInvocationException: Exception has been thrown by the target of an invocation.] 
   System.RuntimeTypeHandle.CreateInstance(RuntimeType type, Boolean publicOnly, Boolean noCheck, Boolean& canBeCached, 
   RuntimeMethodHandleInternal& ctor, Boolean& bNeedSecurityCheck) +0
   System.RuntimeType.CreateInstanceSlow(Boolean publicOnly, Boolean skipCheckThis, Boolean fillCache) +98 
   System.RuntimeType.CreateInstanceDefaultCtor(Boolean publicOnly, Boolean skipVisibilityChecks, Boolean skipCheckThis, Boolean fillCache) +241 
   System.Activator.CreateInstance(Type type, Boolean nonPublic) +69 
   System.Activator.CreateInstance(Type type) +6 
   NHibernate.Proxy.DynamicProxy.ProxyFactory.CreateProxy(Type instanceType, IInterceptor interceptor, Type[] baseInterfaces) in ProxyFactory.cs:51 
   NHibernate.Proxy.DefaultProxyFactory.GetProxy(Object id, ISessionImplementor session) in DefaultProxyFactory.cs:20
Line 51 within the NHibernate sources:-
/Proxy/DyanmicProxy/ProxyFactory.cs line 51 
  object result = Activator.CreateInstance(proxyType); 
Things I have tried:-
1. Castle Proxy - They have created a proxy for 3.1 and not 3.2. I have looked at the NHibernate sources for both 3.1 and 3.2 but the interface to implement has changed.
2. Googled how to work around this problem but to no avail
3. Asked the question on NHusers user group.

I could wait to see if the castle/linfu/spring teams release a proxy that is compatible with version 3.2 however it would be nice to identify the problem within NHibernate itself so as I don't need a dependency on a third party proxy.

If anyone is interested in helping me I have provided a policy.config file, all you need to do is to place it in your web root and place this in your web.config.
<securityPolicy> 
  <trustLevel name="Custom" policyFile="policy.config" /> 
</securityPolicy> 
<trust level="Custom" originUrl="" /> 
I really would love to get this NH3.2 running in Medium trust as I love the new mapping by code syntax, any ideas, pointers, workarounds will be gratefully received.

Whats next:-
I can see that Spring has now updated there framework so as to be compatible with NH3.2 GA, I will try this next...

5 comments:

  1. try making your many-to-one lazy=false. The underlying cause is the GetObjectData implementation in the proxy factory requires serialization. If you must lazy load the many-to-one associations, implement your own proxy interfaces.

    ReplyDelete
  2. The problem is more deeper than that as session.load throws an exception as well. I will need to use a custom proxy framework so waiting for linfu/spring/castle to catch up with release 3.2

    ReplyDelete
  3. I recreated the issue using your policy file. The exception thrown is "Security accessibility of the overriding method must match the security accessibility of the method being overriden". This is because the proxy factory is creating a new in-memory assembly to hold the emitted derived class (proxy) to store in the proxy cache. Any entity load that needs to create a proxy for lazy initialization can trigger the exception.

    I tested by mapping a recursive bi-directional cat->kittens, then queried a kitten. NHibernate will create a proxy for the parent cat in this case, and this will trigger the exception. Switching the many-to-one lazy=false will fetch the parent cat instead of creating the proxy, so no exception.

    I guess the important point is that any proxy creation will trigger the exception.

    I have implemented my own bytecode provider for DI with NHibernate. It still uses the default proxy factory, but I will be looking into rolling my own this evening to see if I can solve this problem. I'll let you know if I make any progress.

    ReplyDelete
  4. This is what peverify gave me on the saved-off generated assembly containing the dynamic proxy. I'll see about correcting the implementation tomorrow. The good news id this was the only error... we'll see.

    [IL]: Error: [c:\Users\Randy\generatedassembly.dll : CatProxy::.ctor][mdToken=0x6000001][offset 0x00000001][found ref ('this' ptr) 'CatProxy'] Call to .ctor only allowed to initialize this pointer from within a .ctor. Try newobj.

    ReplyDelete
  5. Success! here's the problem...

    NHibernate's ProxyFactory issues a call to Object::.ctor as the base of the proxy (which it isn't, In my case it is the Cat class).

    "IL.Emit(OpCodes.Call, baseConstructor);"

    The simple correction is to replace with emitting a call to the real base type's .ctor like so
    "IL.Emit(OpCodes.Call, baseType.GetConstructor(new System.Type[]{}));"

    Unfortunatly, this can have a negative impact on those of us that use constructor DI because the real base class may not have a default constructor.

    I am going to do a little more research on supporting entity classes without a default constructor.

    ReplyDelete