4

I have some problem with understanding how to create injectable classes…

Here is my example:

public interface IService
{
  string FindSomeData()
}

Now we create a class which implements the interface:

public class FolderService : IService
{
  private string _path;

  public FolderService(string path)
  {
    _path = path;
  }

  public string FindSomeData()
  {
    //Open a folder using _path and find some data
  }
}

And maybe other class:

public class DbService : IService
    {
      private MyConnectionClass _connection;

      public DbService(MyConnectionClass connection)
      {
        _connection = connection;
      }

      public string FindSomeData()
      {
        //Connect to database using _connection object and find some data
      }
    }

Now I would like to add one of the classes to IoC Container e.x.:

if (InDebug)
 SimpleIoc.Default.Register<IService, FolderService>();
else
 SimpleIoc.Default.Register<IService, DbService>();

And know I have a problems. When I want to pass this object to the constructor of some other classes:

public MyViewModel(IService service)
{
 _service = service;
}
// Read folder name from TextBox on View and then call _service.FindSomeData

Then I would like to pass user selected path to the IService object (FolderService) in this case. How should I do this in a correct way (according to SOLID and other good practiciess patterns…)?

Once I should pass string (folder path), once a MyConnectionClass (if connection to database). What is the best way to do that kind of things?

Best regards, Michal

Roofy
  • 89
  • 2
  • 9
  • 1
    Possible duplicate of [MVVM SimpleIoc, how to use an interface when the interface implementation requires construction parameters](https://stackoverflow.com/questions/8850106/mvvm-simpleioc-how-to-use-an-interface-when-the-interface-implementation-requir) – Yannick Meeus Jul 20 '18 at 11:38
  • Thank you @YannickMeeus but this is not what I asked for - I know how to register class with parametrized constructor. The problem is different. I asked is it good practise to write classes like that and pass them to the IoC Container. The IService shouldnt know from which datastore it will download the data - this functionality I have moved to the derrivered classes. The problem is when the user change (e.x. folder path) then I should instantiate the IService object again with changed constructor parameter... – Roofy Jul 20 '18 at 11:45
  • Then it is off-topic ;) .. how about a config file containing that path? – nilsK Jul 20 '18 at 11:46
  • @nilsK the path or connection string can change during the application is working :/ – Roofy Jul 20 '18 at 11:47
  • @Roofy So you have different db-contexts? You could write a generic/base context and derive from it. AContext : BaseContext. That way you would need a connection string for every context of course and load it in derived context constructors. – nilsK Jul 20 '18 at 11:51
  • Maybe I can describe it in a different way: I have a two versions of application: one is using database and one is using folders to store the data. Depends on which Congfiguration I choose (VS ConfigurationManager: FOLDERAPP/DBAPP) I am asking the user to select folder path or db connection string, port and so one. So maybe I should use some Factory to create a classes instead of passing them through the parameter as a Interface? – Roofy Jul 20 '18 at 11:54
  • This is easy solution, but the question is: is it correct? I would like to learn creating software using SOLID principles and good programming practicies :) – Roofy Jul 20 '18 at 11:59

1 Answers1

3

You can encapsulate folder path provide/change logic into a separate provider like IFolderPathProvider and inject it into FolderService

public interface IFolderPathProvider {
    string GetFolderPath();
    void SetFolderPath(string);
}

public class FolderPathProvider : IFolderPathProvider  {
    ...
}

public class FolderService : IService
{
  private IFolderPathProvider _folderPathProvider;

  public FolderService(IFolderPathProvider folderPathProvider)
  {
    _folderPathProvider = folderPathProvider;
  }

  public string FindSomeData()
  {
    string path = _folderPathProvider.GetFolderPath();
    //Open a folder using path and find some data
  }
}

When user changes the path, inject IFolderPathProvider to the handler and call SetFolderPath. Similarly, you can create IDbConnectionProvider. Depending on the situation, they can be combined into one DataConfigProvider but I 'm not sure what exactly do you need there; the main idea is to separate folderpath/dbconnection changing logic from the services and keep using dependency injection.

Bagdan Gilevich
  • 1,231
  • 1
  • 11
  • 17
  • Bagdan Gilevich thank you very much. This is very good idea. Thats exacly what I need - I wanted to separate logic of IService from how the data is looking for and make it possible to DI. I think your idea is very good solution. Thank you guys for all of the answers. Best regards, Michał – Roofy Jul 21 '18 at 08:12