Dependency Injection for AzureFunction ServiceBus based on message information - dependency-injection

I am working on AzureFunction ServiceBus v3 which handle multi-tenant message. In message I will have TenantId and I will need to register DependencyInjection per message base on this TenantId.
What I tried so far:
At StartUp, I stored the IServiceCollection as static variable
Retrieve the TenantId from the serialized message in Function'd Run method
Update IServiceCollection based on above TenantId and retrieve the Service
_serviceCollection.AddTransient<ITenantIdResolver>(ctx => { return new CompanyResolver{TenantId=tenantId}; }); var service = _serviceCollection.BuildServiceProvider().GetService<T>();
But it throw exception: Unable to resolve service for type 'Microsoft.Azure.WebJobs.Script.IEnvironment' while attempting to activate 'Microsoft.Azure.WebJobs.Script.Configuration.ScriptHostOptionsSetup' I do some research and look like it was because I used IHttpClientFactory.
How can I fix this?
Or even better if there is a way to retrieve the message in StartUp, so I can inject the tenantId properly? Like
serviceCollection.AddTransient<ITenantIdResolver>(ctx => { var tenantId = GetServicebusMessage().TenantId; return new CompanyResolver { TenantId=tenantId }; }

I think that would be the wrong order of things. Dependency injection should be setup before the message is being processed.
A resolver could be a solution. Register the resolver with the dependency injection container, and let the function be dependent on the resolver. Based on the message you get the right instance from the resolver. In this article it is explained better under "IoC Container Registration #3 – Resolver + Factory Method Pattern": https://techcommunity.microsoft.com/t5/apps-on-azure/dependency-injection-on-azure-functions-5-ways-selecting/ba-p/1502394

According to Dependency injection for azure functions, it's not possible to use the services at an early stage.
My suggestion is to change the architecture to "Durable Orchestrations" so you call an ActivityTrigger function from the orchestrator which gets back list of tenants then you fire other ActivityTriggers that will handle them.

Related

ServiceBusTrigger in a Multitenant application - Dependency Injection for ServiceBusReceivedMessage

I have a multitenant application which uses scoped dependencies to retrieve a tenantId from an incoming httprequests to the Azure functions and instantiate various resources based on the tenant.
For example:
services.RegisterScoped((sp)=>
{
var httpContextAccessor = sp.GetRequiredService<IHttpContextAccessor>();
var tenantId = httpContextAccessor.GetTenantId(); // extension method
// Instantiate some other tenant specific dependencies e.g. DBContext.
});
Now, I have a ServiceBusTrigger which processes messages for all tenants. The ServiceBusReceivedMessage parameter contains an object which has a tenantId property so that each message can be processes for a particular tenant.
Is there a way to have the ServiceBusReceivedMessage instantiate a resource, say a DbContext, from within the function? This is necessary because each message may have to persist some data via a DbContext, which needs to be instantiated with a specific connection string based on the tenantId in the message.
Some things I have considered:
Using Activator.CreateInstance(//pass in the tenant connection string after retrieving it manually)
Can ServiceBusRecievedMessage be registered within the serviceBusTrigger per message?? This seems hacky (there isn't a straightforward way to do that) but also the most preferred approach as the rest of the DI containers can be leveraged without having to instantiate objects manually as in the first option.
[FunctionName("MyCustomTrigger")]
public Task Run(
[ServiceBusTrigger("MultiTenantEndpoint")]
ServiceBusReceivedMessage message,
ServiceBusClient client,
ServiceBusMessageActions messageActions,
ILogger logger,
ExecutionContext executionContext)
{
var tenantId = message.GetTenantId();
// register tenantId as scoped for this request only somehow?
await injectedService.Process(message);
}
Alternate approaches or designs for this?

Configure injected services with information available in runtime

I've been trying to modify injected services with values available from authenticated users.
Using the built-in DI container, I added the required service with Scoped lifetime.
services.AddScoped<ITodoRepository, TodoRepository>();
Then registered a custom middleware between authorization and MVC middlewares to modify the repository with user specific information.
// Configure authorization middleware
app.Use(async (context, next) =>
{
var todoRepository = context.ApplicationServices.GetRequiredService<ITodoRepository>();
todoRepository.UserStoreId = context.User.GetUserStoreId();
await next.Invoke();
});
// Configure MVC middleware
When the program execute a request, the injected repository within my controller does not presist the saved value.
Am i doing something wrong?
From my understanding, scoped objects are saved within request.
Also, is there another way to achieve this?
You can create your own service, i.e. IAuthenticatedUserService/AutheticatedUserService.
Into it, you inject IHttpContextAccessor.
public interface IAuthenticatedUserService
{
ClaimsPrincipal User { get; }
}
Then you inject the IAuthenticatedUserService into your repository, where you can access the logged-in user.
Of course you could also directly inject IHttpContextAccessor into your repository and access httpContextAccessor.HttpContext.User within it, but since repositories are usually defined in their own assembly, you'd also need to reference the Microsoft.AspNetCore.Http.Abstractions package from it which would cause a tight(er) coupling.
But if you don't mind this kind of coupling, just inject the IHttpContextAccessor into your repository, they are supposed to be scoped (=per request) or transient anyways.
P.S. don't forget the Dependency Injection/Inversion of Control mantra: "Don't call us, we call you". You have attempted to call "us" (the repository) to set a value.
Am i doing something wrong? From my understanding, scoped objects are saved within request.
I was able to fix the issue by replacing
context.ApplicationServices.GetRequiredService<ITodoRepository>();
with
context.RequestServices.GetRequiredService<ITodoRepository>();

OWIN Service resolution Using Autofac

I have an WebApi application using OWIN and Autofac. Although controllers and parameters get resolved correctly, I would like to be able to use OwinContext.Get<type> to resolve types registered with Autofac. Is that posible?
Already setapp.UseAutofacMiddleware(container); and config.DependencyResolver = new AutofacWebApiDependencyResolver(container);
By example, I registered builder.Register<IAppConfiguration>(c => new AppConfig()); and I'd like to resolve it using owinContext.Get<IAppConfiguration>().
There is no way to get OwinContext.Get<T> to resolve things from Autofac. If you dive into Microsoft.Owin.OwinContext.Get in Reflector, you'll see it's backed entirely by a dictionary of things you register with an environment. It's not dynamic and there's no way (without creating your own IOwinContext implementation) to get it to resolve things either out of the dictionary or out of dependency resolution.
If you are in a DelegatingHandler or an ApiController you will have a reference to the current HttpRequestMessage. Use message.GetDependencyScope() to get the current request-level dependency scope to resolve services.
public HttpResponseMessage SomeControllerAction()
{
var service = this.Request.GetDependencyScope().GetService(typeof(Service));
}
If you have access to the HttpConfiguration then you can use the HttpConfiguration.DependencyResolver to resolve things. Note that resolver will not have per-request dependencies available. Web API tracks request dependency scope with the inbound HttpRequestMessage so be aware of that limitation. There is an FAQ about per-request lifetime scope that can help you through that.
If you're in a place where there's only an IOwinContext, you may need to make use of a package like CommonServiceLocator and the associated Autofac.Extras.CommonServiceLocator. There really isn't a way to get a reference to the current HttpConfiguration or global container just from an IOwinContext. Again, if you go this route, you won't have per-request dependencies available, so be aware.
The IOwinContext.Get uses the Environment dictionary, resolving objects registered directly with Owin, it does not take into account Autofac container.
I managed to do it by accessing the Autofac OwinLifetimeScope in the Environment property and using the scope to resolve the service.
You can access the LifetimeScope using this code
var scope=OwinContext.Get<Autofac.Core.Lifetime.LifetimeScope>("autofac:OwinLifetimeScope");
and then
scope.GetService(type)
You should check for nulls and write it in a better way, as Extension method maybe.
If you have WebAPI in your project, you can simulate a http request like this
var dependencyScope = new AutofacWebApiDependencyScope(owinContext.GetAutofacLifetimeScope());
var myService = dependencyScope.GetService(typeof(MyService));

"An MVC filter provider has already been registered for a different Container instance." in Simple Injector 2.6

I previously had the setup for property injection in one of my attributes as
Container.RegisterInitializer<PermitAttribute>(initialize =>
{
initialize.QueryProcessor = Container.GetInstance<IQueryProcessor>();
});
And the usage was
public class PermitAttribute : ActionFilterAttribute
{
public IQueryProcessor QueryProcessor { get; set; }
}
but after updating to simpleinjector 2.6.1 The property injection broke. When I am trying to access QueryProcessor object inside PermitAttribute. It resolves null value where as the Simple Injector configuration still has the same property injection via delegate instance .
Is there any breaking change in property injection behavior due to which it was working in v2.5 and its not working anymore in 2.6.1 ?
Update 1:
The Line in the configuration was throwing error for MVC filter provider registration for attributes in v2.6.1
container.RegisterMvcIntegratedFilterProvider();
For that I commented it . And it stopped the property injection working . The property injection was inside one of my attributes . I guess that's the line above which affects it. And its throwing error in v2.6.1
Update 2:
Message
An MVC filter provider has already been registered for a different
Container instance. Registering MVC filter providers for different
containers is not supported by this method.
StackTrace :
at SimpleInjector.SimpleInjectorMvcExtensions.RequiresFilterProviderNotRegistered(Container container)
at SimpleInjector.SimpleInjectorMvcExtensions.RegisterMvcIntegratedFilterProvider(Container container)
at RemsPortal.App_Start.SimpleInjectorInitializer.Initialize() in d:\Projects Work\RemsPortal\V2.0 Web Portal\RemsPortal\App_Start\SimpleInjectorInitializer.cs:line 39
Update 3 :
entire Configuration
public static void Initialize()
{
var container = new Container();
InitializeContainer(container);
container.RegisterMvcIntegratedFilterProvider();
container.RegisterMvcControllers(Assembly.GetExecutingAssembly());
container.Verify();
DependencyResolver.SetResolver(new SimpleInjectorDependencyResolver(container));
}
private static void InitializeContainer(Container Container)
{
Container.RegisterManyForOpenGeneric(typeof(IAsyncCommandHandler<,>),
AppDomain.CurrentDomain.GetAssemblies());
Container.RegisterOpenGeneric(typeof(ITransactionCommandHandler<,>),
typeof(TransactionCommandHandlerDecorator<,>));
Container.RegisterOpenGeneric(typeof(ICommandResult<>),
typeof(CommandHandlerResult<>));
Container.Register<ICommandResolver, CommandResolver>();
Container.Register<DbContext, RemsContext>();
Container.RegisterOpenGeneric(typeof(IPager<>), typeof(PagerModel<>));
//Container.RegisterPerWebRequest<DbContext, RemsContext>();
Container.Register<UserManager<Users, Guid>, RemsUserManager>();
Container.Register<RoleManager<Roles, Guid>, RemsRoleManager>();
Container.Register<IUserStore<Users, Guid>,
UserStore<Users, Roles, Guid, UserLogins, UserRoles, Claims>>();
Container.Register<IRoleStore<Roles, Guid>, RoleStore<Roles, Guid, UserRoles>>();
Container.RegisterManyForOpenGeneric(typeof(IAsyncQueryHandler<,>),
AppDomain.CurrentDomain.GetAssemblies());
Container.RegisterManyForOpenGeneric(typeof(IAsyncQueryHandler<>),
AppDomain.CurrentDomain.GetAssemblies());
Container.RegisterManyForOpenGeneric(typeof(IQueryHandler<,>),
AppDomain.CurrentDomain.GetAssemblies());
Container.RegisterOpenGeneric(typeof(IQueryResult<>), typeof(QueryResult<>));
Container.RegisterOpenGeneric(typeof(IPaginator<>), typeof(Paginator<>));
Container.Register<IPaginator, Paginator>();
Container.RegisterOpenGeneric(typeof(IAsyncQueryHandler<>), typeof(BaseQuery<>));
Container.RegisterOpenGeneric(typeof(IQueryHandler<>), typeof(BaseQuery<>));
Container.Register<IQueryProcessor, QueryProcessor>(Lifestyle.Singleton);
Container.Register<ILog, NLogger>(Lifestyle.Singleton);
Container.RegisterInitializer<PermitAttribute>(initialize =>
{
initialize.QueryProcessor = Container.GetInstance<IQueryProcessor>();
});
Container.RegisterInitializer<BaseController>(initialize =>
{
initialize.QueryProcessor = Container.GetInstance<IQueryProcessor>();
initialize.Logger = Container.GetInstance<ILog>();
});
Container.RegisterInitializer<BaseCommandHandler>(initialize =>
{
initialize.UserManager = Container.GetInstance<RemsUserManager>();
initialize.RoleManager = Container.GetInstance<RemsRoleManager>();
initialize.RemsContext = Container.GetInstance<RemsContext>();
initialize.QueryProcessor = Container.GetInstance<IQueryProcessor>();
});
Container.RegisterInitializer<BaseHandler>(initialize =>
{
initialize.UserManager = Container.GetInstance<RemsUserManager>();
initialize.RolesManager = Container.GetInstance<RemsRoleManager>();
});
}
The exception you are seeing is caused by a verification check that has been added to version 2.6 that prevents you from calling RegisterMvcAttributeFilterProvider and RegisterMvcIntegratedFilterProvider multiple times for different container instances. The problem is described in more details here.
The solution is to make sure RegisterMvcIntegratedFilterProvider is called only once in your code for the duration of the complete app domain and since RegisterMvcAttributeFilterProvider is deprecated, prevent having any calls at all to that legacy method. So if you only have one call in there, set a break point on this line, because you might be calling the Initialize() method twice!
The new RegisterMvcIntegratedFilterProvider allows complete integration of MVC attributes in the Simple Injector pipeline which makes sure that the RegisterInitializer method is called on attributes.
Another option though is to enable explicit property injection for attributes, or to fall back on the use of passive attributes as shown here.
But one note on property injection. I noticed you make extensive use of (explicit) property injection, especially for your base classes. From a design perspective however, it's better to remove the base classes all together, because they are a design smell at least, but might become maintenance problems later on. They might violate the Single Responsibility Principle or at least hide that derived types have too many dependencies, which often means too many responsibilities. I create quite big applications myself with MVC and command handlers and query handlers and I am always able to prevent the use of base classes. If a concrete handler needs a dependency, you should simply inject it into the constructor of that type. Prevent hiding that dependency by (ab)using a base type.
There is one important detail that you should be aware about when you use the RegisterMvcIntegratedFilterProvider. MVC caches filter attributes (god knows why) and this means that such attribute is basically becoming a singleton. This implies that every dependency this filter attribute has, becomes a singleton as well. This is of course be big problem if such dependency is not registered as singleton itself; it becomes a captive dependency. Although Simple Injector contains a diagnostic warning to detect these kinds of errors, Simple Injector will be unable to detect this with attributes, because attributes are not registered in the container. Because of this, my advice is to stay away from using property injection in your attributes at all. We are considering to deprecate the RegisterMvcIntegratedFilterProvider method from the MVC integration library.
As per steven It really was calling the container registration twice .
As I got to see tht
I had called SimpleinjectorInitializer.Initialize(); method in global.asax And then the webactivator also calling the same initizer was taking toll on the simpleinjector which caused the initization to fail for a check .
The solution to that is to remove SimpleinjectorInitializer.Initialize(); from the global.asax and let webactivator do its work .

MVC Conditional Dependency Injection

I am trying to figure out the best way to design an MVC4 application with DI via Ninject.
When the user comes to the system, they are presented with a login where they can select one of many databases. Each database is a separate catalog on a database server. What I need to do is inject a repository with the database connection details once the user is logged in. The type of repository does not change, just the connection details.
I have found this post which I could make work, but I am not sure if it is the best approach for my situation. What other options have people used with success?
Problem solved. As I am wishing to pass the connection string in the constructor for my concrete repository I can use the following Ninject WithConstructorArgument:
IBindingWithOrOnSyntax<T> WithConstructorArgument(string name, Func<Ninject.Activation.IContext, object> callback);
With this overload the callback is called every time an object is created. So my code looks like this and the MakeConnectionString method is called every time.
_Kernel = new StandardKernel();
_Kernel.Bind<ISomeItemRepository>().To<FakeSomeItemRepository>()
.WithConstructorArgument("connectionString", (c) =>
{
MakeConnectionString();
});

Resources