Container.GetInstance(Type) when using WcfOperationLifestyle throws ActivationException - wcf

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

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).

Related

Does MediatR walk all classes in the project looking for matches?

There are two parts to this question. First I am going to explain what I think is happening? And then second, in that explanation, I want to verify how it is finding a class it needs.
This is from Nick Chapsas' program Write cleaner APIs in .NET 7 with MediatR.
This code handles a request using MediatR as follows.
n program.cs it has:
app.MediateGet<ExampleRequest>("example/{name}");
which is called via the extension function:
public static WebApplication MediateGet<TRequest>(
this WebApplication app,
string template) where TRequest : IHttpRequest
{
app.MapGet(template, async (IMediator mediator,
[AsParameters] TRequest request) => await mediator.Send(request));
return app;
}
where IHttpRequest is defined as:
public interface IHttpRequest : IRequest<IResult>
{
}
So what has occurred from all this is me have an app.MapGet() call for the url "example/{name}" and it will be handles by MediatR.
ExampleRequest.cs is a POCO containing the URL parameters, etc.
The handler must implement the interface IRequestHandler<ExampleRequest, IResult>. When that url is requested, it will instantiate the appropiate class and call its Handle() method passing in the ExampleRequest and getting a Task returned.
Question 1: Is that correct?
There is the following class in the program that is a handler that implements the require interface. And it is called by MediatR.
public class ExampleHandler : IRequestHandler<ExampleRequest, IResult>
{
private readonly GuidService _guidService;
public ExampleHandler(GuidService guidService)
{
_guidService = guidService;
}
public async Task<IResult> Handle(
ExampleRequest request, CancellationToken cancellationToken)
{
await Task.Delay(10, cancellationToken);
return Results.Ok(new
{
message = $"The age was: {request.Age} and the name was: {request.Name}",
requestGuid = request.GuidService.Id,
ctorGuid = _guidService.Id
});
}
}
Question 2a: How does MediatR find this class? It is not passed to anything or registered with anything. Does MediatR scan all classes looking for a match?
Question 2b: And if so, what if there are two matches?
Question 2c: Also if so, is there a way to register handler classes to speed up finding them and avoid duplicate matches?
1. Effectively, yes. The idea is that you're tying individual endpoint operations to a responsible request/response handler, for which a concrete implementation class may implement one or several; the endpoint is abstracted from that responsibility and just needs to know about it's own required input and output pair, resolution is handled by MediatR.
2a. Reflection and assembly scanning. The registration process asks for assemblies to scan, and the MediatR library will locate and attempt to register classes that fulfill its interfaces
https://github.com/jbogard/MediatR/blob/master/src/MediatR/Registration/ServiceRegistrar.cs
MediatR supports Microsoft.Extensions.DependencyInjection.Abstractions
directly. To register various MediatR services and handlers:
services.AddMediatR(cfg =>
cfg.RegisterServicesFromAssemblyContaining()); or with an
assembly:
services.AddMediatR(cfg =>
cfg.RegisterServicesFromAssembly(typeof(Startup).Assembly));
This registers:
IMediator as transient
ISender as transient
IPublisher as transient
IRequestHandler<,> concrete implementations as transient
IRequestHandler<> concrete implementations as transient
INotificationHandler<> concrete implementations as transient
IStreamRequestHandler<> concrete implementations as transient
IRequestPreProcessor<> concrete implementations as transient
IRequestPostProcessor<,> concrete implementations as transient
IRequestExceptionHandler<,,> concrete implementations as transient
IRequestExceptionAction<,>) concrete implementations as transient
This also registers open generic implementations for:
INotificationHandler<>
IRequestPreProcessor<>
IRequestPostProcessor<,>
IRequestExceptionHandler<,,>
IRequestExceptionAction<,>
2b. Which takes precedence depends on how you register them, but MediatR will only resolve one IRequestHandler for a given type. The assembly scanner will register the first located handler and will skip any additional ones it finds. If you manually register multiple, it will use the last registered.
2c. Yes. You do not have to use the automated assembly scanning via the registration extensions to register MediatR; you can manually register the implementations of IMediator, ISender, and IPublisher yourself, and then selectively register all of your various handlers manually into the IoC container.

Adding DbContext for list of context types at runtime

To implement a plug-in system in a AspNet Core Mvc app, I would like a non-generic method to add a data context from a list of assemblies loaded dynamically at runtime, taking a Type parameter like this:
foreach(Type tp in pluginContexts)
{
services.AddDbContext(tp, options => ...);
}
instead of the usual
services.AddDbContext<PluginDataContext>(options => ...);
That's because for dynamically loaded assemblies, I can not provide the TContext type parameter to the AddDbContextPool method, since that's statically compiled and not available at compile time.
Background
This is for a larger Asp.Net Core MVC app. The plugins must be able to both access the main database of the overall app and a separate database of their own.
Plugin assemblies, containing domain code and their private database context are to be dropped in a specified directory.
The main app loads the plugin assembly dynamically upon startup.
The way I am solving this now is to have each controller get the IConfiguration instance injected, obtain the appropriate connection string from the config, and the database context is instantiated in the controller. Not so nice but does work.
One can easily inject a general class into the Services collection with AddScoped<>, and then use it as a sort of ServiceLocator - however, that is considered an antipattern.
I looked into the source code for AddDbContext but honestly I am lost.
Is there any simple way to achieve this?
Solved it by creating an extensibility point in the plugin assembly.
Define an interface in the main app, which all plugins must implement.
public interface IPluginContextRegistration
{
void RegisterContext(ref IServiceCollection services, Action<DbContextOptionsBuilder> optionsAction);
String GetDatabaseName();
}
Create a class implementing this interface (in the plugin). It has access to the type of its private database context, thus can use the generic AddDbContext method:
public class DatabaseRegistration : IPluginContextRegistration
{
public void RegisterContext(ref IServiceCollection services, Action<DbContextOptionsBuilder> optionsAction)
{
services.AddDbContext<Test1DbContext>(optionsAction);
}
public String GetDatabaseName()
{
return "test-plugin-db";
}
}
Then in the main app ASP.Net Startup.cs file, add following code, which calls the RegisterContext() method for each plugin. For example, if you want to use Sql Server:
void RegisterPluginDbContexts(ref IServiceCollection services, List<Assembly> assemblyList)
{
IEnumerable<IPluginContextRegistration> registrars = new List<IPluginContextRegistration>();
foreach (Assembly assembly in assemblyList)
{
registrars = registrars.Concat(GetClassInstances<IPluginContextRegistration>(assembly));
}
foreach (var reg in registrars)
{
String name = reg.GetDatabaseName();
String connStr = Configuration.GetConnectionString(name);
reg.RegisterContext(ref services, options => options.UseSqlServer(connStr));
}
}
For completeness - the method "GetClassInstances" is just a helper method using Reflection to obtain an instance of classes implementing the specified interface.
So it's simple after all - no need for re-writing framework code .

What is meant by the word "request" in the context of a transient service?

Suppose a service meant to be dependency injected (DI) is as follows.
public interface IWorkService
{
Task<JobResult> DoWorkAsync(string JobId,
JobPostParameters jobParameters,
CancellationToken cancellationToken);
}
public sealed class WorkService : IWorkService
{
private readonly IJobService jobService;
private readonly IHttpClientFactory httpClientFactory;
public WorkService(
IJobService jobService,
IHttpClientFactory httpClientFactory)
{
this.jobService = jobService;
this.httpClientFactory = httpClientFactory;
}
public async Task<JobResult> DoWorkAsync(string jobId,
JobPostParameters jobParameters,
CancellationToken cancellationToken)
{
// omissions...
}
}
Suppose I intend to register the service as transient.
Transient lifetime services are created each time they're requested
from the service container.
That quote comes from this reference. In this situation does the "request" constitute construction followed by exactly one method call followed by destruction? In other words the service object should not contain any private data that survives from one method call to the next?
Edit
I am asking about IWorkService (not the IJobService).
The effective lifetime depends on how IWorkService is created / requested from the dependency injection container.
If there is, for example, a singleton CallerService which gets a IWorkService constructor-injected, that service will use the same IWorkService for each call.
On the other hand, if the CallerService uses an IServiceProvider (for example via an IServiceScopeFactory) - each time GetService is used to get the IWorkService - a new instance will be created.
If the CallerService itself is transient-scoped, the question is shifted to who creates that service at which time.
To sum up, the word "request" describes the act of calling the service provider's GetService method (directly or via some helper method). For each such call, a new instance will be created, if the desired service is transient-scoped.

What is "DbContextOptions`1"?

I have Web API in ASP .NET Core. When I add a db context in Startup.ConfigureServices:
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<FixturesContext>(
options => options.UseSqlServer(Configuration.GetConnectionString("FixturesDatabase")));
services.AddControllers();
}
I see the number of services in the "services" container raises by three, I think those are:
FixturesContext
DbContextOptions
DbContextOptions`1
I am curious what is "DbContextOptions1"? Does anyone know? I have tried googling it but not satysfying result. My goal is to replace original context with in-memory (to run integration tests without original database), so I'm deleting db context and its options and adding in-memory context instead of them.
The third service you are getting is a generic version of the DbContextOptions. When calling .ToString() on a generic type it often looks like this.
The reason why there are three instances is that EF adds a general DbContextOptions object and a more specific one for your defined context.
If you inspect the calls of the third service you should find the type of your DbContext as a generic parameter.
DbContextOptions'1 would be the generic DbContextOptions<FixturesContext> registered to be injected into the context when being initialized.
Reference Configuring DbContextOptions
public class FixturesContext : DbContext
{
public FixturesContext(DbContextOptions<FixturesContext> options)
: base(options)
{ }
//...
}

HttpContextBase.Request exception when using Ninject MVC3

I have a service that takes a dependency on HttpContextBase.
Ninject is injecting this for me already as it's set up in the MvcModule to return new HttpContextWrapper(HttpContext.Current) when HttpContextBase is requested
I want to use this service in Application_AuthenticateRequest, so i'm using property injection so that Ninject resolves it for me
When I try and access Request.UserHostAddress on the HttpContextBase I get a Value does not fall within the expected range exception
If I call HttpContext.Current.Request.UserHostAddress directly it works without problems
ExampleService.cs
public class ExampleService : IExampleService {
HttpContextBase _contextBase;
public ExampleService(HttpContextBase contextBase) {
_contextBase = contextBase;
}
public void DoSomething() {
var ip = HttpContext.Current.Request.UserHostAddress; <== this works
ip = _contextBase.Request.UserHostAddress; <== this fails
}
}
Global.asax
[Inject]
public IExampleService ExampleService { get; set; }
public void Application_AuthenticateRequest() {
ExampleService.DoSomething();
}
I'm missing something here, but I can't see what
Dependencies that are injected into classes live as long as the the class they get injected into, because the class holds a reference to them. This means that in general you should prevent injecting dependencies that are configured with a lifetime that is shorter than the containing class, since otherwise their lifetime is 'promoted' which can cause all sorts of (often hard to track) bugs.
In the case of an ASP.NET application, there is always just one HttpApplication instance that lives as long as the AppDomain lives. So what happens here is that the injected ExampleService gets promoted to one-per-appdomain (or singleton) and since the ExampleService sticks around, so does its dependency, the HttpContextBase.
The problem here of course is that an HTTP context -per definition- can't outlive a HTTP request. So you're storing a single HttpContextBase once, but it gets reused for all other requests. Fortunately ASP.NET throws an exception, otherwise you would probably be in much more trouble. Unfortunately the exception isn't very expressive. They could have done better in this case.
The solution is to not inject dependencies in your HttpApplication / MvcApplication. Ever! Although it's fine to do so when you're injecting singletons that only depend on singletons recursively, it is easy to do this wrong, and there's no verification mechanism in Ninject that signals you about this error.
Instead, always resolve IExampleService on each call to AuthenticateRequest. This ensures that you get an ExampleService with the right lifetime (hopefully configured as per-web-request or shorter) and prevents this kind of error. You can either call into the DependencyResolver class to fetch an IExampleService or call directly into the Ninject Kernel. Calling into the Kernel is fine, since the Application_AuthenticateRequest can be considered part of the Composition Root:
public void Application_AuthenticateRequest() {
var service = DependencyResolver.Current.GetService<IExampleService>();
service.DoSomething();
}