2

I have a .NET 4.0, WPF app which uses Caliburn 1.5.2 e Autofac 3.0.2.

I want to use command line arguments to customize the building of the IoC container.

The problem is that the command line arguments are only available at Appplication.OnStartup (link) and that will only happen when the Bootstrapper.Configure has already been called.

The reason I want to do that is because I have some services in this application (usb device communication, web services) that I'd like to be able to replace with mocking instances. These services are initialized right after registration (Autofac's AutoActivation extension), so the best moment to choose which type to use is when registering it in the container.

I'm considering:

  • Delaying the initialization of the services to the app's main viewmodel.

  • Split container configuration logic in two stages, one in Bootstrapper.Configure and another in Bootstrapper.OnStartup.

The main caveats with that are:

  • The viewmodel would depend on the services just to initialize them.
  • With container configuration split in two parts, I'd have to look for problems in dependency resolution.
  • I think it would be nicer to have container configuration in only one place.

I also thought about looking for Main(string[] args) and caching the parameters in a static instance, but that code is autogenerated in a WPF app, and interfering with that seemed a little too extreme.

I wonder if anyone knows a nicer way of doing that.

Arthur Nunes
  • 6,718
  • 7
  • 33
  • 46

2 Answers2

4
Environment.GetCommandLineArgs()

I just call that from Bootstrapper.Configure() and check for the command line switches, simple as that.

Arthur Nunes
  • 6,718
  • 7
  • 33
  • 46
1

You can still configure the container in the OnStartup() method, it will cause no problems for you as long as you don't request any of those services from the container either implicitly or explicitly before OnStartup() gets called.

Caliburn.Micro doesn't treat the Configure() method in any special way. In fact if you don't provide overrides for the GetInstance(), GetAllInstances() and BuildUp() methods in your bootstrapper Caliburn.Micro won't be able to consume any of the services registered with your container even if you configure it correctly in the Configure() method.

Edit: One more thing i wanted to clarifiy. Since you are going to provide additional configuration in the OnStartup() method then you should derive your bootstrapper from BootstrapperBase instead of Bootstrapper<TRootModel> because the latter will automatically show your main view model for you and because that main view model might consume services that are not yet registered it may cause some problems, so we want to control when it is showed. Anyway, enough blah blah talking, here is what you could use:

public class AppBootstrapper : BootstrapperBase
{
     SimpleContainer container;

     public AppBootstrapper()
     {
         Start();
     }

     protected override void Configure()
     {  
         container = new SimpleContainer();
         container.Singleton<IWindowManager, WindowManager>();
         container.Singleton<IEventAggregator, EventAggregator>();
         container.PerRequest<IShell, ShellViewModel>();
     }

     protected override object GetInstance(Type service, string key)
     {
         var instance = container.GetInstance(service, key);
         if (instance != null)
             return instance;
         throw new InvalidOperationException("Could not locate any instances.");
     }

     protected override IEnumerable<object> GetAllInstances(Type service)
     {
         return container.GetAllInstances(service);
     }

     protected override void BuildUp(object instance)
     {
         container.BuildUp(instance);
     }

     protected override void OnStartup(object sender, System.Windows.StartupEventArgs e)
     {
         // ######################
         // DO THOSE COMMAND LINE CONFIGURATIONS HERE AND EVERYTHING
         // SHOULD BE JUST FINE.
         // ######################
         DisplayRootViewFor<IShell>();
     }
}
Ibrahim Najjar
  • 19,178
  • 4
  • 69
  • 95
  • I'm already using BootstrapperBase, in fact, my bootstrapper is almost the one you described. I believed the container building should go in the Configure() method, cause I took a look at the source and the IoC class is configured right after Configure() is called. If I do configuration somewhere else, I have to guarantee no instance resolution happens until then, or put null checks in the GetInstance() overrides and call base, if container isn't ready. – Arthur Nunes Jul 24 '13 at 12:18
  • @arthurnunes Your are mostly correct and anyway if you want to use a dependency before the `OnStartup()` method gets called you can instantiate it manually using `new` keyword. – Ibrahim Najjar Jul 24 '13 at 12:26