1

Lets assume we have the following service class:

PersonService.cs

public class PersonService : IPersonService
{
    private readonly IPersonRepository _repository;

    public PersonService(IPersonRepository repository)
    {
        _repository = repository;
    }

    /* Interface members here... */
}

This service class depends on IPersonRepository. In our example this repository will handle a file connection:

FilePersonRepository.cs

public class FilePersonRepository : IPersonRepository
{
    private readonly string _filePath;

    public FilePersonRepository (string filePath)
    {
        _filePath = filePath;
    }

    /* Interface members here... */
}

This repository depends on a file path, which is needed to determine which file should be handled in this repository.

These classes are wired in a container:

public static void ConfigureContainer(MyIocContainer container)
{
    container.Register<IPersonRepository, FilePersonRepository>(@"C:\temp\file.txt");
    container.Register<IPersonService, PersonService>();
    container.Register<MainViewModel>();
}

Now, everything is fine if i use a static path (for example in a config file), which is injected only once into this class constructor.

But what happens if i have a ViewModel, which persists along the whole application, and this one is using the registered service class. And the file path must be changebale by an open-folder dialog. How to handle this?

I think using a Property with get/set in my repository or create a class which refers the path on a static string may not so clean.

Any suggestions?

senz
  • 879
  • 10
  • 25
  • 1
    Create and register a Factory that returns an IPersonRepository and has a Create method that accepts a path? – Peter Bons Oct 13 '17 at 08:45
  • 1
    Runtime data such as this should be passed in as parameters of methods, not through the constructor. – NightOwl888 Oct 13 '17 at 08:47
  • But if i had a `SqlPersonRepository` too, which also implements `IPersonRepository` (to make this service flexible) the whole signature will be messed up. – senz Oct 13 '17 at 08:50
  • You'd need to wrap/abstract away the `filePath` into its own type, e.g., have it as a `IRepositoryLocator` or some such. That way whether it's a file source or a DB source, you have a way of implementing either without baking into the repository – DiskJunky Oct 13 '17 at 09:22
  • Seems like you trying to fit two different scenarios under one interface. 1. User select file which will be used for loading person data. 2. User ask to load person data from database without extra dialogs. That means - consumer of Repository should know about actual implementation – Fabio Oct 13 '17 at 09:23
  • Continuing idea of @DiskJunky - consider filepath as connection string for `Sql` implementation – Fabio Oct 13 '17 at 09:24
  • @DiskJunky yea, but the problem to change this on runtime still exists – senz Oct 13 '17 at 09:47
  • 1
    Once abstracted, you'd have a factory class that outputs the correct implementation based on a config file setting, say – DiskJunky Oct 13 '17 at 10:10
  • 1
    Have a look at [Factory method with DI and Ioc](https://stackoverflow.com/a/31971691/). Strategy pattern may work for this scenario, but only if you are sure your file paths will never need to change at runtime. – NightOwl888 Oct 13 '17 at 11:28

1 Answers1

1

There are many solutions how to solve your problem and it is impossible to say which one is the best. But I am quite sure that best practice is to avoid dynamic dependencies.

I personally would do it this way:

public interface IRepositoryConnectionString
{
    void SetConnectionString(string connectionString);
}

public interface IPersonRepository : IRepositoryConnectionString ...

public class FilePersonRepository : IPersonRepository
{
    /* file related private fields here */

    public FilePersonRepository ()
    {
        SetConnectionString(ConfigurationManager.ConnectionStrings[
            "FilePersonRepository.ConnectionString"]);
    }

    public SetConnectionString(string connectionString)
    {
         /* do all required stuf like flush previous file, open file, etc...  */ 
    }

    /* Interface members here... */
}

Almost every storage have some kind of connection string, so this interface shall be applicable to the majority of other possible implementations (Mongo, SQL, Azure Table).

Your application bootstrap could specify what configuration dialog is to be used. For example as:

container.Register<IPersonRepositoryConfigurationDailog, FileRepositoryConfigurationDialog>();
sANDwORm
  • 191
  • 5