Reputation: 26909
I am working on a system that processes financial data, for various financial instruments, on several different timeframes.
For example:
EUR/USD
- m1 Timeframe (1 Minute)
- m5 Timeframe (5 Minute)
- m15 Timeframe (15 Minute)
GBP/USD
- m1 Timeframe (1 Minute)
- m5 Timeframe (5 Minute)
- m15 Timeframe (15 Minute)
The timeframes each have a fairly complex processing pipeline, and I'm using DryIOC to route data through the pipeline, using an EventAggregator as described here: DryIOC Event Aggregator
DryIOC is perfect for this as its super fast and can keep up with the amount of data/events I need.
I have dependencies which are at the Instrument Level, that need to be shared between the different timeframes of that instrument.
And I also have global dependencies such as a broker connection manager that need to be shared between all instruments and all timeframes.
Containers are created at runtime; I may switch on/off different instruments and timeframes, and need to make a new container.
Facade seems perfect for this. I can start with a global container, and for any instrument that gets activated, make a Facade for the instrument. And from that container make a container for each timeframe. Resolutions in the Facade container use the local registrations defined there, and then fall back to the parent when unresolved.
However, as described in the docs Facades have their own Singletons. And when I try to resolve a global dependency from a Facade as a singleton I get a new instance.
This test fails:
[Test]
public void Test()
{
var globalContainer = new Container();
globalContainer.Register<IGlobalDependency, GlobalDependency>(Reuse.Singleton);
var EURUSD_Container = new Container(rules => rules.WithFallbackContainer(globalContainer));
EURUSD_Container.Register<IInstrumentDependency, InstrumentDependency>(Reuse.Singleton);
var EURUSD_Timeframe_1_Container = EURUSD_Container.CreateFacade();
EURUSD_Timeframe_1_Container.Register<ITimeframeDependency, TimeframeDependency>(Reuse.Singleton);
var EURUSD_Timeframe_2_Container = EURUSD_Container.CreateFacade();
EURUSD_Timeframe_2_Container.Register<ITimeframeDependency, TimeframeDependency>(Reuse.Singleton);
var globalfromTimeframe1 = EURUSD_Timeframe_1_Container.Resolve<IGlobalDependency>();
var globalfromTimeframe2 = EURUSD_Timeframe_2_Container.Resolve<IGlobalDependency>();
Assert.AreSame(globalfromTimeframe1, globalfromTimeframe2);
}
I've spent three days battling with Facades, Scopes, NamedScopes and combinations of all these things. Scopes don't work because if I make a new scope for an instrument and then a new scope for each timeframe within the instrument and resolve with InCurrent scope - I still get a new version because each timeframe is in its own scope.
Named scopes didn't work because I only know the instrument names at runtime, and adding new registrations for new instruments, timeframes collided.
How can I keep subcontainers separate, but have then share singletons with their parents?
Update:
public void ScopeTest()
{
var globalContainer = new Container();
globalContainer.Register<IGlobalDependency, GlobalDependency>(Reuse.Singleton);
var EURUSD_Container = globalContainer.OpenScope("EUR/USD");
EURUSD_Container.Register<IInstrumentDependency, InstrumentDependency>(Reuse.InCurrentNamedScope("EUR/USD"), serviceKey: "EUR/USD");
var EURUSD_Timeframe_1_Container = EURUSD_Container.OpenScope("m1");
EURUSD_Timeframe_1_Container.Register<ITimeframeDependency, TimeframeDependency>(Reuse.InCurrentNamedScope("m1"), serviceKey: "m1");
var EURUSD_Timeframe_2_Container = EURUSD_Timeframe_1_Container.OpenScope("m5");
EURUSD_Timeframe_2_Container.Register<ITimeframeDependency, TimeframeDependency>(Reuse.InCurrentNamedScope("m5"), serviceKey:"m5");
var USDJPY_Container = globalContainer.OpenScope("USD/JPY");
EURUSD_Container.Register<IInstrumentDependency, InstrumentDependency>(Reuse.InCurrentNamedScope("USD/JPY"), serviceKey: "USD/JPY");
var USDJPY_Timeframe_1_Container = USDJPY_Container.OpenScope("m1");
USDJPY_Timeframe_1_Container.Register<ITimeframeDependency, TimeframeDependency>(Reuse.InCurrentNamedScope("m1"), serviceKey:"m1");
var USDJPY_Timeframe_2_Container = USDJPY_Container.OpenScope("m5");
USDJPY_Timeframe_2_Container.Register<ITimeframeDependency, TimeframeDependency>(Reuse.InCurrentNamedScope("m5"), serviceKey:"m5");
var globalfromEURUSDTimeframe1 = EURUSD_Timeframe_1_Container.Resolve<IGlobalDependency>();
var globalfromEURUSDTimeframe2 = EURUSD_Timeframe_2_Container.Resolve<IGlobalDependency>();
var globalfromUSDJPYTimeframe1 = EURUSD_Timeframe_1_Container.Resolve<IGlobalDependency>();
var globalfromUSDJPYTimeframe2 = EURUSD_Timeframe_2_Container.Resolve<IGlobalDependency>();
Assert.AreSame(globalfromEURUSDTimeframe1, globalfromEURUSDTimeframe2);
Assert.AreSame(globalfromUSDJPYTimeframe1, globalfromUSDJPYTimeframe2);
Assert.AreSame(globalfromEURUSDTimeframe1, globalfromUSDJPYTimeframe2);
}
Produces the following exception:
DryIoc.ContainerException: Unable to register service Namespace.ITimeframeDependency - {DI=25, ImplType="Namespace.TimeframeDependency", Reuse=CurrentScopeReuse {Name="m1", Lifespan=100}} with duplicate key [m1]. Already registered service with same key is {ID=22.... etc.... Name="m1"
on a side note its so annoying you can't copy exceptions from the visual studio test runner.
Upvotes: 1
Views: 442
Reputation: 26909
I managed to achieve this in the end by using WithRegistrationsCopy()
e.g.
_localContainer = container.WithRegistrationsCopy();
This allows me to get top level singleton instances in subcontainers, but also have specific registrations in subcontainers not known to other containers.
Upvotes: 2