Adding Context TO DI CreateScope - .net-core

In a console application I'm using masstransit to process messages coming from a rabbitMQ queue. Every message deals with a specific customer and contains a customterId (this could be in header or message, tbd).
I'm using standard Microsoft Dependency Injection and I have several services I wish to be scoped to this customer.
I can use IServiceProvider.CreateScope in the masstransit consumer. But I cannot seem to determine the proper way to set a specific "value" to a scope. Something that every scoped service provided can use to determine the customer for which the scope was defined. Something that the scoped service can use to determine the "context"
My feeling is that I am looking to define something like HttpContext (This off course is not available in console application).
Do I have to do this manually by setting properties on a scoped service on which all other (scoped) services have a dependency? That feels not very thread safe to me (what if a service is instantiated in a scope before I can set the customer property?).
I know this is a little open for a question here, yet maybe someone can still provide an answer?
I would provide code, but this more an architectural question and on internal the workings of MSDI.

MassTransit creates a scope for every consumer, so creating another scope is a bad idea. Any scoped dependencies will be resolved through the consumer's constructor automatically by the container.

Related

Since ILogger<T> is a singleton, how different threads can use BeginScope() without affecting others?

This is related to this question. Context: .Net Core 3.1, using Microsoft.Extensions.Logging
Loggers are singletons in the application's IHost. If I inject (DI) an ILogger<T> into my class or method, the injected object is the same instance other classes or methods receive if they ask for ILogger<T>. This poses the question of what happens when I use logger.BeginScope($"Processing {transactionId}") in one thread. What happens with the other threads? Do they change the logging scope as well? Do logging scopes get mixed up? If they don't: how does that work, being their loggers the same object? If they do mix scopes, how can I make two threads use different logging scopes for a given ILogger<T> type?
This depends on the logger implementation, but typically they're implemented using a type of stack held within an AsyncLocal.
A call to BeginScope will put a new item onto that stack, and the adjoining Dispose will pop it off of that stack.
When the logger is invoked via LogInformation or otherwise, the data of the current stack object will be copied to write it to the console or whatever output that logger instance is configured to do.
The AsyncLocal is what gives the framework the ability to store information across threads and tasks.
For reference, check out the Microsoft.Extensions.Logging.Console source code:
ConsoleLogger.cs#L67
LoggerExternalScopeProvider.cs#L14

Should AbstractListenerContainerFactory really close the consumer it uses to check topics

In org.springframework.kafka.listener.AbstractMessageListenerContainer, when starting the container, the checkTopics method checks if the subscribed topic exists on the broker using a Kafka consumer that is created in a try with resources block.
When the consumer is closed, the closure cascades down to many Closeable associated objects, notably the key and value deserializers (see org.apache.kafka.clients.consumer.KafkaConsumer). In a Spring application, Deserializers are generally declared as beans so there is only one instance in the factory of each type, and while most deserializers implement close as a no-op, I have come across cases where closing a Deserializer renders it unusable from that point on.
It seems to me that while closing a consumer sounds reasonable, given that multiple instances are spun up by Spring and the one created here is just a throwaway, the cascade down to Deserializer beans is an undesirable consequence that maybe wasn't noticed when the AbstractMessageListenerContainer was written.
There is a workaround - when creating the KafkaListenerContainerFactory just call
factory.getContainerProperties().setMissingTopicsFatal(false);
but this removes the safety check for topic existence and seems like a bit of a hack. Is closing the consumer really the right thing to do in AbstractMessageListenerContainer?
We can and will use an AdminClient instead of a Consumer to check that the topic(s) exist.
However, this is not a panacea since stop()ping a container also closes the Consumer(s) so the same problem would exist when the container(s) are restarted.
In cases like this, it's better to let Kafka manage the lifecycle of the deserializer instead of declaring it as a bean.

How to introduce application-wide context object?

I need to make several properties accessible from application's business layer. Those are some ids and common settings. Most of them are valid only through request-response lifespan.
This is a web application (ASP.NET Web Forms to be specific) with dependency injection set up.
Currently those properties are passed through method parameters directly to business layer's services. That works but is not very efficient since:
sometimes parameters' values need to be passed deeper obscuring the readability a bit
some properties should be lazy resolved, and this should be done only once per request
retrieving properties which are resolved by touching a database can be confusing for new developers (there is not unified way of doing this)
some services are constructed by a factory which enriches them with some config parameters
I was thinking about introducing an application context interface, with an implementation in the main project, which would be created on every request. It could be injected to the services directly making them parametrized automatically and independently (services won't need the factory anymore).
Is it how this problem should be tackled or maybe there are some other options?
One option I don't like here is that it might bond the main particle with business layer which is not a perfect example of The Clean Architecture.
Id say you solution is a very common one - inject an 'application context' into your classes. One thing I would be careful of though is making sure you are following the Integration Segregation Principle (from SOLID). Dont just start making all your classes expect an application context instance. Instead, design interfaces that split the application context up, and have your classes expect them as dependencies. Your application context will then need to implement all the interfaces.
This is the correct way to do things as it decouples your classes from implementation. Really your classes don't care if their dependency is from one giant application context, they just care about specific methods implemented by it. This will make your code more robust as you will reduce the risk of breaking something if you change the implementation of the application context later on.
Why don't you use some dependency injection container? Your global settings and parameters can be registered to it as pseudo-singletons and then you will be able to neatly request them from any point inside your application.

How to access 'templating' service not in controller

Ok, so the problem is:
I've got some 'order' entity, and it has 'status' property. On changing status, i wanted some other objects to be informed of this event, so i've decided to use Observer pattern. One of the observers notifies clients via email. Now i want to render Email text's from some of the twig templates. As i get from the Book, rendering templates in controllers are done with 'templating' service.
So the question as it follows: How can i access 'templating' service in my Observer class?
Specification:
I was advised, to implement my Observer as a service, but i'm not sure 'bout that. I've tried to solve this problem, and here is my options:
Use Registry. Solution that is straight and hard as rail. I guess it misses the whole point of DI and Service Container. Huge plus of this solution, is that i can access all common services from any point of my application.
To pass needed services from the context via constructor, or via setters. This is more like in Sf2 spirit. There comes another list of problems, which are not related to this question field.
Use observers as a service. I'm not really sure 'bout this option 'cos, in the book it is written, that service is a common functionality, and i don't think that observing entity with number of discrete properties is a common task.
I'm looking for a Sf2 spirit solution, which will be spread over whole project, so all answers with an explanation are appreciated.
As with any other service in a Symfony2 project, you can access it from within other classes through the dependency injector container. Basically what you would do is register your observer class as a service, and then inject the templating service into your observer service. See the docs for injecting services.
If you're not familiar with how Symfony handles dependency injection, I'd suggest reading that entire chapter of the documentation - it's very helpful. Also, if you want to find all the services that are registered for application, you can use the console command container:debug. You can also append a service name after that to see detailed info about the service.
Edit
I read your changes to the question, but still recommend going down the DI route. That is the Symfony2 spirit :) You're worried that your observer isn't common enough to be used as a service, but there's no hard rule saying "You must use this piece of code in X locations in order for it to be 'common'".
Using the DIC comes with another huge benefit - it handles other dependencies for you. Let's say the templating service has 3 services injected into itself. When using the DIC, you don't need to worry about the templating service's dependencies - they are handled for you. All you care about is telling it "inject the templating service into this other service" and Symfony takes care of all the heavy lifting.
If you're really opposed to defining your observer as a service, you can use constructor or setter injection as long as you're within a container-aware context.

Practices to register types with IoC container?

I have a solution with several projects (MyApp.Data, MyApp.BLL, MyApp.Web). I register types in Global.asax file in MyApp.Web (main web application):
builder.RegisterType<SomeService1>().As<ISomeService1>().InstancePerHttpRequest();
builder.RegisterType<SomeService2>().As<ISomeService2>().InstancePerHttpRequest();
//...etc
And I wonder whether it's a bad practice to register types and their scope using attributes in the other assemblies (for example, in MyApp.BLL). See below:
[Dependency(typeof(ISomeService1), ComponentLifeStyle.Transient)]
public class SomeService1 : ISomeService1
{
//methods and properties go here
}
Using local attributes or other ways to indicate wiring for a DI Container tightly couples the service to the DI Container, so I don't think that's a good idea.
Additionally, it may constrain your future options. If, for example, you specify the lifestyle scope, you can't reuse the service with a different scope.
In general, you should compose the application in a Composition Root (global.asax), which gives you a single location with a clearly defined responsibility where all classes are composed.
That would be much more manageable and maintainable that spreading the configuration data all over your classes.
As your question implies, it makes some sense to delegate responsibility for registration to the assembly that knows what needs to be registered. For example, if you
use the SolrNet library, it provides a method that performs component registration, to encapsulate the knowledge of what needs to be registered and to spare the library's consumer from having to learn all about the library before getting started.
However, there is a potential issue with this approach. Would your registration requirements change if you used the dependent assemblies in other applications? For example, would it make sense to register something as ComponentLifeStyle.HttpRequestScoped and then use it in a non-Web application? By delegating registration to the dependency, you are coupling the dependency to its consumer's registration requirements (and to its choice of IoC container).
Autofac (I can't speak for other IoC containers) provides a way round this. It enables you to override registrations so that the most recently registered component is used when a service is resolved. This means that you can call a library's registration method and then register your own services to override the defaults.
There is another problem with your proposed attribute-based registration - it doesn't enable you to specify a lambda expression as a component creator. How would you implement a registration like this with attributes?
builder.Register(c => new A(c.Resolve<B>()));
It might be preferable to define an IRegistrar interface, and then use reflection to search all loaded assemblies for implementations and invoke them. Perhaps something like this:
public interface IRegistrar
{
void RegisterComponents();
}

Resources