【问题标题】:Service Locator confusion服务定位器混淆
【发布时间】:2011-03-29 20:47:50
【问题描述】:

我只是在编写一个实现 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”进行延迟初始化时,为什么要为它创建一个注册(T 服务)方法?那是双重工作。

4.) 我应该去找服务定位器吗?

更新

目前我觉得我必须为我的个人目的强行使用 DI 工具 =>

App.xaml.cs => 这里我创建了 MainWindow 并将其 datacontext 设置为 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 对象的可绑定集合,这些对象在它们的构造函数中接受一些服务。在以下代码之后的下一段中,请参阅由 ONE PeriodViewModel 创建的 DocumentListViewModel.cs,它再次接受服务 - 与我在 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 中的每个 DataRow 都有一个 Period 并且 Period 有一个特定的单元格数据模板化到 DocumentListViewModel - Period 1 有 N Documents 是关系仅供参考......所以 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 工具如何帮助我具体地按照 MVVM 模式开发 wpf 应用程序?

【问题讨论】:

标签: c# dependency-injection service-locator


【解决方案1】:

第四个问题最容易回答:不,你根本不应该使用服务定位器,因为it's an anti-pattern

那么有什么选择呢?使用Register Resolve Release pattern。这应该是回答您其他问题的一个很好的起点。

【讨论】:

  • @Mark 好的,我会阅读有关 R3 模式的信息,谢谢。我肯定会回来提问。完成...作为示例的控制台应用程序对我来说太简单了,无法将获得的知识转移到现实生活中的 LOB,您是否有进一步的链接/解释标记?
  • 是的,那个 RRR 帖子里有很多链接 :) 如果这还不够,我有一本关于 DI 的书:affiliate.manning.com/idevaffiliate.php?id=1150_236
  • @Mark 你对我更新的问题有答案吗?这将显示您对实际应用程序中使用的 DI 工具的真实了解。 :)
  • 不要把它看得太多了,但是如果我花时间深入研究,你提供的详细程度将接近咨询:) 但是,我略读了它并您的问题似乎与嵌套服务有关。我将发布一些可能对您有帮助的相关问题的链接。
  • 哦,我的书的示例代码有一个完全工作的 WPF 示例,它一直使用 DI :)
【解决方案2】:
  1. 只要您需要服务T 的实例,就可以调用它。您需要更健壮的代码来处理您没有任何逻辑可处理的情况,其中T 未知或无法由您的服务定位器处理。

  2. 这因应用程序而异,但最常见的是,服务注册发生在应用程序入口点,例如在 Windows 应用程序中,在加载表单之前,在 ASP.NET 应用程序中,在Application_Start 方法中,在服务中,在加载服务Main 方法时。这是您必须根据需要进行的特定于应用程序的调用。请注意,这通常是一次一次性调用。

  3. 如果您想公开延迟初始化,那么您应该有两种注册方法,一种将采用T 的实例(如果您想始终使用该实例),另一种采用@ 987654331@,可以在需要实例时调用(然后缓存,如果需要)。

  4. 1234563 987654322@、Unity 或任何其他 DI 工具。

【讨论】:

  • @casperOne 所以我猜 ServiceLocator 总是静态类?您的链接 commonservicelocator.codeplex.com 与您提到的 lightcore.ch/en/default.aspx 或 Unity 等典型 DI 工具有什么区别?
  • @msfanboy:它并不总是必须是静态的,实际上,将其设置为静态会使它无法拥有一个单一的位置(而不是针对不同的上下文使用不同的解析器,这是一个可行的设置)。关于 Common Service Locator,它实际上不做依赖注入,它只是提供了一个抽象,其他 DI 工具可以插入;这样,如果您决定更改 DI 工具,您只需更改初始化 Common Service Locator 的代码,您不必更改所有使用 Common Service Locator 的调用。
  • @msfanboy:也就是说,您想将 Lightcore 与 Unity、Ninject、Castle Windsor 等进行比较,但您想使用 Common Service Locator(您没有到,但抽象级别很好,除非您需要一些非常特定于这些工具之一的东西)来公开您选择的任何一个(根据您的需要)。
  • @msfanboy:另外,@Mark Seemann 说服务位置是一种反模式是对的;如果您正在调用服务定位器,那么您可能做错了什么。只有在最高级别,您才可以调用某种服务定位器,在此之下,DI 和 IOC 应该接管,或者更好的是,3R 应该进来(因为他是提到)。
  • @CasperOne 可能现在的问题是“我需要什么” => 我需要一些可以让我获得接口实例的东西,我想在我的代码中的任何地方都调用这个“get” - 是吗顺便说一句不好?-
猜你喜欢
  • 2015-11-03
  • 1970-01-01
  • 2014-07-15
  • 1970-01-01
  • 2021-11-08
  • 2016-06-27
  • 1970-01-01
  • 1970-01-01
  • 2016-04-12
相关资源
最近更新 更多