Comments (8)
What you are witnessing is an error in the documentation. You are correct that there is no RegisterExecutionContextScope
extension method. This method does not exist, and there are no plans making it available in v3. Instead, you will have to create a ExecutionContextScopeLifestyle
instance and pass it on the Register
methods. In case you are using Web API, you can use RegisterWebApiRequest
, which is in fact exactly the same, because this WebApiRequestLifestyle is in fact an ExecutionContextScopeLifestyle with a more convenient name.
To make working with scoped lifestyles more convenient, In v3 you will be able to do this:
var container = new Container();
container.Options.DefaultScopedLifestyle = new ExecutionContextScopeLifestyle();
container.Register<IUnitOfWork, DbUoW>(Lifestyle.Scoped);
container.Register<IUserContext, AspNetUserContext>(Lifestyle.Scoped);
from documentation.
Thanks! +1 for the Options. That thought did cross my mind when I was messing with scopes and was made to change all Register...
lines to take the scope object.
On a side note, what led me down this path is the The XYZCommandHandler is registered as 'Hybrid Lifetime Scope / Web Request' lifestyle, but the instance is requested outside the context of a Hybrid Lifetime Scope / Web Request.
issue but I think I'll post a separate thread for that, if I cannot figure out why its happening,
I'm using the Hybrid style since I have some ops that are executed in background using (HostingEnvironment.QueueBackgroundWorkItem
) and I have a strong feeling that the ctor injected service is injected with a WebRequest scope but when the background op tries to access it, the scope is no longer valid. I feel that I need to exclusively control scope using BeginExecutionContextScope
, something I thought I could avoid. I've gone thru your SO answers and was wondering if there is any other way other than creating a decorator...
from documentation.
The documentation contains an interesting section on working in multi-threaded applications. To reduce complexity. you should prevent moving object graphs from thread to thread. You should only use objects you resolve on the thread you resolved them on (or in the same asynchronous context in case you're using async). This keeps the knowledge about what is thread-safe and what is not inside your composition root and this reduces complexity.
So instead of passing your handler to the QueueBackgroundWorkItem
, pass the command message and resolve a new graph inside the background thread. This will also be the place to start a new scope.
from documentation.
Talk about the joys of retrofitting old code! Let me show some code:
public class AbcService(IMediator mediator, IMailService mailService, IPushNotificationService pushNotificationService, ...) : IAbcService
{
public async Task DoSomthingComplex(...)
{
...
var someValue = await _mediator.RequestAsync(command);
HostingEnvironment.QueueBackgroundWorkItem(ct => mailService.SendEmail(...));
HostingEnvironment.QueueBackgroundWorkItem(ct => pushNotificationService SendPushNotification(...));
return someValue;
}
}
IAbcService
is injected in controller and so it gets created with WebRequest scope. Along with it, all dependent services as well.
By the time mailService and pushNotification service go around finishing their business, the web request scope is finished and hence the error message.
Resolving a new object graph makes sense. Let me try that!
from documentation.
What you are actually missing here is a proxy class for your IMailService
. For instance:
public class BackgroundProcessingMailServiceProxy : IMailService
{
private readonly Func<IMailService> serviceProvider;
public BackgroundProcessingMailServiceProxy(Func<IMailService> serviceProvider) {
this.serviceProvider = serviceProvider;
}
public void SendEmail(...) {
HostingEnvironment.QueueBackgroundWorkItem(ct => {
var mailService = serviceProvider.Invoke();
mailService.SendEmail(...);
});
}
}
This class acts like a IMailService
, but makes sure the mail is sent in the background. This class has the following advantages:
- Consumers can now simply depend on
IMailService
without having to be concerned about doing any multi-threaded or background operations. This simplifies those classes considerably, and makes them testable again. - Logic related to multi-threading is moved to one place in the application: the composition root. This makes it much easier to reason about thread-safety.
Such abstraction can be registered as follows:
container.Register<IMailService, RealSmtpMailService>();
container.RegisterDecorator(typeof(IMailService), typeof(BackgroundProcessingMailServiceProxy),
Lifestyle.Singleton);
Since the BackgroundProcessingMailServiceProxy
both implements IMailService
and wraps IMailService
(or Func<IMailService>
in this case), it can be considered a decorator and that's why Simple Injector allows you to use the RegisterDecorator
method. But as easily you can make the following registration as well:
container.Register<RealSmtpMailService>();
container.RegisterSingle<IMailService>(
new BackgroundProcessingMailServiceProxy(container.GetInstance<RealSmtpMailService>));
Does this help?
from documentation.
Very much. I did pretty much the same changes, except moving QBWI to the proxy but like you reasoned, moving it inside makes more sense.
Curious, what are the differences (if any) between the 2 registration styles?
I can see that the former is more semantic and readable and perhaps will not lock the container (as opposed to later which makes a GetInnstance
call which will lock it), but other than that, is there any other difference?
from documentation.
When using RegisterDecorator
, you will get full container support, including auto-wiring and diagnostics. This will especially be useful when the decorator itself contains dependencies. In that case, it is usually more convenient to let the container wire that type for you, especially because it allows you to check for common configuration mistakes, such as lifestyle mismatches.
The second registration on the other hand, would be useful in cases when the decorator has no other dependencies, or are dependencies that are not likely to change and are singletons as well. You lose the container's ability to diagnose this object graph, but if everything is a singleton already, verification might not be that interesting at all. Do note however that while the call to new BackgroundProcessingMailServiceProxy(container.GetInstance<RealSmtpMailService>)
does create an BackgroundProcessingMailServiceProxy
instance, it does not request an RealSmtpMailService
from the container, but what happens here is that the C# compiler wraps this method in a Func<T>
delegate, which is injected into the proxy's constructor. Since the Func<T>
is only called when the proxy's SendMail(...)
method is called, so will the GetInstance<RealSmtpMailService>()
method call. This means that at this point where the proxy is created, the container isn't locked at all.
One reason to use the second method over the register decorator, is because you might not see this really as decoration, in which case using RegisterDecorator
might confuse other developers.
from documentation.
Works like a charm! I also realized that with this approach, I don't need a scoped or hybrid lifestyle. In fact, using a hybrid lifestyle throws the same ...instance is requested outside the context of a Hybrid Lifetime Scope / Web Request...
error for DbContext
object. Not sure if that's supposed to happen though. The DbContext is registered as container.Register<DbContext, DbContext>();
Thanks for the help! 👍
from documentation.
Related Issues (20)
- There is a way to share Singletons registrations between two containers? HOT 2
- Describe how to load ASP.NET Core Controllers and ViewComponents from different assemblies HOT 2
- Add documentation for ScopedLifestyle.Flowing to lifestyle docs
- Add example about parallel processing of work in the context of scoped dependencies HOT 1
- Example extension method not compiling in the docs of Diagnostic Warning - Disposable Transient Components HOT 2
- Decoration example with DeadlockRetry & Validation HOT 3
- Why do the SqlUserRepository class code have constructor called UserController? HOT 1
- Add example for the new ASP.NET Core 6.0 "startup-less" configuration to ASP.NET Core MVC Integration NuGet package integration docs HOT 3
- "Register.Collection" instead of "Collection.Register" HOT 2
- WPF: How do I compose UI elements via DI? HOT 2
- Add information on ASP.NET Core integration page about missing [FromService] integration HOT 1
- Add context-based example that uses ctor argument names
- Add context-based example that uses ctor argument names HOT 1
- Add context-based example that uses parent's parent type
- Documentation typo HOT 2
- Question: Why I should not store the resolved service in a private field in filter attributes? HOT 1
- IOptions commentary HOT 3
- Update copyright
- Windows form how to call forms correctly from container HOT 5
- Add information about the design decision to not support optional constructor dependencies
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from documentation.