seesharper / LightInject.Interception

LightInject.Interception supports Aspect Oriented Programming through proxy-based method interceptors.
11 stars 7 forks source link

Intermittent: Value cannot be null.Parameter name: con #1

Closed seesharper closed 8 years ago

seesharper commented 8 years ago

From @cephius473 on December 4, 2015 10:33

Hi,

I am currently using: id="Castle.Core" version="3.3.0" targetFramework="net45" id="LightInject" version="3.0.2.6" targetFramework="net45" id="LightInject.Interception" version="1.0.0.8" targetFramework="net45"

Setup:

 public class MyClass : BaseClass
 {
 }

 public abstract class BaseClass : IDisposable
 {
    public virtual void Dispose()
    {
    }
 }

T = MyClass

My code goes like this:

 public void Test()
 {            
      _serviceContainer.Register<T>();            
      _serviceContainer.Intercept(
                     sr => sr.ServiceType == typeof(T),
                     (factory, definition) => Instance.defineProxyType(definition, new MyInterceptor()));

      T instance = default(T);

        using (Instance._serviceContainer.BeginScope())
        {
            instance = (T)(Instance._serviceContainer.TryGetInstance<T>());
        }
 }

 private void defineProxyType(ProxyDeefinition definition, IInterceptor myInterceptor)
 {
      definition.Implement(
               () => myInterceptor,
               m =>m.IsDeclaredBy(definition.TargetType) && m.IsPublic)
 }

When calling the TryGetInstance, intermittently, I encounter the error. Also for GetInstance

Trying to figure out It seems when this is called, targetField is null

 private void PushProxyInstanceIfReturnValueEqualsTargetInstance(ILGenerator il, Type returnType)
         {
             var returnValueVariable = il.DeclareLocal(returnType);
             il.Emit(OpCodes.Stloc, returnValueVariable);
             var lazyTargetType = typeof(Lazy<>).MakeGenericType(proxyDefinition.TargetType);
             var getValueMethod = lazyTargetType.GetMethod("get_Value");
             il.Emit(OpCodes.Ldarg_0);
             il.Emit(OpCodes.Ldfld, targetField);

....

Then I became confused because the method which sets value for targetField, will always return since the proxyDefinition.TargetType.IsClass is true

  private void DefineTargetField()
  {
       if (proxyDefinition.TargetType.IsClass)
        {
             return;
        }

        Type targetFieldType;
        if (proxyDefinition.UseLazyTarget)
        {
            targetFieldType = typeof(Lazy<>).MakeGenericType(proxyDefinition.TargetType);    
        }
        else
        {
            targetFieldType = proxyDefinition.TargetType;
        }

        targetField = typeBuilder.DefineField("target", targetFieldType, FieldAttributes.Private);            
    }

Please correct me if I'm wrong at this.

might be related to: https://github.com/seesharper/LightInject/issues/201

StackTrace: at System.Reflection.Emit.ModuleBuilder.GetFieldTokenNoLock(FieldInfo field) at System.Reflection.Emit.ModuleBuilder.GetFieldToken(FieldInfo field) at System.Reflection.Emit.ILGenerator.Emit(OpCode opcode, FieldInfo field) at LightInject.Interception.ProxyBuilder.PushProxyInstanceIfReturnValueEqualsTargetInstance(ILGenerator il, Type returnType) in c:\GitHub\LightInject\NuGet\Build\Net45\LightInject.Interception\LightInject.Interception.cs:line 1512 at LightInject.Interception.ProxyBuilder.PushReturnValue(ILGenerator il, Type returnType) in c:\GitHub\LightInject\NuGet\Build\Net45\LightInject.Interception\LightInject.Interception.cs:line 1496 at LightInject.Interception.ProxyBuilder.ImplementInterceptedMethod(MethodInfo targetMethod, Int32[] interceptorIndicies) in c:\GitHub\LightInject\NuGet\Build\Net45\LightInject.Interception\LightInject.Interception.cs:line 1712 at LightInject.Interception.ProxyBuilder.ImplementMethod(MethodInfo targetMethod) in c:\GitHub\LightInject\NuGet\Build\Net45\LightInject.Interception\LightInject.Interception.cs:line 1680 at LightInject.Interception.ProxyBuilder.ImplementMethods() in c:\GitHub\LightInject\NuGet\Build\Net45\LightInject.Interception\LightInject.Interception.cs:line 1670 at LightInject.Interception.ProxyBuilder.GetProxyType(ProxyDefinition definition) in c:\GitHub\LightInject\NuGet\Build\Net45\LightInject.Interception\LightInject.Interception.cs:line 1233 at LightInject.InterceptionContainerExtensions.CreateProxyType(Type serviceType, Type[] additionalInterfaces, IServiceFactory serviceFactory, Action2 defineProxyType, ServiceRegistration registration) in c:\GitHub\LightInject\NuGet\Build\Net45\LightInject.Interception\LightInject.Interception.cs:line 136 at LightInject.InterceptionContainerExtensions.CreateProxyServiceRegistration(ServiceRegistration registration, Type[] additionalInterfaces, IServiceFactory serviceFactory, Action2 defineProxyType) in c:\GitHub\LightInject\NuGet\Build\Net45\LightInject.Interception\LightInject.Interception.cs:line 171 at LightInject.InterceptionContainerExtensions.<>cDisplayClass3.b2(IServiceFactory serviceFactory, ServiceRegistration registration) in c:\GitHub\LightInject\NuGet\Build\Net45\LightInject.Interception\LightInject.Interception.cs:line 66 at LightInject.ServiceContainer.EmitNewInstanceWithDecorators(ServiceRegistration serviceRegistration, IEmitter emitter) in c:\GitHub\LightInject\NuGet\Build\Net45\LightInject\LightInject.cs:line 3114 at LightInject.ServiceContainer.<>cDisplayClass9e.b9a(IEmitter methodSkeleton) in c:\GitHub\LightInject\NuGet\Build\Net45\LightInject\LightInject.cs:line 3739 at LightInject.ServiceContainer.<>cDisplayClass39.b38(IEmitter ms) in c:\GitHub\LightInject\NuGet\Build\Net45\LightInject\LightInject.cs:line 3059 at LightInject.ServiceContainer.CreateDynamicMethodDelegate(Action`1 serviceEmitter) in c:\GitHub\LightInject\NuGet\Build\Net45\LightInject\LightInject.cs:line 2980 at LightInject.ServiceContainer.CreateDelegate(Type serviceType, String serviceName, Boolean throwError) in c:\GitHub\LightInject\NuGet\Build\Net45\LightInject\LightInject.cs:line 3824 at LightInject.ServiceContainer.CreateDefaultDelegate(Type serviceType, Boolean throwError) in c:\GitHub\LightInject\NuGet\Build\Net45\LightInject\LightInject.cs:line

Copied from original issue: seesharper/LightInject#229

seesharper commented 8 years ago

Should be covered by #229 now being fixed

seesharper commented 8 years ago

From @ilyabreev on January 25, 2016 16:25

Issue is still present. Reproduced it with latest version of LightInject.Interception.cs from this repo. "Value cannot be null. Parameter name: con". In fact, targetField is null when passed to il.Emit(OpCodes.Ldfld, targetField); in the PushProxyInstanceIfReturnValueEqualsTargetInstance

No matter what interceptor's Invoke body is.

seesharper commented 8 years ago

From @ilyabreev on January 25, 2016 16:29

    public static class CompositionRoot
    {
        public static ServiceContainer CreateContainer()
        {
            var container = new ServiceContainer();
            container.Register(typeof(CouchbaseClient), new PerContainerLifetime());
            container.Intercept(r => r.ServiceType == typeof(CouchbaseClient), sf => new CouchbaseClientProfiler());
            return container;
        }
    }

    public class CouchbaseClientProfiler : IInterceptor
    {
        public object Invoke(IInvocationInfo invocationInfo)
        {
            return invocationInfo.Proceed();
        }
    }

CouchbaseClient is class from Couchbase .NET SDK.

seesharper commented 8 years ago

Take a look at the test in file Issue_1. Please modify to make a failing test

ilyabreev commented 8 years ago

In example with MyClass and BaseClass test passes. But if I change MyClass to CouchbaseClient, test fails. Failing test:

        public void ShouldInterceptDisposeFromBaseClass()
        {
            var container = new ServiceContainer();
            container.Register<CouchbaseClient>();
            container.Intercept(
                           sr => sr.ServiceType == typeof(CouchbaseClient), 
                           (factory, definition) => DefineProxyType(definition, new SampleInterceptor()));
            var test = container.GetInstance<CouchbaseClient>();
            test.Dispose();
        }

If I comment Intercept call, test passes.

seesharper commented 8 years ago

Have you tried to create a new instance of the CouchbaseClient

var client = new CouchbaseClient();

This threw an NRE inside CouchbaseClient

Adding a bit of config solved this

<?xml version="1.0"?>
<configuration>
  <configSections>
    <section name="couchbase" type="Couchbase.Configuration.CouchbaseClientSection, Couchbase"/>
  </configSections>
  <couchbase>
    <servers bucket="default" bucketPassword="">
      <add uri="http://192.168.0.2:8091/pools"/>
      <add uri="http://192.168.0.3:8091/pools"/>
    </servers>
  </couchbase>
</configuration>

Then I was able to create an instance of the client and it also worked with the container using interception.

ilyabreev commented 8 years ago

Config was there already. And I can create a new instance of the CouchbaseClient and get new instance through serviceContainer. But I get an exception when add interception.

seesharper commented 8 years ago

Are u sure that you are running the LightInject.Interception 1.0.0.10 package?. If so, you need to send me the simplest thing possible to recreate the issue. bernhard.richter@gmail.com along with all binaries from couchbase.

ilyabreev commented 8 years ago

Using LightInject.Interception 1.0.0.8. Is there 1.0.0.10 for .NET 4.0?

seesharper commented 8 years ago

Nope. .net 40 is end of life as of this January

On Tuesday, 26 January 2016, Ilya Breev notifications@github.com wrote:

Using LightInject.Interception 1.0.0.8. Is there 1.0.0.10 for .NET 4.0?

— Reply to this email directly or view it on GitHub https://github.com/seesharper/LightInject.Interception/issues/1#issuecomment-174951034 .

seesharper commented 8 years ago

http://blogs.msdn.com/b/dotnet/archive/2015/12/09/support-ending-for-the-net-framework-4-4-5-and-4-5-1.aspx

Note that you can create a project targeting > 4.5.2 and still reference a 4.0 assembly which seems to be the case with couchbaseclient.

ilyabreev commented 8 years ago

Thanks for help!