Reputation: 1058
I have a WebAPI service using SimpleInjector. I have this set up using AsyncScopedLifestyle
for my scoped dependencies, and one of these dependencies is my Entity Framework DataContext
. Many things in my service depend on the DataContext
, and it is generally injected in to my MediatR handlers using constructor injection - this works well. Separately I have a few areas where I need to create an instance of an object given its type (as a string), so I have created a custom activator class (ResolvingActivator
) that is configured with a reference to Container.GetInstance(Type)
:
In my container bootstrap code:
ResolvingActivator.Configure(container.GetInstance);
I can then create objects by using methods such as:
ResolvingActivator.CreateInstance<T>(typeName)
When I'm using WebAPI, the above is working perfectly.
A further part of the project is a legacy API that uses WCF. I have implemented this as a translation layer, where I translate old message formats to new message formats and then dispatch the messages to the Mediator; I then translate the responses (in new format) back to old format and return those to the caller. Because I need access to the Mediator in my WCF services, I'm injecting this in their constructors, and using the SimpleInjector.Integration.Wcf
package to let SimpleInjector's supplied SimpleInjectorServiceHostFactory
build instances of the services. I've also created a hybrid lifestyle, so I can use the same container for my both my WebAPI and WCF services:
container.Options.DefaultScopedLifestyle = Lifestyle.CreateHybrid(
new AsyncScopedLifestyle(),
new WcfOperationLifestyle());
This works well for some calls, but when a call ultimately calls my ResolvingActivator
class, I get an ActivationException
thrown, with the following message:
The DataContext is registered as 'Hybrid Async Scoped / WCF Operation' lifestyle, but the instance is requested outside the context of an active (Hybrid Async Scoped / WCF Operation) scope.
As I only receive this error when making WCF calls, I'm wondering if I have something wrong in my configuration. In a nutshell, this will work:
public class SomeClass
{
private readonly DataContext db;
public SomeClass(DataContext db)
{
this.db = db;
}
public bool SomeMethod() => this.db.Table.Any();
}
But this will not:
public class SomeClass
{
public bool SomeMethod()
{
// Code behind is calling container.GetInstance(typeof(DataContext))
var db = ResolvingActivator.CreateInstance<DataContext>();
return db.Table.Any();
}
}
Any ideas where I'm going wrong?
Edit: here is the stack trace from the ActivationException
:
at SimpleInjector.Scope.GetScopelessInstance[TImplementation](ScopedRegistration`1 registration)
at SimpleInjector.Scope.GetInstance[TImplementation](ScopedRegistration`1 registration, Scope scope)
at SimpleInjector.Advanced.Internal.LazyScopedRegistration`1.GetInstance(Scope scope)
at lambda_method(Closure )
at SimpleInjector.InstanceProducer.GetInstance()
at SimpleInjector.Container.GetInstance(Type serviceType)
at Service.Core.ResolvingActivator.CreateInstance(Type type) in Service.Core\ResolvingActivator.cs:line 43
at Service.Core.ResolvingActivator.CreateInstance(String typeName) in Service.Core\ResolvingActivator.cs:line 35
at Service.Core.ResolvingActivator.CreateInstance[TService](String typeName) in Service.Core\ResolvingActivator.cs:line 69
With a full stack trace here: https://pastebin.com/0WkyHGKv
Upvotes: 1
Views: 469
Reputation: 172826
After close inspection of the stack trace, I can conclude what's going on: async.
The WcfOperationLifestyle
under the covers depends on WCF's OperationContext.Current
property, but this property has a thread-affinity and doesn't flow with async operations. This is something that has to be fixed in the integration library for Simple Injector; it simply doesn't support async at the moment.
Instead, wrap a decorator around your handlers that start and end a new async scope. This prevents you from having to use the WcfOperationLifestyle
all together. Take a look at the ThreadScopedCommandHandlerProxy<T>
implementation here to get an idea how to do this (but use AsyncScopedLifestyle
instead).
Upvotes: 1