Order of Startup Tasks not being respected

Jun 27, 2013 at 6:42 PM
I don't know if it's a bug, but I'm having trouble to set up the startup tasks: the order is not being applied. How do I know? I set a breakpoint in the each StartupTask and the last one is being called first:
Bootstrapper.IncludingOnly
               .Assembly(Assembly.GetAssembly(typeof(App)))
               .AndAssembly(Assembly.GetAssembly(typeof(Adapter)))
               .With.Unity()
               .And.ServiceLocator()
               .And.AutoMapper()
               .And.StartupTasks()
               .UsingThisExecutionOrder(s => s
                   .First<SetupServiceLocatorToContainerTask>()
                   .Then<RegionAdapterMappingsTask>()
                   .Then<RegionBehaviorTask>()
                   .Then<FrameworkExceptionTask>()
                   .Then<ShellTask>()
                   .Then<InitializeModulesTask>())
               .Start();
The InitializeModulesTask is being called first of all.
public class InitializeModulesTask : IStartupTask
    {
        private readonly IUnityContainer container;

        public InitializeModulesTask(IUnityContainer container)
        {
            this.container = container;
        }

        public void Reset()
        {
        }

        public void Run()
        {
            var manager = container.Resolve<IModuleManager>();
            manager.Run();
        }
    }
This one is meant to be called first:
public class SetupServiceLocatorToContainerTask : IStartupTask
    {
        private readonly IUnityContainer container;

        public SetupServiceLocatorToContainerTask(IUnityContainer container)
        {
            this.container = container;
        }

        public void Reset()
        {
            
        }

        public void Run()
        {
            container.RegisterInstance<IServiceLocator>(ServiceLocator.Current);
        }
    }
Jun 27, 2013 at 7:04 PM
Okay. Actually is not running the tasks. It tries to instanciate each before running, and since this one:
public class RegionAdapterMappingsTask : IStartupTask
    {
        private readonly RegionAdapterMappings regionAdapterMappings;
        private readonly IServiceLocator serviceLocator;

        public RegionAdapterMappingsTask(RegionAdapterMappings regionAdapterMappings, IServiceLocator serviceLocator)
        {
            this.regionAdapterMappings = regionAdapterMappings;
            this.serviceLocator = serviceLocator;
        }

        public void Reset()
        {
        }

        public void Run()
        {
            regionAdapterMappings.RegisterMapping(typeof(Selector), serviceLocator.GetInstance<SelectorRegionAdapter>());
            regionAdapterMappings.RegisterMapping(typeof(ItemsControl), serviceLocator.GetInstance<ItemsControlRegionAdapter>());
            regionAdapterMappings.RegisterMapping(typeof(ContentControl), serviceLocator.GetInstance<ContentControlRegionAdapter>());
        }
    }
depends on IServiceLocator, and I only register the IServiceLocator on SetupServiceLocatorToContainerTask Run, it can't instanciate the RegionAdapterMappingsTask.
Wouldn't be great to only instanciate moments before running the task?
Well... I will have to use ServiceLocator.Current, which is awfull.
Coordinator
Jun 27, 2013 at 7:36 PM
I have three suggestions

1) If you are trying to register all the RegionAdapters in your mappings you could create a registration class that registers a populated Dictionary<type, IRegionAdapter>.
Then you can inject such dictionary in the constructor of your RegionAdapterMappingTask. After that registering is piece of cake with a Link statement
    adapters.ForEach(p => regionAdapterMappings.RegisterMapping(p.Key, p.Value));
2) Inject in the constructor the three types of RegionAdapters and uset them on your Run method. No need to use a container.


3) If you don't like any you can also use the Bootstrapper Resolve method to get your instances like this
Bootstrapper.ContainerExtension.Resolve<SelectorRegionAdapter>()
I hope that helps.

Luis
Jun 27, 2013 at 8:30 PM
Edited Jun 27, 2013 at 8:39 PM
I'm trying (again, I must say) to integrate the Bootstrapper into our project. My headache is that we are using PRISM framework for WPF, and PRISM is just a pain in the a** when comes to the bootstrap logic. They didn't do a good job in that part, and I'm failing miserably every time I try to migrate.
I tried to use SimpleInjector, but no good there, the PRISM simple was not made for another IoC other than Unity and MEF (they claim it's possible.. well, not so much)

I'd have to go through the UnityBoostrapper code in PRISM codeplex project to see what is required to run (in which order) to be able to try migrating to Bootstrapper library. So far, I couldn't make it work.

Our project is a big project, but it is fairly well design.
My problem now is that if I remove the AutoMapper (by making the Profiles' classes INTERNAL), the projects runs successfully, but when it tries to use AutoMapper, it fails (obviously).
When I make the Profiles classes PUBLIC, hence the boostrapper will see it, the project give an exception on the following StartupTask:

PS: the IServiceLocator is being registered in the SetupServiceLocatorToContainerTask, which comes before the ShellTask
public class ShellTask : IStartupTask
    {
        private readonly IUnityContainer container;

        public ShellTask(IUnityContainer container)
        {
            this.container = container;
        }

        public void Reset()
        {
        }

        public void Run()
        {
            var shell = new Shell();
            RegionManager.SetRegionManager(shell, container.Resolve<IRegionManager>());
            RegionManager.UpdateRegions(); //ACTIVATIONEXCEPTION here: Activation error occured while trying to get instance of type AutoPopulateRegionBehavior, key ""

            SplashScreenSimulator.Start(1);
            App.Current.MainWindow = shell;
            App.Current.MainWindow.Show();
            SplashScreenSimulator.Close();
        }
    }
I have no clue whatsoever what's happening... so sad.. I'll have to abandon the migration again...

Resolution of the dependency failed, type = "Microsoft.Practices.Prism.Regions.Behaviors.AutoPopulateRegionBehavior", name = "(none)".
Exception occurred while: while resolving.
Exception is: InvalidOperationException - The type IServiceLocator does not have an accessible constructor.
At the time of the exception, the container was:

Resolving Microsoft.Practices.Prism.Regions.Behaviors.AutoPopulateRegionBehavior,(none)
Resolving parameter "regionViewRegistry" of constructor Microsoft.Practices.Prism.Regions.Behaviors.AutoPopulateRegionBehavior(Microsoft.Practices.Prism.Regions.IRegionViewRegistry regionViewRegistry)
Resolving Microsoft.Practices.Prism.Regions.RegionViewRegistry,(none) (mapped from Microsoft.Practices.Prism.Regions.IRegionViewRegistry, (none))
Resolving parameter "locator" of constructor Microsoft.Practices.Prism.Regions.RegionViewRegistry(Microsoft.Practices.ServiceLocation.IServiceLocator locator)
Resolving Microsoft.Practices.ServiceLocation.IServiceLocator,(none)
Coordinator
Jun 27, 2013 at 8:39 PM
Unfortunately I don't have any experience with Prism.
Is it possible that Prism is using a different unity container than the one that Boostrapper is initializing?
Jun 27, 2013 at 9:14 PM
luisbocaletti wrote:
Is it possible that Prism is using a different unity container than the one that Boostrapper is initializing?
I don't know, but by what I could figured: no, it's not using...
I think it's never using the Container explicity (mainly because it can't, since it uses Unity or Mef).
It declares the interfaces in the constructor, so it could be resolved implicity.

But.. I don't really know, I'll have to dig deeper in the code... the exception is odd indeed. The IServiceLocator is configured in the container...

PRISM UnityBootstrapper
Jun 27, 2013 at 9:58 PM
Edited Jun 27, 2013 at 9:58 PM
Sorry again, but maybe you can help hehe.. I think I found the source of the problem. I have this Map:
public class WebServiceProfile : AutoMapper.Profile
    {
        protected override void Configure()
        {
            Mapper.CreateMap<Perfil, Model.Profile>()
                  .ForMember(dest => dest.AspectRatio, opt => opt.ResolveUsing(p => p.WideScreen ? AspectRatio.SixteenByNine : AspectRatio.FourByThree))
                  .ForMember(dest => dest.FFMPEGParameters, opt => opt.MapFrom(src => src.ParametrosFFMPEG))
                  .ForMember(dest => dest.Height, opt => opt.MapFrom(src => src.Altura))
                  .ForMember(dest => dest.Id, opt => opt.MapFrom(src => src.Codigo))
                  .ForMember(dest => dest.Name, opt => opt.MapFrom(src => src.Nome))
                  .ForMember(dest => dest.Parameters, opt => opt.MapFrom(src => src.Parametros))
                  .ForMember(dest => dest.Width, opt => opt.MapFrom(src => src.Largura));
        }
    }
Look at the Model.Profile.
If I comment those lines, the project runs successfully! oO'
Coordinator
Jun 27, 2013 at 10:22 PM
I don't see anything wrong with this code. Can you try commenting out all members and un-comment them one by one to see which line is causing your headaches.?
Jun 28, 2013 at 2:02 PM
Edited Jun 28, 2013 at 2:43 PM
Man.. this is the weirdest thing ever.
The source of the problem is the "Perfil" class. It is a class generated by wsdl tool. It's a class from a WebService
 Mapper.CreateMap<Perfil, Model.Profile>();
More oddly, there are 3 properties in the Perfil class that if I uncomment, the project fails. If I uncomment any of the properties, the project fails. I mean, WTF?
 [System.Diagnostics.DebuggerStepThroughAttribute()]
    [System.ComponentModel.DesignerCategoryAttribute("code")]
    [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://tempuri.org/")]
    public partial class Perfil {
        
        private int codigoField;
        
        private string nomeField;
        
        private string parametrosField;
        
        private Dispositivo[] dispositivosField;
        
        private bool wideScreenField;
        
        private int alturaField;
        
        private int larguraField;
        
        private int bitrateField;
        
        private string parametrosFFMPEGField;
        
        /// <remarks/>
        public int Codigo {
            get {
                return this.codigoField;
            }
            set {
                this.codigoField = value;
            }
        }
        
        /// <remarks/>
        public string Nome
        {
            get
            {
                return this.nomeField;
            }
            set
            {
                this.nomeField = value;
            }
        }
        
        ///// <remarks/>
        public string Parametros
        {
            get
            {
                return this.parametrosField;
            }
            set
            {
                this.parametrosField = value;
            }
        }
        
        ///// <remarks/>
        [System.Xml.Serialization.XmlArrayItemAttribute(IsNullable = false)]
        public Dispositivo[] Dispositivos
        {
            get
            {
                return this.dispositivosField;
            }
            set
            {
                this.dispositivosField = value;
            }
        }
        
        ///// <remarks/>
        public bool WideScreen
        {
            get
            {
                return this.wideScreenField;
            }
            set
            {
                this.wideScreenField = value;
            }
        }
        
        ///// <remarks/>
        //public int Altura
        //{
        //    get
        //    {
        //        return this.alturaField;
        //    }
        //    set
        //    {
        //        this.alturaField = value;
        //    }
        //}
        
        ///// <remarks/>
        //public int Largura
        //{
        //    get
        //    {
        //        return this.larguraField;
        //    }
        //    set
        //    {
        //        this.larguraField = value;
        //    }
        //}

        /// <remarks/>
        public int Bitrate
        {
            get
            {
                return this.bitrateField;
            }
            set
            {
                this.bitrateField = value;
            }
        }

        ///// <remarks/>
        //public string ParametrosFFMPEG
        //{
        //    get
        //    {
        //        return this.parametrosFFMPEGField;
        //    }
        //    set
        //    {
        //        this.parametrosFFMPEGField = value;
        //    }
        //}
    }
The properties are ParametrosFFMPEG, Largura and Altura
Coordinator
Jun 28, 2013 at 2:44 PM
Edited Jun 28, 2013 at 2:47 PM
That is weird. Can you try mapping Perfil to Perfil and Profile to Profile to see if that fails too?
EDIT: Never mind I just saw your latest comment. Can you explicitely ignore those properties in you Map and see if it fails?
Jun 28, 2013 at 3:19 PM
Edited Jun 28, 2013 at 3:37 PM
The problem is that I'm mapping from Profile to Perfil, not the other way around. So I can't ignore those properties because I'm not mapping from Perfil, I'm mapping from Profile.
I don't really know whats going on... another test and another result: others properties of the Perfil also fails...

The fact is that there's no link (for my perspective) between the properties and the error: "The type IServiceLocator does not have an accessible constructor."

Well... has something to do with this Perfil class, which is weird... but thanks for helping...