0

Я пытаюсь получить доступ к моей MainPageViewModel непосредственно из моего XAML страницы определения:Доступ MainViewModel из XAML страницы при использовании Autofac и вводят услугу (ы)

<Window x:Class="MyApp.MainWindow" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
     xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
     xmlns:local="clr-namespace:MyApp" 
     mc:Ignorable="d" 
     Title="MyApp" Height="350" Width="650" ShowInTaskbar="True" 
     WindowState="Maximized" 
     DataContext="{Binding Source={StaticResource Locator}, Path=MainViewModel}"> 

Мой локатор создан в моем app.xaml:

<Application.Resources> 
    <ResourceDictionary> 
     <vm:ViewModelLocator x:Key="Locator" /> 
    </ResourceDictionary> 
</Application.Resources> 

И мой ViewModelLocator определяется следующим образом:

public class ViewModelLocator 
{ 
    private static readonly IContainer Container; 

    static ViewModelLocator() 
    { 
     // load specific logger - Hardcoded for sample sake 
     string loggerAssembly = "Logger.Database.dll"; 

     // register logger database by type i.e. ILogger 
     var builder = new ContainerBuilder(); 
     Assembly assembly = Assembly.LoadFrom(loggerAssembly); 
     builder.RegisterAssemblyTypes(assembly).As<ILogger>();    

     // register MainViewModel and Inject ILogger 
     // Is this correct? Is this how you inject a service? 
     builder.RegisterType<MainViewModel>().As<ILogger>().InstancePerDependency(); 

     Container = builder.Build(); 
    } 

    public ILogger Logger => Container.Resolve<ILogger>(); 

    // property to access MainViewModel in XAML 
    public MainViewModel MainViewModel => Container.Resolve<MainViewModel>(); 
} 

Но когда я запускаю мое приложение, он генерирует ошибку из App.XAML в локаторе и пытается получить доступ к свойству MainViewModel, как определено выше. Ошибка я получаю:

Сообщение исключения:

None of the constructors found with 
'Autofac.Core.Activators.Reflection.DefaultConstructorFinder' on 
type 'MyCompany.Logger.Database.Logger' can be invoked with the 
available services and parameters: Cannot resolve parameter 
'System.String connectionString' of constructor 'Void .ctor(System.String)'. 

Исключение StackTrace:

at Autofac.Core.Activators.Reflection.ReflectionActivator.ActivateInstance 
(IComponentContext context, IEnumerable`1 parameters) 
at Autofac.Core.Resolving.InstanceLookup.Activate(IEnumerable`1 parameters) 

И мой MainPageViewModel определяется следующим образом:

Public class MainPageViewModel() 
{ 
    public MainViewModel(ILogger logger) 
    { 
    .... 
    } 
} 

Любые идеи о том, как я могу это решить?

UPDATE 1:

Обратите внимание, что мое первоначальное определение:

builder.RegisterAssemblyTypes(assembly).As<ILogger>(); 

было, вероятно, хорошо, как и вся моя "КИ" сборка только реализовать интерфейс ILogger но я первоначально использовать AsImplementedInterfaces()

Я обновил свой код на основе предложений @HenkHolterman:

builder.RegisterType<MainViewModel>(); 
builder.RegisterAssemblyTypes(assembly).AsImplementedInterfaces(); 

Но я не получаю следующее исключение:

None of the constructors found with 'Autofac.Core.Activators.Reflection. 
DefaultConstructorFinder' on type 'MyCompany.Logger.Database.Logger' can be 
invoked with the available services and parameters: 

Cannot resolve parameter 'System.String connectionString' of constructor 
'Void .ctor(System.String)'. 

Теперь на основе последней части этого исключения, по-видимому, связано с параметром ConnectionString, и этот параметр был частью моей базы данных класса регистратора (MyCompany.Logger.Database.Logger), поэтому я временно удалил его, чтобы проверить, что предложил @HenkHolterman, и теперь это работает, но у меня все еще есть проблема.

Мне нужно передать строку соединения, когда я использую регистратор базы данных. Мне нужно передать имя файла, когда я использую регистратор файлов и т. Д.

так как передать параметр конструктору журнала. Я пробовал:

builder.RegisterAssemblyTypes(assembly).AsImplementedInterfaces(). 
    WithParameter("ConnectionString", connectionString); 

, но он по-прежнему вызывает параметр меньше (т.е. Logger) в MyCompany.Logger.Database.Logger.

Если я добавлю параметр connectionString обратно в конструктор Logger, он отменяет исключение, упомянутое выше.

Я продолжу исследовать его завтра и обновит свой ответ, если найду что-нибудь, но если у кого-нибудь есть предложения, я бы очень благодарен за помощь.

Обновления 2

Как было отмечено @HenkHolterman, параметры чувствительны к регистру и должны соответствовать конвенциям, используемым в конструкторе.

Обратите внимание, что есть два способа передать параметры с помощью Autofac. При регистрации и при разрешении. Оба работают одинаково.

Таким образом, приведенный выше код, который генерируется сообщение об ошибке при передаче параметров должны быть определены как:

builder.RegisterAssemblyTypes(assembly).AsImplementedInterfaces(). 
WithParameter("connectionString", connectionString); 
+0

Возможно, вы должны использовать опцию лямбда-калькулятора. –

+0

'.WithParameter (" ConnectionString ", connectionString)' - действительно ли параметр использует 'C', а не' c'? и connectionString должна быть константой (известной во время регистрации). –

+0

@HenkHolterman, ты был прав. У меня был «C» вместо «c». – Thierry

ответ

1
// register MainViewModel and Inject ILogger 
    // Is this correct? Is this how you inject a service? 
    builder.RegisterType<MainViewModel>() 
      .As<ILogger>() 
      .InstancePerDependency(); 

Нет, это не правильно. Вы регистрируете свой MainViewModel как ILogger. Рекурсивная и, вероятно, конфликтующая регистрация.

Вы должны зарегистрировать MainVm как сам. Я не так хорошо знаком с AutoFac, но я думаю, что это просто:

builder.RegisterType<MainViewModel>(); 

но IIRC есть также .AsSelf() расширения.

Но builder.RegisterAssemblyTypes(assembly).As<ILogger>(); также выглядит неправильно. Это регистрирует каждый типа в этой сборке как ILogger. Вероятно, вы хотите что-то вроде

builder.RegisterAssemblyTypes(assembly) 
      .Where(...)      // optional filter 
      .AsImplementedInterfaces(); 
+0

Я сделал предложенные изменения и частично работал. Частично, поскольку я не могу заставить его работать, когда у моего конструктора Logger есть параметр, но он определенно находится на правильном пути. Я обновил свой ответ с дополнительной информацией. Дополнительная обратная связь действительно будет оценена. Еще раз спасибо. – Thierry