Путаница с Service Locator

Я просто пишу класс, реализующий шаблон ServiceLocator.

   public class ServiceFactory : IServiceFactory
    {
        private IDictionary<Type, object> instantiatedServices;

        public ServiceFactory()
        {
            instantiatedServices = new Dictionary<Type, object>();
        }

        public T GetService<T>() where T : class, new()
        {
            if (this.instantiatedServices.ContainsKey(typeof(T)))
            {
                return (T)this.instantiatedServices[typeof(T)];
            }
            else
            {
                T service = new T();

                instantiatedServices.Add(typeof(T), service);

                return service;
            }
        }
    }

Теперь у меня несколько вопросов:

1.) Откуда мне называть этот класс? app.xaml.cs делает материал wpf?

2.) Следует ли мне регистрировать услуги, если да, где мне это делать?

3.) Когда я выполняю ленивую инициализацию службы "ICustomerService", зачем тогда мне создавать для нее метод Register (T service)? это двойная работа.

4.) Стоит ли мне вообще искать сервисный локатор?

ОБНОВЛЕНИЕ

На данный момент я чувствую, что должен использовать инструмент DI для моих личных целей, а именно =>

App.xaml.cs => Здесь я создаю MainWindow и устанавливаю для него текст данных в MainViewModel.cs.

public partial class App : Application
    {
        protected override void OnStartup(StartupEventArgs e)
        {
            var mainVM = new MainViewModel();
            var mainWindow = new MainWindow();
            mainWindow.DataContext = mainVM;
            mainWindow.ShowDialog();            
        }        
    }

MainViewModel.cs => Здесь я предварительно загружаю / настраиваю данные, которые мне нужны для определенных Controller / ViewModel, таких как LessonPlannerDailyViewModel или LessonPlannerWeeklyViewModel и т. д.

public class MainViewModel : SuperViewModel
{     

        private LightCommand _newSchoolYearWizardCommand;       
        private LightCommand _showSchoolclassAdministrationCommand;
        private LightCommand _showLessonPlannerDailyCommand;
        private LightCommand _showLessonPlannerWeeklyCommand;  
        private LightCommand _openSchoolYearWizardCommand;

        private SuperViewModel _vm;
        private FadeTransition _fe = new FadeTransition();

        private readonly IMainRepository _mainService;
        private readonly ILessonPlannerService _lessonPlannerService;
        private readonly IAdminService _adminService;
        private readonly IDocumentService _documentService;     
        private readonly IMediator _mediator;

        private readonly  IDailyPlanner _dailyVM;
        private readonly  IWeeklyPlanner _weeklyVM;
        private SchoolclassAdministrationViewModel _saVM;    


        public MainViewModel()  
        {

            // These are a couple of services I create here because I need them in MainViewModel

            _mediator = new  Mediator();            
            _mainService = new MainRepository();
            _lessonPlannerService = new LessonPlannerService();
            _adminService = new AdminService();
            _documentService = new DocumentService();    

            this._mediator.Register(this);  

            InitSchoolclassAdministration();                     
        } 

        //... Create other ViewModel/Controller via button commands and their execute method
}  

Одной из других ViewModel является LessonPlannerDailyViewModel.cs => Здесь я создаю привязываемую коллекцию объектов PeriodViewModel, которые принимают в своем конструкторе некоторые службы. В следующем абзаце после следующего кода см. DocumentListViewModel.cs, созданный ONE PeriodViewModel, который снова принимает службы - те же, что я создал в MainViewModel ... -

 public class LessonPlannerDailyViewModel : LessonPlannerBaseViewModel, IDailyPlanner
    {    
        private ILessonPlannerService _lpRepo;
        private IMainRepository _mainRepo;
        private IMediator _mediator;
        private IDocumentService _docRepo;

        private ObservableCollection<PeriodViewModel> _periodListViewModel;      

        private LightCommand _firstDateCommand;
        private LightCommand _lastDateCommand;
        private LightCommand _nextDateCommand;
        private LightCommand _previousDateCommand;  

        public LessonPlannerDailyViewModel(IMediator mediator, ILessonPlannerService lpRepo, IMainRepository mainRepo, IDocumentService docRepo)
        {
            _mediator = mediator;
            _lpRepo = lpRepo;
            _mainRepo = mainRepo;
            _docRepo = docRepo;

            _mediator.Register(this);

            SchoolYear schoolyear = _mainRepo.GetSchoolYear();

            MinDate = schoolyear.Start;
            MaxDate = schoolyear.End;         

            SelectedDate = DateTime.Now; 
        } 

        private void LoadLessonPlannerByDay(DateTime data)
        {
            _periodListViewModel = new ObservableCollection<PeriodViewModel>();

            _lpRepo.GetLessonPlannerByDay(data).ForEach(p =>
            {
                _periodListViewModel.Add(new PeriodViewModel(p, _lpRepo, _docRepo));
            });          

            PeriodListViewModel = _periodListViewModel;        
        } 

        private DateTime _selectedDate;
        public DateTime SelectedDate
        {
            get { return _selectedDate; }
            set
            {
                if (_selectedDate.Date == value.Date)
                    return;

                _selectedDate = value;
                this.RaisePropertyChanged("SelectedDate");

                LoadLessonPlannerByDay( value );
            }
        }

       // ...

}

PeriodViewModel.cs => Каждая строка данных в моем DataGrid имеет период, а период имеет определенную ячейку данных, привязанную к DocumentListViewModel - период 1 имеет N документов, это отношение FYI ... поэтому PeriodViewModel создает DocumentListViewModel .

public class PeriodViewModel : SuperViewModel
    {  
        private Period _period;
        private ILessonPlannerService _lpRepo;

        public PeriodViewModel(Period period, ILessonPlannerService lpRepo, IDocumentService docRepo)
        {            
            _period = period;
            _lpRepo = lpRepo;

            // Update properties to database
            this.PropertyChanged += (o, e) =>
            {
                switch (e.PropertyName)
                {
                    case "Homework": _lpRepo.UpdateHomeWork(PeriodNumber, LessonDayDate, Homework); break;
                    case "Content": _lpRepo.UpdateContent(PeriodNumber, LessonDayDate, Content); break;
                }
            };

            Documents = new DocumentListViewModel(_period.Id, period.Documents, docRepo); 
        }
   //...
}

DocumentListViewModel.cs => Здесь я настраиваю команды для добавления / удаления / открытия документа, и это можно сделать с помощью documentService / documentRepository

public class DocumentListViewModel : SuperViewModel
    {
        private LightCommand _deleteDocumentCommand;
        private LightCommand _addDocumentCommand;
        private LightCommand _openDocumentCommand;      

        private int _parentId;

        private readonly IDocumentService _documentService;       

        public DocumentListViewModel(int parentId,ObservableCollection<Document> documents, IDocumentService documentService)
        {
            _parentId = parentId;
            _documentService = documentService;
            DocumentList = documents;
            SelectedDocuments = new ObservableCollection<Document>();
        } 

        // ...
} 

Подводя итог проблеме: видите ли вы цепочку объектов, каскадирующих сервисы сверху:

MainViewodel -> LessonPlannerDailyViewModel -> PeriodViewModel -> DocumentListViewModel

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

Как инструмент DI может помочь мне КОНКРЕТНО создавать приложение wpf по шаблону MVVM?


person msfanboy    schedule 29.03.2011    source источник
comment
Довольно любопытно, почему вы сделали это самостоятельно, когда уже сделали для вас?   -  person casperOne    schedule 30.03.2011


Ответы (2)


  1. Вы должны вызывать это всякий раз, когда вам нужен экземпляр службы T. Вам понадобится более надежный код для обработки случаев, когда у вас нет никакой логики для обработки, когда T неизвестен или не может быть обработан вашим локатором служб.

  2. Это варьируется от приложения к приложению, но чаще всего регистрация служб происходит в точке входа в приложение, например в приложениях Windows до загрузки формы, в приложениях ASP.NET в методе Application_Start, в службах, когда загружается метод Main службы. Это вызов для конкретного приложения, который вы должны делать в зависимости от ваших потребностей. Обратите внимание, что обычно это разовый вызов.

  3. Если вы хотите предоставить ленивую инициализацию, тогда у вас должно быть два метода регистрации, один из которых будет принимать экземпляр T (если вы хотите всегда использовать этот один экземпляр), или другой, который принимает Func<T>, который может быть вызван, когда экземпляр требуется (а затем, при желании, кешируется).

  4. Если под этим вы имеете в виду написать его самостоятельно, то я должен категорически сказать нет, это уже было сделано для вас и если вам не нравится такой уровень детализации, ничто не мешает вам использовать такие инструменты, как Ninject, Unity или любой другой доступный инструмент DI.

person casperOne    schedule 29.03.2011
comment
@casperOne, значит, ServiceLocator всегда статический класс, я думаю? В чем разница между вашей ссылкой commonservicelocator.codeplex.com и типичным инструментом DI, например lightcore.ch/en/default.aspx или Unity, о котором вы упомянули? - person msfanboy; 30.03.2011
comment
@msfanboy: Он не всегда должен быть статичным, на самом деле, если сделать его статичным, это слишком сильно исправит наличие одного местоположения (в отличие от наличия разных преобразователей для разных контекстов, что является жизнеспособной настройкой). Что касается Common Service Locator, он фактически не выполняет внедрение зависимостей, он просто предоставляет абстракцию, к которой могут подключаться другие инструменты DI; Таким образом, если вы решите изменить инструменты DI, вам нужно будет изменить только код, который инициализирует локатор общих служб, вам не нужно изменять все вызовы, которые используют локатор общих служб. - person casperOne; 30.03.2011
comment
@msfanboy: Тем не менее, вы хотите сравнить Lightcore с Unity, Ninject, Castle Windsor и т. д., но вы хотите использовать Common Service Locator (у вас его нет есть, но уровень абстракции хорошо, если вам не нужно что-то очень специфичное для одного из этих инструментов), чтобы открыть тот, который вы выберете (в зависимости от ваших потребностей). - person casperOne; 30.03.2011
comment
@msfanboy: Кроме того, @Mark Seemann прав, когда говорит, что местоположение службы - это антипаттерн; если вы звоните в службу поиска, то, вероятно, вы делаете что-то не так. Только на самом верхнем уровне вы должны звонить в какой-либо локатор служб, ниже этого должны действовать DI и IOC, или, что еще лучше, должен войти 3R (поскольку он упомянул). - person casperOne; 30.03.2011
comment
@CasperOne, вероятно, теперь вопрос в том, что мне нужно = ›Мне нужно что-то, что дает мне экземпляр интерфейса, и я хочу называть это get везде в моем коде - это, кстати, плохо? - - person msfanboy; 30.03.2011
comment
@msfanboy: Что ж, метод 3R, на который указал @Mark Seemann, действительно правильный путь. Одно дело - нуждаться в реализации интерфейса на верхнем уровне вашего кода, однако вы должны никогда не делать в рамках своих реализаций интерфейса вызовов локатора сервисов для сервисов, они должны вводиться через конструктор. , желательно. - person casperOne; 30.03.2011
comment
@casperOne Мне действительно нужно разобраться в DI в моем индивидуальном проекте. Поэтому я обновил свой вопрос инициализации образцом кода, который очень индивидуален, поэтому мне нужен действительно индивидуальный ответ. Спасибо за помощь! - person msfanboy; 30.03.2011

На четвертый вопрос проще всего ответить: нет, вам вообще не следует использовать Service Locator, потому что это антипаттерн.

Так какая же альтернатива? Используйте шаблон разрешения разрешения на выпуск зарегистрируйтесь. Это должно быть хорошей отправной точкой для ответа на другие ваши вопросы.

person Mark Seemann    schedule 29.03.2011
comment
@Mark хорошо, я прочитаю о шаблоне R3, спасибо. Обязательно вернусь, чтобы задать вопросы. Готово ... консольное приложение в качестве образца слишком просто для меня, чтобы передать полученные знания в реальный бизнес-объект, у вас есть дополнительные ссылки / пояснения, Марк? - person msfanboy; 30.03.2011
comment
Да, этот пост RRR полон ссылок :) Если этого недостаточно, у меня есть книга о DI: affiliate.manning.com/idevaffiliate.php?id=1150_236 - person Mark Seemann; 30.03.2011
comment
@Mark, у вас есть ответ на мой обновленный вопрос? Это покажет ваши реальные знания об инструментах DI, используемых в реальных приложениях. :) - person msfanboy; 31.03.2011
comment
Не воспринимайте это больше, чем есть, но с уровнем детализации, который вы предоставляете, он подошел бы к консультациям, если бы я нашел время и углубился в это :) Однако я просмотрел его, и кажется, что ваша проблема связана с вложенными Сервисы. Я буду размещать ссылки на связанные вопросы, которые могут быть вам полезны. - person Mark Seemann; 31.03.2011
comment
stackoverflow.com/questions/2420193/ - person Mark Seemann; 31.03.2011
comment
stackoverflow.com/questions/4570750 / - person Mark Seemann; 31.03.2011
comment
stackoverflow.com/questions/2956027/ - person Mark Seemann; 31.03.2011
comment
stackoverflow.com/questions/2232365/ - person Mark Seemann; 31.03.2011
comment
Да, и в образце кода для моей книги есть полностью рабочий образец WPF, который полностью использует DI :) - person Mark Seemann; 31.03.2011
comment
@Mark Да, термин вложенные службы описывает мою проблему. Моя задача - выяснить, кто за что и когда отвечает. Я проверю исходный код ваших книг, спасибо, тогда я вернусь сюда. - person msfanboy; 01.04.2011
comment
@Mark.Теперь у меня есть такой профессиональный DI-парень, я хочу задать вам вопрос. Когда вы говорите, что ServiceLocator плохой, потому что от него есть зависимость, и мы все должны использовать инструменты DI, в конце концов, у нас всегда есть зависимость от инструмента DI. Так что лучше и почему? Для меня это звучит как религиозный вопрос. Может быть, это еще и потому, что у меня слишком мало опыта, чтобы понять все это должным образом, но я действительно хочу научиться и все это попробовать. - person msfanboy; 01.04.2011
comment
Нет, я не говорю, что Service Locator плох из-за зависимости. Это, почему это анти-шаблон: блог. ploeh.dk/2010/02/03/ServiceLocatorIsAnAntiPattern.aspx Сохранение контейнера, привязанного к корню композиции, решает проблему. Остальная часть вашего кода не должна ссылаться на контейнер. - person Mark Seemann; 01.04.2011
comment
@Mark Может быть, мой подход к вложенным сервисам является нарушением SRP, НО, делая это по-своему, у меня есть полностью восстанавливаемая и поддерживаемая DocumentListViewModel для многих частей моего приложения. Если бы я использовал методы open, add, del для своих документов не в DocumentListViewModel, а с некоторыми событиями действия, запускаемыми в LessonPlannerDailyViewModel, действующем как Контроллер, я не мог бы повторно использовать эти методы add / del / open и т. Д. Вообще в других частях моего применение. Так что же важнее для SRP или повторного использования? Если бы я действительно знал, что есть решение моей конкретной проблемы, я бы купил вашу книгу прямо сейчас. - person msfanboy; 01.04.2011
comment
Нет конфликта между SRP и возможностью повторного использования. Вы можете получить и то, и другое. - person Mark Seemann; 01.04.2011
comment
@Mark Googling много, мне кажется, что внедрение сервисов в ViewModels происходит только тогда, когда ViewModel сопоставляется с View. Все образцы www показывают это именно так. Но моя ViewModel, также известная как PeriodViewModel, не отображается на PeriodView, это просто оболочка вокруг модели. Для моей ситуации не существует решения из области DI. Теперь я понял, что НЕ должен передавать все сервисы моего приложения в MainViewModel. - person msfanboy; 02.04.2011
comment
Просто вставьте mainService в MainViewModel ИЛИ просто введите / передайте lessonPlannerService вLessonPlanner-DailyViewModel ИЛИ просто передайте documentService в Ctor DocumentListViewModel. Таким образом параметры уменьшаются, и у меня меньше вложенных сервисов. Я дам вам балл за ваши ответы, некоторые из них были полезны в качестве ответа на мои исходные 4 вопроса, CasperOne благословил :) - person msfanboy; 02.04.2011