advapi
advapi

Reputation: 3907

Changing IoC provider on an Caliburn Micro WPF Application

I've an existing WPF application based on caliburn micro MVVM pattern which was using Ideablade/cocktail for accessing to database. Now I've switched to servicestack and I was keeping on cocktail just for the composition pattern. Since I've noticed it takes quite a bit long to start the application I've done some test and Ninject performs better. I find extremly usefull the MEF approach of defining the Export/ImportingConstrucor approach but and I was wondering how I can have it with Ninject... is it possible?

In my current implementation I've something as

[Export(typeof(IMyInterface))]
[Export(typeof(MyFirstViewModel))]
public class MyFirstViewModel:IMyInterface
{
    [ImportingConstructor]
    public MyFirstViewModel(IEventAggregator eventAggregator)ù
    {
    }
}

I've seend that in ninject I've to define something as

 mKernel.Bind<MyFirstViewModel>().To<MyFirstViewModel>();
 mKernel.Bind<MyFirstViewModel>().To<MyFirstViewModel>();

Can it be automatic? Can I also define a funct to resolve when not found?

Thanks

StackTrace :
 at Caliburn.Micro.IoC.<.cctor>b__0(Type service, String key) in c:\Users\Rob\Documents    \CodePlex\caliburnmicro\src\Caliburn.Micro.Silverlight\IoC.cs:line 13
 at Caliburn.Micro.IoC.Get[T](String key) in c:\Users\Rob\Documents\CodePlex\caliburnmicro\src\Caliburn.Micro.Silverlight\IoC.cs:line 32
 at myApp.Modules.Core.Framework.ViewModels.myAppScreenBase`1..ctor() in c:\Projects\myApp\branches\myApp-branch-20140526\myApp\Core\Framework\ViewModels\myAppScreenBase.cs:line 44
 at myApp.Modules.Core.Framework.ViewModels.myAppSimpleScreen`1..ctor() in c:\Projects\myApp\branches\myApp-branch-20140526\myApp\Core\Framework\ViewModels\myAppSimpleScreen.cs:line 8
 at myApp.Modules.AdE.ViewModels.CMATCLIDDelegheViewModel..ctor(IAdERepository repository, IDialogManager dialogManager, ICommonRepository commonRepository) in c:\Projects\myApp\branches\myApp-branch-20140526\myApp\Modules.AdE\ViewModels\CMATCLIDDelegheViewModel.cs:line 56
 at DynamicInjector1033b54d439c44dbaa064db1c7e82f18(Object[] )
 at Ninject.Activation.Providers.StandardProvider.Create(IContext context)
 at Ninject.Activation.Context.ResolveInternal(Object scope)
 at Ninject.Activation.Context.Resolve()
 at Ninject.KernelBase.<>c__DisplayClass15.<Resolve>b__f(IBinding binding)
 at System.Linq.Enumerable.WhereSelectListIterator`2.MoveNext()
 at System.Linq.Enumerable.<CastIterator>d__b1`1.MoveNext()
 at System.Linq.SystemCore_EnumerableDebugView`1.get_Items()

RepositoryExport :

public class RepositoryBindingGenerator : IBindingGenerator
{
    public IEnumerable<IBindingWhenInNamedWithOrOnSyntax<object>> CreateBindings(Type type, IBindingRoot bindingRoot)
    {

        foreach (var attribute in type.GetCustomAttributes(typeof(RepositoryAttribute), false)
            .OfType<RepositoryAttribute>())
        {
            yield return bindingRoot
                            .Bind(attribute.ContractType ?? type)
                            .To(type).InSingletonScope();
        }


    }
}

but I got this compile error

Error 19 Cannot implicitly convert type 'Ninject.Syntax.IBindingNamedWithOrOnSyntax' to 'Ninject.Syntax.IBindingWhenInNamedWithOrOnSyntax'. An explicit conversion exists (are you missing a cast?)

Upvotes: 0

Views: 653

Answers (1)

BatteryBackupUnit
BatteryBackupUnit

Reputation: 13233

Depending on the configuration of ninject (by default its enabled) you don't need to bind a type to itself, ninject will resolve it automatically. So mKernel.Bind<MyFirstViewModel>().To<MyFirstViewModel>(); is superfluous. Remark: Creating the binding anyway also works.

However, if you want to bind Bar to IFoo or Foo to IFoo you need to bind it. With it you can tell ninject to look for all types with an [Export] attribute and bind these. Here comes the ninject conventions extension to the rescue. Get the ninject.extensions.conventions nuget package.

Then create a convention binding:

kernel.Bind(x => x
                .FromThisAssembly()
                .SelectAllClasses()
                .WithAttribute<ExportAttribute>()
                .BindWith<ExportBindingGenerator>());

public class ExportBindingGenerator : IBindingGenerator
{
    public IEnumerable<IBindingWhenInNamedWithOrOnSyntax<object>> CreateBindings(Type type, IBindingRoot bindingRoot)
    {
        foreach (var attribute in type.GetCustomAttributes<ExportAttribute>())
        {
            yield return bindingRoot
                            .Bind(attribute.ContractType)
                            .To(type);
        }
    }
}

Things get a bit more complicated when you need to also use the [ImportingConstructor] attribute to tell ninject which constructor to use. But i would suppose that you don't need it, since Ninject's auto-constructor-selection. What you can do however is replace all [ImportingConstructor] attributes with Ninject's [Inject] attribute which does exactly the same.

Notes:

  • You may need to use another method than .FromThisAssembly() to specify all the assemblies which contain the implementation types.
  • If the implementation types are not public, you need to add IncludeNonePublicTypes() to the convention.

Upvotes: 2

Related Questions