unitycontainer / interception Goto Github PK
View Code? Open in Web Editor NEWUnity.Interception package
License: Apache License 2.0
Unity.Interception package
License: Apache License 2.0
Type NamedTypeBuildKey
is being replaced with struct Contract
In our custom TearDown strategy we get property values of dependency via reflection. But with enabled interception we get an exception TargetException
:
Object does not match target type
when calling property.GetValue(transparent_proxy_obj, null)
Hi,
I am trying to update the Unity from 4.0 to 5.10, and my logging interceptor did not work, can somebody give me some idea? Thanks.
My Container setup/
container.Register<ICallHandler, LogCallHandler>(nameof(LogCallHandler));
container.AddNewExtension<Interception>();
and all my interface-implementaion registrations are going through the following helper function.
if (typeof(Service).IsInterface)
{
injectionMembers.Add(new InterceptionBehavior<PolicyInjectionBehavior>());
injectionMembers.Add(new Interceptor<InterfaceInterceptor>());
}
var lifetimeManager = registerAsSingleton ? new ContainerControlledLifetimeManager() : null;
container.RegisterType<Service, Implementation>(name, lifetimeManager, injectionMembers.ToArray());
and my targetted class are annotated with a HandlerAttribute
Due to the change in how types are created late interceptor additions no longer supported.
Unity makes all decisions about how to create object during registration. These methods do not register types but merely add indicator policies to already registered types and no longer work as expected.
Why not support netcoreapp2.0 and netstandard2.0
@cshung wrote:
Consider the following code snippet
using Microsoft.Practices.Unity;
using Microsoft.Practices.Unity.InterceptionExtension;
using System;
using System.Collections.Generic;
namespace ConsoleApplication1
{
public interface IService
{
void work();
}
public class Service : IService
{
public void work() { a(); }
private void a() { b(); }
private void b() { c(); }
private void c() { throw new NotImplementedException { }; ; }
}
public class Interceptor : IInterceptionBehavior
{
public bool WillExecute { get { return true; } }
public IEnumerable<Type> GetRequiredInterfaces() { return Type.EmptyTypes; }
public IMethodReturn Invoke(IMethodInvocation input, GetNextInterceptionBehaviorDelegate getNext)
{
Console.WriteLine("Enter invoke");
var result = getNext()(input, getNext);
Console.WriteLine("Exit invoke");
return result;
}
}
public class UnityStackTraceMain
{
static void Main(string[] args)
{
var container = new UnityContainer { };
container.AddNewExtension<Interception>();
//container.AddNewExtension<AppContainerExtension>();
container.RegisterType<IService, Service>(
new ContainerControlledLifetimeManager(),
new Interceptor<InterfaceInterceptor>(),
new InterceptionBehavior<Interceptor>());
try
{
container.Resolve<IService>().work();
}
catch (System.Exception ex)
{
Console.WriteLine(ex.ToString());
Console.WriteLine(ex.InnerException);
}
Console.ReadLine();
}
}
}
We expect the ex.ToString() has the frames a(), b() and c(), but instead, it only have the dynamic generated function.
The root cause of this is because the exception is caught and thrown again. .NET does not preserve the stack trace in the exception in that case and is clearly documented.
The proper way to fix this is to wrap the exception. In order to make existing code work with the fix. I wrap the exception with the same type and same message, that should allow most code to work with it.
Since the interceptor code is not available in this repo, I created the pull request to fix this in CodePlex instead.
https://unity.codeplex.com/SourceControl/network/forks/cshung/unity/contribution/8726
The following code illustrates the problem:
public interface ITestCalls
{
void Test1();
}
public abstract class BaseTestCalls : ITestCalls
{
public void Test()
{
}
}
[Tag("track")]
public class TrackTestCalls : BaseTestCalls
{
}
[Tag("track1")]
public class TrackTestCalls1 : ITestCalls
{
public void Test()
{
}
}
static void Main(string[] args)
{
IUnityContainer container = new UnityContainer();
container.AddNewExtension<Interception>();
container
.Configure<Interception>()
.AddPolicy("track")
.AddMatchingRule<TagAttributeMatchingRule>(
new InjectionConstructor("track", false))
.AddCallHandler<TrackCallHandler>(
new ContainerControlledLifetimeManager())
;
container
.RegisterType<ITestCalls, TrackTestCalls>(
"track",
new InterceptionBehavior<PolicyInjectionBehavior>(),
new Interceptor<TransparentProxyInterceptor>())
.RegisterType<ITestCalls, TrackTestCalls1>(
"track1",
new InterceptionBehavior<PolicyInjectionBehavior>(),
new Interceptor<TransparentProxyInterceptor>())
;
var test = container.Resolve<ITestCalls>("track");
test.Test();
var test1 = container.Resolve<ITestCalls>("track1");
test1.Test();
}
test.Test() does not exhibit the decorated behavior and the object test is not supplied with a pipeline. However test1.Test() behaves as expected and test1 has the pipeline.
I could not find this being documented anywhere.
When configuring an interception policy that uses a CallHandler
which it depends upon injection of other services registered in the container, the lifetime is ignored and the same instance of the call handler is used for different resolution contexts.
This used to work fine up to:
But started failing on the next release:
See the following test
[TestMethod]
public void TestLifetimeManagement()
{
//Container setup
var container = new UnityContainer();
container.AddNewExtension<Interception>();
container.RegisterType<ILog, Log>(new TransientLifetimeManager());
container.RegisterType<ITestService, TestService>(new TransientLifetimeManager(),
new InterceptionBehavior<PolicyInjectionBehavior>(), new Interceptor<VirtualMethodInterceptor>());
container.Configure<Interception>()
.AddPolicy("logging")
.AddMatchingRule(new TypeMatchingRule(typeof(TestService)))
.AddCallHandler<LogCallHandler>(
new TransientLifetimeManager(),
new InjectionConstructor(new ResolvedParameter<ILog>()),
new InjectionProperty("Order", 1)
);
var testService1 = container.Resolve<ITestService>();
testService1.GetValues();
Assert.AreEqual(1, LogCallHandler.InstanceCount);
var testService2 = container.Resolve<ITestService>();
testService2.GetValues();
Assert.AreEqual(2, LogCallHandler.InstanceCount);
}
And considder that the LogCallHandler
is defined as
public class LogCallHandler : ICallHandler
{
public static int InstanceCount { get; private set; }
private readonly ILog log;
public LogCallHandler(ILog log)
{
this.log = log;
ObjectInstance = ++InstanceCount;
}
public int Order { get; set; }
public int ObjectInstance { get; }
public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext)
{
var method = $"{input.MethodBase.DeclaringType}.{input.MethodBase.Name}";
log.Write($"{method} - starting invocation by call handler instance {ObjectInstance}");
var result = getNext()(input, getNext);
log.Write($"{method} - invocation ended by call handler instance {ObjectInstance}");
return result;
}
}
Depending on the set of libraries used (as described on the first section) the second assert will succeed or fail.
You'd expect that the call handler is resolved by the container in the same way as any other type, according to what the lifetime manager dictates.
Additional context
This might look trivial at a first glance, but when using a LifetimeManager such as the PerRequestLifetimeManager
and when the injected member for the CallHandler is something that can't be treated as a singleton, a big problem arises (for instance: the object will be already disposed when the call handler tryes to use it on the second round).
I have both a unit test project as well as a realistic web application with this behavior which I'm happy to provide. I've tried to go into the belly of Unity to understand this better, but got a bit lost when doing so. If I happen to discover more details on the source of the problem will update this (and if I get it right, even send you a pull request).
We are currently finding an issue where we are testing a DC failover and are getting null exceptions when we fail over to another DC. The app recovers, but the first few hits give us this error:
problemId: System.NullReferenceException at Unity.Interception.Interceptors.GenericParameterMapper.Map
method: Unity.Interception.Interceptors.GenericParameterMapper.Map
outerType: Unity.Exceptions.ResolutionFailedException
assembly: Unity.Interception, Version=5.5.3.0, Culture=neutral, PublicKeyToken=489b6accfaf20ef0
outerAssembly: Unity.Container, Version=5.8.6.0, Culture=neutral, PublicKeyToken=489b6accfaf20ef0
It's resolving a type that is clearly defined and works, but the initial hits fail and we don't know why.
Here is the stack trace:
[
{
"parsedStack": [
{
"assembly": "Unity.Container, Version=5.8.6.0, Culture=neutral, PublicKeyToken=489b6accfaf20ef0",
"method": "Unity.UnityContainer.ThrowingBuildUp",
"level": 0,
"line": 0
},
{
"assembly": "Unity.Container, Version=5.8.6.0, Culture=neutral, PublicKeyToken=489b6accfaf20ef0",
"method": "Unity.UnityContainer.Resolve",
"level": 1,
"line": 0
},
{
"assembly": "Unity.Abstractions, Version=3.3.0.0, Culture=neutral, PublicKeyToken=6d32ff45e0ccc69f",
"method": "Unity.UnityContainerExtensions.Resolve",
"level": 2,
"line": 0
},
{
"assembly": "Company.Cs.ObjectFactory.Unity, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null",
"method": "Company.Cs.ObjectFactory.Unity.ObjectFactory.Create",
"level": 3,
"line": 0
},
{
"assembly": "Company.Cs.Web.Mvc, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null",
"method": "Company.Cs.Web.Mvc.Filters.SingleSignOnAuthorizationFilter.get_TokenStore",
"level": 4,
"line": 0
},
{
"assembly": "Company.Cs.Web.Mvc, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null",
"method": "Company.Cs.Web.Mvc.Filters.SingleSignOnAuthorizationFilter.OnAuthorization",
"level": 5,
"line": 0
},
{
"assembly": "Company.Cs.InternalChat.Web.Ui, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null",
"method": "Company.Cs.InternalChat.Web.Ui.Filters.SsoAuthorizationFilter.OnAuthorization",
"level": 6,
"line": 0
},
{
"assembly": "System.Web.Mvc, Version=5.2.6.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35",
"method": "System.Web.Mvc.ControllerActionInvoker.InvokeAuthorizationFilters",
"level": 7,
"line": 0
},
{
"assembly": "System.Web.Mvc, Version=5.2.6.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35",
"method": "System.Web.Mvc.Async.AsyncControllerActionInvoker+<>c__DisplayClass3_1.b__0",
"level": 8,
"line": 0
}
],
"outerId": "0",
"message": "Resolution of the dependency failed, type = 'Company.Cs.Interfaces.TokenStores.ISingleSignOnTokenStore', name = '(none)'.\nException occurred while: while resolving.\nException is: NullReferenceException - Object reference not set to an instance of an object.\n-----------------------------------------------\nAt the time of the exception, the container was: \r\n Resolving Company.Cs.Interfaces.TokenStores.ISingleSignOnTokenStore,(none)\r\n",
"type": "Unity.Exceptions.ResolutionFailedException",
"id": "9781729"
},
{
"parsedStack": [
{
"assembly": "Unity.Interception, Version=5.5.3.0, Culture=neutral, PublicKeyToken=489b6accfaf20ef0",
"method": "Unity.Interception.Interceptors.GenericParameterMapper.Map",
"level": 0,
"line": 0
},
{
"assembly": "Unity.Interception, Version=5.5.3.0, Culture=neutral, PublicKeyToken=489b6accfaf20ef0",
"method": "Unity.Interception.Interceptors.InstanceInterceptors.InterfaceInterception.InterfaceMethodOverride.CreateDelegateImplementation",
"level": 1,
"line": 0
},
{
"assembly": "Unity.Interception, Version=5.5.3.0, Culture=neutral, PublicKeyToken=489b6accfaf20ef0",
"method": "Unity.Interception.Interceptors.InstanceInterceptors.InterfaceInterception.InterfaceImplementation.Implement",
"level": 2,
"line": 0
},
{
"assembly": "Unity.Interception, Version=5.5.3.0, Culture=neutral, PublicKeyToken=489b6accfaf20ef0",
"method": "Unity.Interception.Interceptors.InstanceInterceptors.InterfaceInterception.InterfaceImplementation.Implement",
"level": 3,
"line": 0
},
{
"assembly": "Unity.Interception, Version=5.5.3.0, Culture=neutral, PublicKeyToken=489b6accfaf20ef0",
"method": "Unity.Interception.Interceptors.InstanceInterceptors.InterfaceInterception.InterfaceInterceptorClassGenerator.CreateProxyType",
"level": 4,
"line": 0
},
{
"assembly": "Unity.Interception, Version=5.5.3.0, Culture=neutral, PublicKeyToken=489b6accfaf20ef0",
"method": "Unity.Interception.Interceptors.InstanceInterceptors.InterfaceInterception.InterfaceInterceptor.CreateProxy",
"level": 5,
"line": 0
},
{
"assembly": "Unity.Interception, Version=5.5.3.0, Culture=neutral, PublicKeyToken=489b6accfaf20ef0",
"method": "Unity.Interception.Intercept.ThroughProxyWithAdditionalInterfaces",
"level": 6,
"line": 0
},
{
"assembly": "Unity.Interception, Version=5.5.3.0, Culture=neutral, PublicKeyToken=489b6accfaf20ef0",
"method": "Unity.Interception.ContainerIntegration.ObjectBuilder.InstanceInterceptionStrategy.PostBuildUp",
"level": 7,
"line": 0
},
{
"assembly": "Unity.Container, Version=5.8.6.0, Culture=neutral, PublicKeyToken=489b6accfaf20ef0",
"method": "Unity.UnityContainer.ThrowingBuildUp",
"level": 8,
"line": 0
}
],
"outerId": "9781729",
"message": "Object reference not set to an instance of an object.",
"type": "System.NullReferenceException",
"id": "20687649"
}
]
I've noticed some recent activity on these repos so I'm going to shoot my shot.
I am upgrading some code (that's an understatement) from Unity 3 to Unity 5 and have encountered what is either a bug or a significant behavioral difference regarding interception between the versions.
In Unity 5.11 (and likely earlier versions, I haven't explored deep enough to find where the issue starts), Unity will re-resolve objects registered with the "PerResolveLifetimeManager" when resolving objects needed for any defined interception behavior. I have verified that the same object will be used across the object graph for any reference that is not part of interception behavior.
Here is a test case that should demonstrate the issue. I tried another lifetime ("PerThread") to demonstrate that it does seem to specifically be an issue with "PerResolve".
UnityPerResolveInterceptionBrokenPoc.zip
I've pulled the interception repo and have been trying to uncover the issue, but I'm quite a bit out of my depth. Depending on the scope of the fix (I would have to evaluate fixing this vs other options for my team to consider), I'd be willing to submit a PR if I can get pointed in the right direction.
We are attempting to upgrade from Unity 2x to Unity 5.8.11. Everything appears to be working except that the memory allocated for CallHandlers associated with a class that uses a TransientLifetimeManager is not be released by Unity.
I have tried registering the class two different ways with the same result:
Is there any documentation about the Interception extension?
I was only able to find blog posts but they seem to use older versions of Unity
Thanks.-
Interface interceptor code generator fails when setting sign key of emitted assembly.
Current implementation follows the suit with Unity and exhibits the same behavior as described here
It associates Intercept information with ToType and as result suffers from the same error. Subsequent registrations could affect previously registered unrelated types.
Interception at one level works...
ControllerA(IBusinessServiceA businessService)
BusinessService is injected, I see WillExecute, GetRequiredInterfaces, Invoke all run
Interception 2-deep does not work...
ControllerB(IBusinessServiceB businessService)
BusinessServiceB(IDal dal)
I see Will Execute, GetRequiredInterfaces for Dal, and I see the DAL constructor fire, I see the BusinessServiceB constructor fire. WillExecute and GetRequiredInterfaces fire, then I get an exception.
container.RegisterType<IBusinessServiceA, BusinessServiceA>(new Interceptor<InterfaceInterceptor>(), new InterceptionBehavior<MyInterceptionBehaviorA>());
container.RegisterType<IBusinessServiceB, BusinessServiceB>(new Interceptor<InterfaceInterceptor>(), new InterceptionBehavior<MyInterceptionBehaviorA>());
container.RegisterType<IDal, Dal>(new Interceptor<InterfaceInterceptor>(), new InterceptionBehavior<MyInterceptionBehaviorB>());
Works fine with normal registration.
container.RegisterType<IBusinessServiceA, BusinessServiceA>();
container.RegisterType<IBusinessServiceB, BusinessServiceB>();
container.RegisterType<IDal, Dal>();
Here's the exception...
warn: Microsoft.AspNetCore.Server.Kestrel[0]
Overriding address(es) 'http://localhost:53382/'. Binding to endpoints defined in UseKestrel() instead.
Hosting environment: Development
Content root path: c:\users\scovel\Source\Repos\TestUnity\TestUnity
Now listening on: http://127.0.0.1:8088
Application started. Press Ctrl+C to shut down.
fail: Microsoft.AspNetCore.Server.Kestrel[13]
Connection id "0HLDAT7258BVT", Request id "0HLDAT7258BVT:00000001": An unhandled exception was thrown by the application.
System.InvalidOperationException: Unable to resolve service for type 'TestUnity.Implementations.IBusinessServiceB' while attempting to activ
ate 'TestUnity.Controllers.ValuesControllerB'.
at Microsoft.Extensions.Internal.ActivatorUtilities.GetService(IServiceProvider sp, Type type, Type requiredBy, Boolean isDefaultParamete
rRequired)
at lambda_method(Closure , IServiceProvider , Object[] )
at Microsoft.AspNetCore.Mvc.Controllers.ControllerActivatorProvider.<>c__DisplayClass4_0.b__0(ControllerContext controll
erContext)
at Microsoft.AspNetCore.Mvc.Controllers.ControllerFactoryProvider.<>c__DisplayClass5_0.g__CreateController|0(Con
trollerContext controllerContext)
at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.d__14.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.d__22.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.Rethrow(ResourceExecutedContext context)
at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.d__17.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.d__15.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNetCore.Builder.RouterMiddleware.d__4.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Swashbuckle.AspNetCore.SwaggerUI.SwaggerUIIndexMiddleware.d__3.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Swashbuckle.AspNetCore.Swagger.SwaggerMiddleware.d__6.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNetCore.Hosting.Internal.RequestServicesContainerMiddleware.d__3.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.Frame`1.d__2.MoveNext()
i think this issue happen again e684936 in 5.9.1.189 was rollback this code.
Hi,
Since the InjectionFactory is deprecated, I am trying to use the RegisterFactory method.
I have registrations which use a factory and also have interceptor(s):
Func<IUnityContainer, object> factoryFunc = c => new FileSystemWithWaitAndRetry(
new FileSystemService(container.Resolve<System.IO.Abstractions.IFileSystem>()),
container.Resolve<ILogger>());
container.RegisterType<IFileSystemService>(
createLifetimeManager(),
new InjectionFactory(factoryFunc),
new Interceptor<InterfaceInterceptor>(),
new InterceptionBehavior(new ImpersonationBehavior(sSettings.ImpersonatorDomain, sSettings.ImpersonatorUsername, sSettings.ImpersonatorPassword, container.Resolve<ILogger>())));
(This is a service for filesystem access with a 'wait and retry' decorator. Interception adds impersonation)
How can the RegisterType be rewritten to RegisterFactory and still pass the InterceptionBehaviour?
Is the RegisterFactory method missing some functionality?
Thanks!
RegisterInstance
method.Current: container returns original instance, all method calls go directly to that instance.
Expected: container returns a proxy wrapper, method calls can be handled by the interceptor.
Broken versions: 5.x (tried 5.9.0 and latest stable 5.11.1).
Working versions: 4.0.1 and older.
There is no problem with RegisterType
method, unity returns a proxy wrapper as expected in a such scenario. The issue affects only RegisterInstance
-way. Both default and named instances are being returned unwrapped.
Below is a unit test to demonstrate the issue, it works well with Unity versions up to 4.0.1 and fails with 5.x:
[TestFixture]
public class InterceptionTests
{
[Test]
public void RegisteredInstanceIntercepting_ReturnsProxy()
{
var container = new UnityContainer();
container.AddNewExtension<Interception>();
var instance = new SomeService();
container
.RegisterInstance<ISomeService>(instance)
.Configure<Interception>()
.SetInterceptorFor<ISomeService>(new TransparentProxyInterceptor())
.AddPolicy("SomePolicy")
.AddMatchingRule(new TypeMatchingRule(typeof(ISomeService)))
.AddCallHandler(new SomeCallHandler());
var wrapper = container.Resolve<ISomeService>();
Assert.IsTrue(RemotingServices.IsTransparentProxy(wrapper), "Wrapper is not a proxy");
Assert.AreNotSame(instance, wrapper);
}
private interface ISomeService
{
void Foo();
}
private sealed class SomeService : ISomeService
{
public void Foo() => throw new NotImplementedException();
}
private sealed class SomeCallHandler : ICallHandler
{
public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext) => throw new NotImplementedException();
public int Order { get; set; }
}
}
Is it enough for you or should I attach a zip with the whole project?
Am I missing some breaking change with interceptors between 4.x and 5.x?
Is there some workaround?
@kostemar Wrote:
I ran into a problem with the Unity.Interception extension (using Unity v4.0.1 and Unity.Interception v4.0.1) and VirtualMethodInterception.
Since the issue is a bit difficult to explain, I attached a console application that reproduces the issue. Basically, I have some "domain objects" that look something like this:
public interface IDataImportJob
{
void Run();
}
public abstract class DataImportBase : IDataImportJob
{
public virtual void Run()
{
OnRun();
}
protected abstract bool OnRun();
}
public interface IProductPartAndOptionMasterDataImport : IDataImportJob
{
}
public class ProductPartAndOptionMasterDataImport : DataImportBase, IProductPartAndOptionMasterDataImport
{
private readonly IOtherObject _otherObject;
public ProductPartAndOptionMasterDataImport(IOtherObject otherObject)
{
_otherObject = otherObject;
}
protected override bool OnRun()
{
var productsOk = RunProductPartAndOptionImport();
return productsOk;
}
protected virtual bool RunProductPartAndOptionImport()
{
int partsProcessed = 42;
ImportProductPartFromProduct(ref partsProcessed);
return true;
}
[TracingCallHandler(true, true, true)]
// if you remove the "partsProcessed" parameter here, the issue does not occur.
protected virtual void ImportProductPartFromProduct(ref int partsProcessed)
{
int someParameter = 0;
_otherObject.SomeMethod(ref someParameter);
}
}
public interface IOtherObject
{
void SomeMethod(ref int someArgument);
}
public class OtherObject : IOtherObject
{
public virtual void SomeMethod(ref int someArgument)
{
throw new Exception("Something bad happened.");
}
}
The TracingCallHandler is a CallHandler that can log and swallow exceptions.
Now, when I run the console application, the "Something bad happened" exception is logged as expected, but then I get a second exception
(System.ArgumentOutOfRangeException) that crashes the application.
I guess the problem is that somewhere in the dynamically generated code (DynamicModule.ns.Wrapped_ ...) a ParameterCollection is
created with an empty arguments array. Then, the indexer of that ParameterCollection is used with an index of 0, which will eventually result in that System.ArgumentOutOfRangeException.
If you remove the partsProcessed parameter from the ImportProductPartFromProduct method, the issue does not occur.
My usecase is that I'm trying to implement entry/exit/exception logging on an async method.
I an IInterceptionBehavoiur implementation, the call to getNext()(...) is performed asynchronously and I don't get to log the exit of the funciton.
Interface is required in order to retrieve ITypeInterceptor but it is not required and could be changed to simple IUnityContainer. This will allow to move interception analysis into registration phase and will noticeable boost performance.
The legacy way of calling this interface will be preserved via Extension method.
Interface is requires IBuilderContext
in order to retrieve IEnumerable<IInterceptionBehavior>
but it could be changed to use IUnityContainer. This will allow to move interception analysis into registration phase and will noticeable boost performance.
The legacy way of calling this interface will be preserved via Extension method.
It's not the same as unitycontainer/container#160, but perhaps it is related to each other.
Compile and run the following code for .NET Core
(I tried for 2.1) and Framework
(tried for 4.7.2). The output is different.
class Program
{
static void Main(string[] args)
{
var container = new UnityContainer()
.AddNewExtension<Interception>()
.RegisterType<Foo>()
.RegisterType<IBar, Bar>();
var child = container.CreateChildContainer()
.RegisterType<IFoo, Foo>(new ContainerControlledLifetimeManager())
.RegisterType<Foo>(new ContainerControlledLifetimeManager());
var registrations = child.Registrations.Where(r => r.RegisteredType == typeof(Foo));
foreach (var registration in registrations)
{
Console.WriteLine(
$"{registration.RegisteredType.Name}->" +
$"{registration.MappedToType.Name}" +
$"[{registration.LifetimeManager}]");
}
}
public interface IFoo { }
public class Foo : IFoo { }
public interface IBar { }
public class Bar : Foo, IBar { }
}
For Framework version the output is
Foo->Foo[Lifetime:PerContainer]
But for .NET Core version is:
Foo->Foo[Lifetime:Transient]
Foo->Foo[Lifetime:PerContainer]
For .NET Core
must be the same registration as for Framework
. The transient registration is obviously wrong.
@japrom wrote:
I have a pretty trivial example using latest on NuGet (Unity / Unity.Interception 4.0.1)
public interface IInterface<in TIn, out TOut>
{
TOut DoSomething(TIn input);
}
public class Thing : IInterface<string, string>
{
public string DoSomething(string input)
{
return input;
}
}
public class TestInterceptor : IInterceptionBehavior
{
public bool WillExecute => true;
public IEnumerable<Type> GetRequiredInterfaces()
{
return Type.EmptyTypes;
}
public IMethodReturn Invoke(IMethodInvocation input, GetNextInterceptionBehaviorDelegate getNext)
{
return getNext()(input, null);
}
}
public void Main(string[] args)
{
var proxy = Intercept.ThroughProxy<IInterface<string, string>>(
new Thing(),
new InterfaceInterceptor(),
new[] { new TestInterceptor() });
proxy.DoSomething("hello world");
}
Results in the exception:
TypeLoadException: Could not load type 'DynamicModule.ns.Wrapped_IInterface`2_f77b7d7f16c54c3393a389eedeced496' from assembly 'Unity_ILEmit_InterfaceProxies, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null' because it declares a covariant or contravariant type parameter and is not an interface or delegate.
I dug into the code a bit and found InterfaceInterceptorClassGenerator.cs.
Zeroing the *variant flags seems to make it work for me.
genericTypes[i].SetGenericParameterAttributes(
genericArguments[i].GenericParameterAttributes & ~GenericParameterAttributes.VarianceMask
);
Is there any reason why they should be there?
The problem is resolve slows down when an interceptor is set. Our company had several production incidents when it happens. It is clearly visible when resolve in multi-threaded with high degree of parallelism. If you want we can provide benchmark tests shown difference between current implementation and the suggested one (see below).
Let's suppose we have the following code:
var container = new UnityContainer()
.AddNewExtension<Interception>()
.RegisterType<IFoo, Foo>();
container
.Configure<Interception>()
.SetDefaultInterceptorFor<IFoo>(new InterfaceInterceptor());
container.Resolve<IFoo>(); // Resolve #1
container.Resolve<IFoo>(); // Resolve #2
Every resolve PipelineManager
is instantiated here. Then CreatePipeline
method invokes GetBaseDefinition()
twice here and here. GetBaseDefinition
uses inside locks that leads to slowing down of whole dependency resolves.
Base method definitions can be cached in static ConcurrentDictionary
, for example. So CreatePipeline
can be rewritten something like this:
private static readonly ConcurrentDictionary<HandlerPipelineKey, MethodInfo> BaseMethodDefinitions =
new ConcurrentDictionary<HandlerPipelineKey, MethodInfo>();
private HandlerPipeline CreatePipeline(MethodInfo method, IEnumerable<ICallHandler> handlers)
{
var key = HandlerPipelineKey.ForMethod(method);
if (_pipelines.TryGetValue(key, out var pipeline))
{
return pipeline;
}
var baseMethodDefinition = BaseMethodDefinitions
.GetOrAdd(key, pipelineKey => method.GetBaseDefinition());
if (baseMethodDefinition == method)
{
_pipelines[key] = pipeline = new HandlerPipeline(handlers);
return pipeline;
}
_pipelines[key] = pipeline = CreatePipeline(baseMethodDefinition, handlers);
return pipeline;
}
@ENikS If you want we can prepare a PR, so you do not spend time on it. Also, as I mentioned, we can provide benchmark tests of current solution and supposed one.
These methods are deprecated:
public Interception SetInterceptorFor(Type typeToIntercept, string? name, ITypeInterceptor interceptor)
public Interception SetInterceptorFor(Type typeToIntercept, string? name, IInstanceInterceptor interceptor)
I used the sample code to configure Unity with UseUnityServiceProvider() and ConfigureContainer(IUnityContainer container) in the Startup class. However, the interceptors don't trigger handlers. The same code with interceptors works fine in console applications for both .NET Core and .NET Framework.
The same issue is described here - https://stackoverflow.com/questions/50699219/how-to-use-unity-container-interception-in-asp-net-core-webapi
@mizasie wrote:
Hello,
We use strong named assemblies and internal interfaces/classes in our solutions. Unfortunately it is not possible to use Unity Interception in that case. It creates a dynamic assembly which is not signed and therefore not loaded. We figured out how easily that could be implemented. We did the change locally and rebuild Unity Interception by ourselves. It works great in our particular case.
Important hint: This is only an issue, if you work with internal interfaces/classes. You have to add the following line to your AssemblyInfo.cs
[assembly: InternalsVisibleTo("Unity_ILEmit_InterfaceProxies, PublicKey=002400000480000094000000060200000024000052534131000400000100010095d32d9c234a5c26a67e6f9e63c75501460914df0da0774b35cfd527aaaf774d8b751091c06d63e22d5704eafda93e7666bb7b446abfda77696aec773acff37d117ddd0e688a468505776c154915906181812fb3191b99d389dc5fc4faca70dc7113932ea239c9299edb068d157ab375e38408f54f1f9e69b69ae8e989daafb1")]
Have a look into the constructor without parameters:
https://github.com/unitycontainer/unity/blob/master/source/Unity.Interception/Src/Interceptors/InstanceInterceptors/InterfaceInterception/InterfaceInterceptorClassGenerator.cs
We changed it into that:
[SuppressMessage("Microsoft.Performance", "CA1810:InitializeReferenceTypeStaticFieldsInline",
Justification = "Need to use constructor so we can place attribute on it.")]
static InterfaceInterceptorClassGenerator()
{
var assemblyName = new AssemblyName("Unity_ILEmit_InterfaceProxies")
{
KeyPair = new StrongNameKeyPair(Resources.unity)
};
AssemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(
assemblyName,
#if DEBUG_SAVE_GENERATED_ASSEMBLY
AssemblyBuilderAccess.RunAndSave);
#else
AssemblyBuilderAccess.Run);
#endif
}
The key file itself could be found on the top level within the build
folder. Its name is “unity.snk”. We imported that file as resource file in Visual Studio and named it unity
. Therefore it’s possible to access the content with the command Resources.unity
.
It would be great to have that functionality in the official builds too.
Thanks for all your efforts.
Cheers
Micha
Hi,
The following error occurs when resolving the interface IADUtilities:
The type X.Business.Utilities.ADUtilitiesStubbed is not interceptable.
Parameter name: interceptedType
Exception occurred while:
·resolving type: 'ADUtilitiesStubbed'
•resolving type: 'IADUtilities' mapped to 'ADUtilitiesStubbed'
My setup:
public interface IAdUtilities {}
public class AdUtilities : IAdUtilities {}
public class AdUtilitiesStubbed : IAdUtilities {}
The registrations are build like:
1. Registration by convention:
var assemblies = System.IO.Directory.GetFiles(AppDomain.CurrentDomain.BaseDirectory, AssemblyConstants.AssemblyNamePrefix + "*.dll")
.Select(f => System.Reflection.Assembly.LoadFile(f));
container.RegisterTypes(
AllClasses.FromAssemblies(assemblies).Where(t => t.Namespace.StartsWith(AssemblyConstants.AssemblyNamePrefix, StringComparison.OrdinalIgnoreCase)),
WithMappings.FromMatchingInterface,
WithName.Default,
t => lifetimeManager());
Result of this:
2. Overwrite convention registrations by more advanced registration
container.RegisterType<IADUtilities, ADUtilities>(
createLifetimeManager(),
new Interceptor<InterfaceInterceptor>(),
new InterceptionBehavior<LogBehavior>(),
new InterceptionBehavior(new ImpersonationBehavior("mydomain", "myUsername", "myPassword", container.Resolve<Interfaces.ILogger>()))
);
Result of this:
The type X.Business.Utilities.ADUtilitiesStubbed is not interceptable.
Parameter name: interceptedType
Exception occurred while:
·resolving type: 'ADUtilitiesStubbed'
This is probably caused by the fact that interception is registered on the FromType (here IAdUtilities).
3. For unit testing, the standard registrations should be overwritten by stubbed classes
container.RegisterType<Business.Interfaces.Utilities.IADUtilities, Business.Utilities.ADUtilitiesStubbed>(createLifetimeManager());
Result of this:
The type X.Business.Utilities.ADUtilitiesStubbed is not interceptable.
Parameter name: interceptedType
Exception occurred while:
·resolving type: 'ADUtilitiesStubbed'
•resolving type: 'IADUtilities' mapped to 'ADUtilitiesStubbed'
It seems like the resolve happens in 2 steps:
Why does resolving the Stubbed class fail? This is not as expected (in old unity versions this works correct).
Can this be fixed please?
fyi: When reregistereing AdUtilitiesStubbed as last, resolve succeeds:
container.RegisterType<Business.Utilities.ADUtilitiesStubbed, Business.Utilities.ADUtilitiesStubbed>(createLifetimeManager());
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.