Intereting Posts
Выбор пользовательского поставщика кэша вывода для определенных действий controllerа Доступ к сервису WCF из клиентского приложения, когда пользователь находится за прокси-сервером Получить только методы с определенной подписью из Type.GetMethods () Создавать zip-файл из всех файлов в папке Почему System.Windows.Forms.Control не помечен как сериализуемый? Сделать атрибут MSTest ? Как завершить соединение HttpWebRequest на C #? Это не работает даже при настройке таймаута или readwritetimeout Cant Mock my Get () из моего репозитория – MongoDB.Driver 2.2.3 Как получить доступ к элементам в динамическом списке? C # – Можно ли использовать инициализатор массива для построения одного байтового массива из другого? Синхронизация streamов. Как именно блокировка делает доступ к памяти «правильной»? Как переключаться между представлениями с помощью DataTemplate + Triggers Несоответствие поведения оператора Запустить программу с параметрами Можно ли получить символ авторского права в приложении C # Console?

Разрешение classа с настраиваемым параметром в Simple Injector

Я создаю WPF MVVM-приложение, используя Simple Injector в качестве контейнера DI. Теперь у меня возникают некоторые проблемы, когда я пытаюсь разрешить представление из Simple Injector, потому что мне нужно передать параметр в свой конструктор во время построения (не при регистрации вида в контейнере, таким образом, это не применимо: Простые значения вставки форсунки в конструктор ).

То, что мне нужно, это примерно так:

var item = container.GetInstance(myParameter); 

Я прочитал несколько мест, что это невозможно в Simple Injector, потому что это не должно быть сделано (в том числе здесь: https://simpleinjector.codeplex.com/discussions/397080 ).

Это правда, и если да, то как я могу это сделать?


Исходная информация

У меня есть коллекция нескольких моделей и моделей просмотра, которые просматриваются определенным ключом, а параметр, который я хочу передать в представление, – это ключ для модели представления. Я нашел это необходимым, потому что модели просмотра и модели используются во многих местах приложения и должны оставаться в синхронизации / быть одинаковыми, если они имеют один и тот же ключ. Я не думаю, что могу использовать всю видимость для решения этой проблемы, и я не знаю ключей, когда я регистрируюсь в контейнере. Я также понял, что подход ViewModelLocator может быть шаблоном ServiceLocator (анти -?), Однако в настоящее время это лучшее, что у меня есть.

Мой конструктор в настоящее время выглядит так, и я хочу, чтобы IViewModelLocator был разрешен, а я передаю ключ:

 public FillPropertiesView(IViewModelLocator vml, object key) { // .. Removed code // Assign the view model var viewModel = vml.GetViewModel(key); DataContext = viewModel; } 

IViewModelLocator выглядит следующим образом (и аналогичный интерфейс существует для моделей).

 public interface IViewModelLocator { // Gets the view model associated with a key, or a default // view model if no key is supplied T GetViewModel(object key = null) where T : class, IViewModel; } 

Теперь у меня есть следующие вопросы:

  • Каков наилучший способ разрешить представление с помощью ключа модели представления?
  • Должен ли я сделать некоторый рефакторинг, чтобы включить это?
  • Я пропустил некоторые из возможностей контейнера DI, так как я создал свой собственный словарь на основе ViewModelLocator ?

Дополнительная информация

Я показал выше ViewModelLocator, и причина, по которой я использую это, заключается в том, чтобы сохранить совместимость (в основном это просто дает мне данные о времени разработки при открытии в Blend). Модель просмотра во время выполнения могла быть одинаковой для каждого экземпляра представления (не зависящего от ключа), если бы мне не приходилось открывать разные windows одновременно (см. Следующий параграф). Однако проблема, описанная выше, одинакова, когда ViewModel извлекает модель, а ModelLocator нужен ключ для извлечения существующей модели, если она существует.

Это часть приложения VSTO, ориентированного на PowerPoint (что влияет на некоторые части дизайна). Когда выбран объект на экране, открывается панель задач (это в основном FillPropertiesView, описанное выше). Выбранный объект имеет ключ, который предоставляется в представлении, чтобы представление извлекало правильную модель представления из ViewModelLocator. Затем модель представления получит ссылку на модель, используя IModelLocator (аналогично IViewModelLocator) и тот же ключ. В то же время controller будет извлекать модель из ModelLocator, используя тот же ключ. Контроллер прослушивает изменение событий из модели и обновление объектов на экране. Тот же процесс реплицируется всякий раз, когда выбран новый объект, и одновременно может открываться несколько окон, которые взаимодействуют с одними и теми же или разными объектами одновременно (то есть с несколькими панелями задач с уникальными моделями просмотров).

До сих пор я разрешил представление с помощью конструктора без параметров без параметров и затем после этого ввел модель представления с вызовом метода:

 // Sets the view model on a view public void SetViewModel(IViewModelLocator vml, object key) { // Assign the view model _viewModel = vml.GetViewModel(key); DataContext = _viewModel; } 

Мне никогда не приходилось регистрировать представление в контейнере, но разрешил конкретный тип следующим образом:

 string key = "testkey" // read from the selected object var view = container.GetInstance(); var vml = container.GetInstance(); view.SetViewModel(vml, key); 

Эта проблема возникла, когда я попытался реорганизовать это, так что мне не нужно было каждый раз называть метод SetViewModel () и вручную разрешать модели просмотра и т. Д. Это было очень грязно, когда мне также приходилось делать это ручное инициирование в модели представления для инициирования модель аналогичным образом.


ViewModelLocator

ViewModelLocator в настоящее время работает как shell вокруг контейнера DI, то есть модели просмотра регистрируются в Simple Injector.

Регистрации выглядят следующим образом (в classе под названием CompositionHost):

 container.RegisterSingle(); container.RegisterSingle(); 

Реализация выглядит следующим образом:

 // Base implementation used by ViewModelLocator and ModelLocator public class ServiceLocator where TService : class { private readonly Dictionary _instances = new Dictionary(); // Gets a service instance based on the type and a key. // The key makes it possible to have multiple versions of the same service. public T GetInstance(object key = null) where T : class, TService { var combinedKey = new CombinedTypeKey(typeof(T), key); // Check if we already have an instance if (_instances.ContainsKey(combinedKey)) { return _instances[combinedKey] as T; } // Create a new instance // CompositionHost is a static reference to the DI container (and // should perhaps be injected, however, that is not the main issue here) var instance = CompositionHost.GetInstance(); _instances.Add(combinedKey, instance); return instance; } // A combined key to ease dictionary operations private struct CombinedTypeKey { private readonly object _key; private readonly Type _type; public CombinedTypeKey(Type type, object key) { _type = type; _key = key; } // Equals and GetHashCode() are overridden } } public class ViewModelLocator : IViewModelLocator { private readonly ServiceLocator _viewModelLocator; public ViewModelLocator(ServiceLocator locator) { _viewModelLocator = locator; // Dummy code that registers design time data is removed } // IViewModel is just an empty interface implemented by the view models public T GetViewModel(object key = null) where T : class, IViewModel { return _viewModelLocator.GetInstance(key); } } 

    Внедрение локатора сервисов в ваши classы (почти) никогда не будет таким путем, потому что это запрещает проверку времени зависимостей и анализ зависимостей времени выполнения . По этой причине я также могу посоветовать зарегистрировать ВСЕ ваши корневые типы (например, ваши представления), поскольку в противном случае Simple Injector остается в темноте и не может дать вам совет о любых возможных неправильных конфигурациях, которые могут возникнуть у вас.

    Поскольку у вас есть пары View + ViewModel, которые всегда кэшируются вместе, но могут зависеть от экземпляра модели, которые повторно используются несколькими парами View + ViewModel, я предлагаю следующий дизайн.

    Определите абстракцию для просмотров и моделей просмотра:

     public interface IView { IViewModel ViewModel { get; } } public interface IViewModel { TModel Model { get; set; } } 

    Определите абстракцию для получения / кэширования вида по ключу.

     public interface IViewProvider where TView : IView { TView GetViewByKey(object key); } 

    С этими абстракциями ваш взгляд может выглядеть следующим образом:

     public class FillPropertiesView : IView { public FillPropertiesView(FillPropertiesViewModel viewModel) { this.ViewModel = viewModel; } public IViewModel ViewModel { get; private set; } } 

    И ваши controllerы могут зависеть от IViewProvider чтобы они могли перезагрузить представление при IViewProvider нового ключа:

     public class FillPropertiesController : Controller { IViewProvider viewProvider; FillPropertiesView view; public FillPropertiesController( IViewProvider provider) { this.viewProvider = provider; } public void Reinitialize(object key) { this.view = this.viewProvider.GetViewByKey(key); } } 

    Реализация для IViewProvider может выглядеть так:

     public class ViewProvider : IViewProvider where TView : class, IView { Dictionary views = new Dictionary(); Container container; IModelProvider modelProvider; public ViewProvider(Container container, IModelProvider modelProvider) { this.container = container; this.modelProvider = modelProvider; } public TView GetViewByKey(object key) { TView view; if (!this.views.TryGetValue(key, out view)) { this.views[key] = view = this.CreateView(key); } return view; } private TView CreateView(object key) { TView view = this.container.GetInstance(); view.ViewModel.Model = this.modelProvider.GetModelByKey(key); return view; } } 

    Эта реализация зависит от абстракции (ранее не IModelProvider ) IModelProvider . Это в основном ваш старый ModelLocator , но, используя общий тип, вы можете сделать реализацию намного проще, потому что у нас может быть один экземпляр этого типа для TModel (то же самое относится к ViewProvider), что избавляет вас от необходимости делать что-то с сохранением элементов с комбинацией {Тип + ключ}.

    Вы можете зарегистрировать все это следующим образом:

     Assembly asm = Assembly.GetExecutingAssembly(); container.RegisterManyForOpenGeneric(typeof(IView<>), asm); container.RegisterManyForOpenGeneric(typeof(IViewModel<>), asm); container.RegisterOpenGeneric(typeof(IViewProvider<,>), typeof(ViewProvider<,>), Lifestyle.Singleton); container.RegisterOpenGeneric(typeof(IModelProvider<>), typeof(ModelProvider<>), Lifestyle.Singleton); var controllers = from type in asm.GetTypes() where type.IsSubClassOf(typeof(Controller)) where !type.IsAbstract select type; controllers.ToList().ForEach(t => container.Register(t)); container.Verify(); 

    С помощью RegisterManyForOpenGeneric вы позволяете Simple Injector искать поставляемые сборки для поиска реализаций данной открытой общей абстракции, а Simple Injector будет зарегистрировать их для вас. С RegisterOpenGeneric вы указываете открытую абстракцию и рассказываете Simple Injector, какую реализацию следует использовать при запросе близкой общей версии этой абстракции. Последняя строка ищет в вашем приложении, чтобы искать все типы controllerов и регистрирует их в системе.