New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Resolve dependencies requiring an implementation created by a parameter factory #477
Comments
So, I've found a way of handling this that works around this issue. Still wondering if there is a way to do this in LightInject. async Task Main()
{
var container = new ServiceContainer();
container.Register<IRepository, Repository>(new PerScopeLifetime());
container.Register<IService, Service>(new PerScopeLifetime());
container.Register<IContext, Context>(new PerScopeLifetime());
container.Register<IContextFactory, ContextFactory>(new PerScopeLifetime());
// excuse this grossness. Just trying to test scope and concurrency
var values = await Task.WhenAll(Enumerable.Range(0, 1000).Select(i => new Func<Task<int>>(() =>
{
using (var scope = container.BeginScope())
{
var contextFactory = (ContextFactory)container.GetInstance<IContextFactory>();
contextFactory.SetContext(new Context { Data = i });
var service = scope.GetInstance<IService>();
return Task.FromResult(service.DoSomeStuff());
}
})()));
values.Dump();
}
// Define other methods and classes here
public interface IService
{
int DoSomeStuff();
}
public interface IRepository
{
int Get();
void Set(int i);
}
public interface IContext
{
int Data { get; set; }
}
public class Service : IService
{
IRepository _repository;
IContext _context;
public Service(
IContextFactory contextFactory,
IRepository repository)
{
_context = contextFactory.GetContext();
_repository = repository;
}
public int DoSomeStuff()
{
return _repository.Get();
}
}
public class Repository : IRepository
{
IContext _context;
public Repository(IContextFactory contextFactory)
{
_context = contextFactory.GetContext();
}
public int Get()
{
return _context.Data;
}
public void Set(int i)
{
_context.Data = i;
}
}
public class Context : IContext
{
public int Data { get; set; }
}
public interface IContextFactory
{
IContext GetContext();
}
public class ContextFactory : IContextFactory
{
IContext _context;
public void SetContext(Context context)
{
_context = context;
}
public IContext GetContext()
{
return _context;
}
} |
I think you should stick with the workaround since mixing runtime data with services is sort of an anti-pattern. Take a look at this comment for more information about the topic. |
Thank you for that insight, I think everything was starting to look like a nail that DI could resolve. The post from Steven van Deursen, made me think a bit more about it, and I'm still a bit on the fence as to whether I should refactor to pass this data at runtime. Basically, in my situation, I'm looking to refactor some code that is using the context to mostly pass around the state to every method, and I'd like to avoid every method having to have this context argument passed into it. It would seem from the post that he suggests using some kind of context provider to encapsulate the concrete implementation of the state, which is kind of what I was going for here, but I'm not sure if his intent was that you should just pass around the entire context, or only expose the information the client will need. Unfortunately, it seems that most of the information in the context object is being used by the clients. I'm curious whether you think it would be better to add the context as a parameter to a method, or whether using a context provider like above would be more appropriate? |
Well, that depends 😀One example from AspNet.Core is the https://docs.microsoft.com/en-us/aspnet/core/fundamentals/http-context?view=aspnetcore-2.2 Personally, I am not a big fan of passing "contexts" around as methods parameters. I prefer them to be ambient and that I can inject them where they are needed, just like with the Does that make any sense ? 😀 If you need to "capture" the context and have it flow across async/await points, you can do that by storing the context in an |
It does make sense, and I do prefer the idea of passing in the context through the constructor via an interface, as opposed to methods, but wasn't sure whether I'd be shooting myself in the foot somehow. Definitely seems practical in this case. To your second point about capturing the context in There is a portion of my code that needs to "fire and forget" a task at the end of the request flow. A snippet of the idea is below. async Task Main()
{
var container = new ServiceContainer();
container.Register<IRepository, Repository>(new PerScopeLifetime());
container.Register<IService, Service>(new PerScopeLifetime());
container.Register<IContext, Context>(new PerScopeLifetime());
container.Register<IContextFactory, ContextFactory>(new PerScopeLifetime());
var bool someSetting = true;
using (var scope = container.BeginScope())
{
var contextFactory = (ContextFactory)container.GetInstance<IContextFactory>();
contextFactory.SetContext(new Context { Data = i });
var service = scope.GetInstance<IService>();
// My assumption here, is that even though the dependencies are "disposed"
// at the end of the using block, the method should still have no problem
// continuing on another thread since the dependency graph for this
// object has already been instantiated. In this case, the only dependencies that may be
// instantiated happen through message dispatch calls, which internally
// use the container to create a new scope). Am I right about this assumption?
if (someSetting)
var fireAndForget = service.DoSomeStuff();
var value = await service.DoSomeStuff();
}
values.Dump();
}
// Define other methods and classes here
public interface IService
{
int DoSomeStuff();
}
public interface IRepository
{
int Get();
void Set(int i);
}
public interface IContext
{
int Data { get; set; }
}
public class Service : IService
{
IRepository _repository;
IContext _context;
public Service(
IContextFactory contextFactory,
IRepository repository)
{
_context = contextFactory.GetContext();
_repository = repository;
}
public Task<int> DoSomeStuff()
{
return _repository.Get();
}
}
public class Repository : IRepository
{
IContext _context;
public Repository(IContextFactory contextFactory)
{
_context = contextFactory.GetContext();
}
public async Task<int> Get()
{
await Task.Delay(500);
return _context.Data;
}
public async Task Set(int i)
{
await Task.Delay(2000);
_context.Data = i;
}
}
public class Context : IContext
{
public int Data { get; set; }
}
public interface IContextFactory
{
IContext GetContext();
}
public class ContextFactory : IContextFactory
{
IContext _context;
public void SetContext(Context context)
{
_context = context;
}
public IContext GetContext()
{
return _context;
}
} So my thoughts are contained in the comment block above. Is this type of thing safe, since the dependency graph has already been created (no lazies or anything resolved on demand)? |
This code should be safe. All services are |
@seesharper Thanks for the advice!! I think I've got a better understanding of the options available now, so you can close this ticket when ready. |
Great 👍👍 |
Hi there!! I'm trying to use LightInject to resolve the dependencies of implementations that are being resolved on a per request basis. One of the dependencies is a request context that nearly every object in the dependency graph must use, but it also is created upon request based on the parameters passed into the method.
Here is an example of the setup.
The idea here is that the "context" object is passed around to all of the dependencies, but is scoped and resolved dynamically via each request. I thought the idea here, #237, was a fairly similar situation, but I'm not sure it solves my particular problem.
Is there a way to do this in LightInject?
The text was updated successfully, but these errors were encountered: