0

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?)

advapi
  • 3,661
  • 4
  • 38
  • 73
  • I think the exception is unrelated to Ninject, please see http://stackoverflow.com/questions/17622546/ioc-is-not-initialized-on-win7-xp – BatteryBackupUnit May 26 '14 at 09:56

1 Answers1

2

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.
BatteryBackupUnit
  • 12,934
  • 1
  • 42
  • 68
  • Hello BatteryBackupUnit, I've tried your solution and it partially works...partially since I forgot to tell you that in some ViewModels I have to export an ITaggable interface and the viewmodel itself, when I try to resolve the kernel.GetAll I got an exception tellin IoC has not been initialized.. how can I fix this? Thanks – advapi May 26 '14 at 07:13
  • Can you please provide the exact exception? Complete message and Stacktrace. – BatteryBackupUnit May 26 '14 at 07:25
  • IoC is not initialized. 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 – advapi May 26 '14 at 09:15
  • 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) – advapi May 26 '14 at 09:15
  • at Ninject.Activation.Context.ResolveInternal(Object scope) at Ninject.Activation.Context.Resolve() at Ninject.KernelBase.<>c__DisplayClass15.b__f(IBinding binding) at System.Linq.Enumerable.WhereSelectListIterator`2.MoveNext() at System.Linq.Enumerable.d__b1`1.MoveNext() at System.Linq.SystemCore_EnumerableDebugView`1.get_Items() – advapi May 26 '14 at 09:16
  • please edit the original question and put the error / stacktrace there.. it's so unreadable as a comment! ;-) – BatteryBackupUnit May 26 '14 at 09:17
  • I'll do right now, btw I've seen that the problem is when I do IoC.Get(); inside the constructors – advapi May 26 '14 at 09:38
  • I've almost managed... I had to set the two function of ioc... GetInstance and GetAllInstances... Thanks – advapi May 26 '14 at 19:33
  • Excuse me again, I've seen that this way I've got my Repositories that are not singleton, I've created a CustomAttribute called RepositoryAttribute and I've decorated all my repo, I want to bind them InSingletonScope.can I do kernel.Bind(x => x .FromThisAssembly() // .IncludingNonePublicTypes() .SelectAllClasses() .WithAttribute() .WithAttribute() .BindWith() ); – advapi May 27 '14 at 13:23
  • sure thing, just adjust the binding generator, add an `.InSingletonScope()` after to `.To(type)`. – BatteryBackupUnit May 27 '14 at 19:24