Caliburn-Micro / Caliburn.Micro

A small, yet powerful framework, designed for building applications across all XAML platforms. Its strong support for MV* patterns will enable you to build your solution quickly, without the need to sacrifice code quality or testability.
http://caliburnmicro.com/
MIT License
2.8k stars 778 forks source link

Calburn.Micro resolving performance #795

Closed tapika closed 2 years ago

tapika commented 2 years ago

In following article:

https://www.palmmedia.de/Blog/2011/8/30/ioc-container-benchmark-performance-comparison

There was multiple containers tested, and results were collected - including Caliburn.Micro.

At the moment Caliburn.Micro is not fastest container - if you check tests on github (https://github.com/danielpalme/IocPerformance) - you will find out that container named "No" is the fastest.

No container is based on IocPerformanceDictionary, which apparently has superior performance compared to other alternatives.

Since Caliburn.Micro is not purely container itself, it's tightly bound to UI - I was thinking - why not to combine all good parts into one.

So in theory Caliburn.Micro could reuse IocPerformanceDictionary and improve benchmark performance. (To have both - UI functionality and container performance)

vb2ae commented 2 years ago

The NO container in the performance test is not a dependency injection container. It is just used for a base line. You can use other dependency injection containers with Caliburn.Micro. Here is a sample that uses Microsoft Dependency Injection.

https://github.com/vb2ae/CaliburnMicroMicosoftDI

Here is a link to an example that uses Castle Windsor.

https://gist.github.com/tarasn/55079e824b64e4e0e072

I was thinking of making examples that uses DryLoc and UnityContainer. Any suggested container you would like to see?

I will work on creating some templates that use other di containers.

tapika commented 2 years ago

is not a dependency injection container. It is just used for a base line

I've understood that container no satisfies same requirements as rest of containers - meaning you can register and resolve required interfaces. Container "no" might not have everything that container supports, but it has basic support as majority of containers. Maybe I missed something ?

Any suggested container you would like to see?

What is the purpose ? I think it maybe makes sense to cross bind Caliburn.Micro to UnityContainer for people who use unity, but eventually it's also slow - maybe just replacing all containers with "no" container - then performance would be best.

gerfen commented 2 years ago

@vb2ae Thanks for creating the sample which integrates Microsoft.Extensions.DependencyInjection. I have a question regarding using a similar approach with a WPF project. In my WPF project I have created a boostrapper to do the wire up. I'm struggling with getting INavigationService wired up as there does not appear to be a hook to get to the Frame during initialization of my application. I've searched through the issues in this repository and on StackOverflow but have not found an answer.

I have found older samples of doing the wire up of INavigationService with SimpleContainer where a method is called in the ShellViewModel from the ShellView like this:

        <Frame cm:Message.Attach="RegisterFrame($source)"  Grid.Row="1" NavigationUIVisibility="Hidden" />
        public void RegisterFrame(Frame frame)
        {
            _navigationService = new FrameAdapter(frame);
            _container.Instance(_navigationService); 
            _navigationService.NavigateToViewModel(typeof(LandingViewModel));
        }

Unfortunately, this approach will not work with Microsoft.Extensions.DependencyInjection as the ServiceProvider will have already been created before RegsiterFrame has been called and MS DI does not support adding new registrations to the ServiceCollection after ServiceProvider has been created.

I would appreciate any advice on how to register an instance of INavigationService for my WPF app with in my Boostrapper class.

Thanks in adavance!

vb2ae commented 2 years ago

@gerfen What framework are you using for your wpf app?

gerfen commented 2 years ago

@vb2ae Not sure exactly what you're asking? UI framework?

vb2ae commented 2 years ago

Are you using .net 5, .net 6, .net core 3.1, .net framework 4.8? etc

gerfen commented 2 years ago

Oh, .net 6.

gerfen commented 2 years ago

@vb2ae I found a path forward. There may be a better way but this is what I came up with...

Instead of declaring the Frame in my ShellView, I'm programmatically creating it and adding to the Grid control defined in ShellView. This allows me to create the Frame and a FrameAdapter then register the FrameAdapter as an instance of INavigationService in Bootstrapper.Configure() like so (note - I'm still using SimpleContainer here):

protected override void Configure()
{
    _container = new SimpleContainer();
     //... code elided
    _frameSet = new FrameSet();
    _container.RegisterInstance(typeof(INavigationService), null, _frameSet.NavigationService);
}

where FrameSet looks like this:

public class FrameSet
{
    public FrameSet()
    {
        Frame = new Frame
        {
            NavigationUIVisibility = NavigationUIVisibility.Hidden
        };
        FrameAdapter = new FrameAdapter(Frame);
    }
    public Frame Frame { get; private set; }
    public FrameAdapter FrameAdapter { get; private set; }
    public INavigationService NavigationService => FrameAdapter as INavigationService;
}

Then in Bootstraper.OnStartup, I add the Frame to the Grid control:

 protected override async void OnStartup(object sender, StartupEventArgs e)
 {
      await DisplayRootViewForAsync<ShellViewModel>();
      AddFrameToMainWindow(_frameSet.Frame);

      _frameSet.NavigationService.NavigateToViewModel(typeof(LandingViewModel));
 }

 private void AddFrameToMainWindow(Frame frame)
 {
      var mainWindow = Application.MainWindow;
      if (mainWindow == null)
      {
          throw new NullReferenceException("'Application.MainWindow' is null.");
      }

      if (mainWindow.Content is not Grid grid)
      {
          throw new NullReferenceException("The grid on 'Application.MainWindow' is null.");
      }

      Grid.SetRow(frame, 1);
      Grid.SetColumn(frame, 0);
      grid.Children.Add(frame);
  }

If you have a better approach, I'm open to suggestions.

Thanks!