服务定位器混乱
我只是编写一个实现 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 =>在这里,我预加载/设置某些控制器/视图模型(例如 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个文档是关系仅供参考...所以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 ->周期视图模型 -> DocumentListViewModel
我需要级联它们,因为如果我不使用静态服务定位器,我只能确保在级联服务时拥有一个服务实例...
这里的 DI 工具如何帮助我具体< /strong> 遵循 MVVM 模式开发 wpf 应用程序?
I am just writing a class implementing the ServiceLocator pattern.
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;
}
}
}
Now I have several questions:
1.) Where should I call this class from? the app.xaml.cs doing wpf stuff?
2.) Should I register services , if yes where should I do that?
3.) When I do lazy Initialization of the service "ICustomerService" why then should I create a Register(T service)-method for it? thats double work.
4.) Should I go at all for a service locator?
UPDATE
At the moment I feel I have to rape a DI tool for my individual purposes which are =>
App.xaml.cs => Here I create the MainWindow and set its datacontext to the 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 => Here I preload/setup data which I need for certain Controller/ViewModel`s like the LessonPlannerDailyViewModel or LessonPlannerWeeklyViewModel etc...
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
}
On of the other ViewModel is the LessonPlannerDailyViewModel.cs => Here I create a bindable collection of PeriodViewModel objects which take in their constructor some services. At next paragraph after the following code see the DocumentListViewModel.cs created by ONE PeriodViewModel which takes again services - the same that I created in the 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 => Every DataRow in my DataGrid has a Period and a Period has a certain cell datatemplated to the DocumentListViewModel - Period 1 has N Documents is the relation FYI... so a PeriodViewModel creates a 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 => Here I setup the commands for add/delete/open a document and this can be done with the 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>();
}
// ...
}
To sum the problem up: Do you see the chain of objects cascading the services from top:
MainViewodel -> LessonPlannerDailyViewModel -> PeriodViewModel -> DocumentListViewModel
I need to cascade them because if I am not using a static service locator I can only make sure to have ONE instance of a service when I am cascading the services...
How can a DI tool here help me CONCRETELY doing a wpf app following the MVVM pattern?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
第四个问题是最容易回答的:不,您根本不应该使用服务定位器,因为这是一种反模式。
那么还有什么选择呢?使用注册解决发布模式。这应该是回答您其他问题的一个很好的起点。
The fourth question is the easiest to answer: no, you shouldn't go for Service Locator at all because it's an anti-pattern.
So what is the alternative? Use the Register Resolve Release pattern. That should be a good starting point to answer your other questions.
只要您需要服务
T
的实例,您就可以调用它。您需要更强大的代码来处理您没有任何逻辑来处理T
未知或无法由服务定位器处理的情况。这因应用程序而异,但最常见的是,服务注册发生在应用程序入口点,例如在 Windows 应用程序中,在加载表单之前,在 ASP.NET 应用程序中,在
Application_Start
方法,在服务中,当加载服务Main
方法时。这是一个特定于应用程序的调用,您必须根据您的需要进行调用。请注意,它通常是一次性调用。如果您想公开延迟初始化,那么您应该有两种注册方法,一种将采用
T
的实例(如果您想始终使用该实例),另一种采用aFunc
,可以在需要实例时调用它(然后根据需要进行缓存)。如果您的意思是自己写一个,那么我必须强调说不,它已经为您完成了 如果您不喜欢这种粒度级别,没有什么可以阻止您使用 Ninject、< a href="http://unity.codeplex.com/" rel="nofollow">Unity 或任何其他 DI 工具。
You would call this whenever you need an instance of a service
T
. You would need more robust code to handle the cases where you don't have any logic to handle whereT
is unknown or unable to be handled by your service locator.This varies from application to application, but most typically, registration of services happens at the application entry point, e.g. in Windows applications, before the form is loaded, in ASP.NET applications, in the
Application_Start
method, in services, when the serviceMain
method is loaded. This is an application-specific call that you have to make depending on your needs. Note it is typically a one-time call.If you want to expose lazy initialization, then you should have two registration methods, one which will take an instance of
T
(if you want to always use that one instance), or one that takes aFunc<T>
, which can be called when the instance is needed (and then cached, if desired).If by this you mean write one yourself, then I would have to emphatically say no, it's already been done for you and if you don't like that level of granularity, there is nothing stopping you from using tools like Ninject, Unity or any of the other DI tools out there.