3

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?

msfanboy
  • 5,123
  • 12
  • 65
  • 116
  • 1
    Kind of curious why you would do this yourself when it's [already done for you](http://commonservicelocator.codeplex.com/)? – casperOne Mar 29 '11 at 20:49

2 Answers2

5

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.

Mark Seemann
  • 209,566
  • 41
  • 390
  • 671
  • @Mark ok I will read about the R3 pattern, thank you. Surely I will return for questions. Done... a console app as sample is too simple for me to transfer the gained knowledge into a real life LOB, do you have further links/explanation Mark? – msfanboy Mar 29 '11 at 21:19
  • 1
    Yeah, that RRR post is full of links :) If that's not enough, I have a book about DI: http://affiliate.manning.com/idevaffiliate.php?id=1150_236 – Mark Seemann Mar 29 '11 at 21:29
  • @Mark do you have an answer for my updated question? This would show your real knowledge about DI tools used in real life apps. :) – msfanboy Mar 31 '11 at 19:08
  • 1
    Don't take this for more than it is, but with the level of detail you provide it would approach consulting if I were to take the time and dig into that :) However, I skimmed it and it seems that your issue is with nested services. I'll be posting some links to related questions that might be of help to you. – Mark Seemann Mar 31 '11 at 19:15
  • http://stackoverflow.com/questions/2420193/dependency-injection-constructor-madness – Mark Seemann Mar 31 '11 at 19:16
  • http://stackoverflow.com/questions/4570750/dependency-injection-turtles-all-the-way-down – Mark Seemann Mar 31 '11 at 19:16
  • http://stackoverflow.com/questions/2956027/how-to-build-a-generic-re-usable-modal-dialog-for-wpf-following-mvvm – Mark Seemann Mar 31 '11 at 19:17
  • http://stackoverflow.com/questions/2232365/unity-wpf-injecting-a-datacontext-via-property-injection-to-a-child-control – Mark Seemann Mar 31 '11 at 19:17
  • 1
    Oh, and the sample code for my book has a fully working WPF sample that uses DI all the way :) – Mark Seemann Mar 31 '11 at 19:18
  • @Mark Yes the term "nested services" describes my problem. My problem is to find out who is responsible for what and when. I will check your books source code, thank you I return here then. – msfanboy Mar 31 '11 at 21:19
  • @Mark Now I have such a pro DI guy here I want to ask you a question. When you say the ServiceLocator is bad because there is a dependency on it and we should all use DI tools, well in the end we have always a dependency to the DI tool. So what is better and why? That sounds like a religious question to me. Maybe its also because I have too less experience to understand all that properly, but I am really eager to learn and try it all out. – msfanboy Mar 31 '11 at 21:22
  • No, I'm not saying that Service Locator is bad because of the dependency. *This* is why it's an anti-pattern: http://blog.ploeh.dk/2010/02/03/ServiceLocatorIsAnAntiPattern.aspx Keeping the container constrained to the Composition Root addresses the issue. The rest of your code must not reference the container. – Mark Seemann Apr 01 '11 at 04:58
  • @Mark Maybe my approach of nested services is a violation of SRP, BUT doing it my way I have a total resuable and maintainable DocumentListViewModel for many parts in my application. If I would do the open,add,del methods for my documents not in the DocumentListViewModel but with some action events firing to the LessonPlannerDailyViewModel acting as a Controller I could not reuse that add/del/open etc methods at all in other parts of my application. So what weights more SRP or reuseability? If I would really know there is a solution to my specific problem, I would buy your book right now. – msfanboy Apr 01 '11 at 14:46
  • There's no conflict between SRP and reusability. You can have both. – Mark Seemann Apr 01 '11 at 15:56
  • @Mark Googling a lot it seems to me that Injecting services into ViewModels happens only when the ViewModel is mapped to a View. All www samples show it that way. But my ViewModel aka PeriodViewModel is not mapped to a PeriodView its just a wrapper around a model. For my situation there exist no solution from the DI area. What I have realized now is that I should NOT pass all services my app has into the MainViewModel. – msfanboy Apr 01 '11 at 20:07
  • Just "inject" the mainService into the MainViewModel OR just "inject"/pass the lessonPlannerService into theLessonPlanner-DailyViewModel OR just pass the documentService to the Ctor of the DocumentListViewModel. That way the parameters are reduced and I have less nested services. I will give you a point for your answers some of them were helpfull as an answer to my original 4 questions casperOne is blessed :) – msfanboy Apr 01 '11 at 20:20
1
  1. 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 where T is unknown or unable to be handled by your service locator.

  2. 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 theApplication_Start method, in services, when the service Main 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.

  3. 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 a Func<T>, which can be called when the instance is needed (and then cached, if desired).

  4. 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.

casperOne
  • 70,959
  • 17
  • 175
  • 239
  • @casperOne so a ServiceLocator is always static class I guess? What is the difference between your link http://commonservicelocator.codeplex.com/ and a typical DI tool like http://lightcore.ch/en/default.aspx or Unity you mentioned ? – msfanboy Mar 29 '11 at 21:01
  • @msfanboy: It doesn't always have to be static, actually, making it static would fix it too much to having a single location (as opposed to having different resolvers for different contexts, which is a viable setup). In regards to Common Service Locator, it doesn't *actually* do dependency injection, it just provides an abstraction that other DI tools can plug into; this way, if you decide to change DI tools, you only have to change the code that initializes the Common Service Locator, you don't have to change *all* the calls that use the Common Service Locator. – casperOne Mar 29 '11 at 21:06
  • @msfanboy: That said, you want to compare Lightcore to Unity, Ninject, Castle Windsor, etc, but you want to use Common Service Locator (you don't *have* to, but the level of abstraction is nice, unless you need something very specific to one of these tools) to expose whichever one you choose (based on your needs). – casperOne Mar 29 '11 at 21:07
  • @msfanboy: Also, @Mark Seemann is right when he says that service location is an anti-pattern; if you are making calls to a service locator, then you are probably doing something wrong. It should only be at the *very top level* that you are making calls to a service locator of some kind, beneath that, DI and IOC should take over, or even better, 3R should come in (as he's mentioned). – casperOne Mar 29 '11 at 21:32
  • @CasperOne probably the question is now "What do I need" => I need something that gets me an instance of an interface and I want to call this "get" everywhere in my code - is that bad by the way ?- – msfanboy Mar 29 '11 at 21:48
  • @msfanboy: Well, the 3R method that is pointed out by @Mark Seemann is really the way to go. It's one thing to need an implementation of an interface at the top level of your code, however, you should *never* make calls within your interface implementations to the service locator for services, they should be injected through the constructor, preferably. – casperOne Mar 29 '11 at 22:19
  • @casperOne I really need to get a grasp of the DI stuff with my individual project. Therefore I have updated my init question with a sample code which is very individually therefore I need a really individual answer. Thanks for helping! – msfanboy Mar 30 '11 at 19:26