1

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.

Community
  • 1
  • 1
reach4thelasers
  • 26,181
  • 22
  • 92
  • 123
  • I assume you are disposing your facades afterwards? What should happen with shared singletons then? – dadhi Nov 10 '16 at 05:46
  • Can you elaborate more why named scopes are not working for you, may be via small example? – dadhi Nov 10 '16 at 05:48
  • updated the question with an example – reach4thelasers Nov 10 '16 at 12:38
  • Given the example with scopes from Update: what if you change the scope names to identify the path, e.g. "EUR-USD/m1". And additionally remove the service keys from scoped registrations, because the names are not needed to select the scoped registration, there is an implicit rule to enable this. – dadhi Nov 10 '16 at 19:42
  • Not sure you can call this a "scope" if registrations in different scopes need to have unique names. But I found a solution that got things working the way I wanted. – reach4thelasers Nov 12 '16 at 14:12

1 Answers1

2

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.

reach4thelasers
  • 26,181
  • 22
  • 92
  • 123
  • Glad it worked for you. I am interested in real-world cases of using facade / child containers to improve things in V3: https://bitbucket.org/dadhi/dryioc/issues/270/diagnose-fallback-facade-container – dadhi Nov 12 '16 at 14:48