1

I have a data access layer, which currently communicates with a database.

public interface IDao<T>  // T is my DTO
{
   Write(IEnumerable<T> dtosToPersist)
}

public class Dao<T> : IDao<T>
{
    private readonly IBulkCopySaver<T> _bulkSaver;
    private IEnumerable<T> _buffer;

    public Dao(IBulkCopySaver<T> bulkSaver)
    {
       _bulkSaver = bulkSaver;
       _buffer = new List<T>();
    }

    public void Write(IEnumerable<T> dtosToPersist)
    {
     // implementation logic.
    }

}

I would like to also add persistence into XML for example. My goal is to replace the Dao with a new object implementing IDao, changing calling code as little as possible (Dao is massively used). I'm using a dependancy injector and the golden goose would be to just switch from database to xml persistence just by changing the injection interface/concrete type bindings.

The problem for me is writing in the XML case would need more information, I would need the folder path for example. Also that folder path is determined at runtime from program arguments and the type T. The interface contract of my data access object is not satisfying as it only takes an IEnumerable.

What are my options here ?

My first idea is to make the dependancy injector, aware of the filepath parameter and inject it as constructor parameter. Supposing the folderPath is in a context local field of the injector, using Ninject :

Bind<IDao<DtoConcreteType>.To<Dao<DtoConcreteType>
Bind<IDao<DtoConcreteType>.To<XmlDao<DtoConcreteType>.WithConstructorArguments("filePath", context.folderPath)

However this feels a bit wrong to me. The injection framework will do reflection under the hood and I feel there should be a OOP way to solve this.

Another idea as I was writing this bits is that maybe the XmlDao.Write may be calling a provider object (injected) that would give it the details it lacks (an IFilePathProvider that knows program arguments and makes decision based on type T). This seem way more simple

What is your opinion ? Do you have an idea for this ? What about the ideas I had ?

2 Answers 2

2

Using constructor arguments is likely the best way to provide the file path information.

You are concerned that the DI container implementation uses reflection. If you wish to avoid this, you will have to inject the IDao dependency manually. This can be done by passing an IDao object around as a method parameter. This makes the dependency more explicit, but also means that large parts of the code will have to be updated to pass through this additional parameter. So possibly, sticking with the current DI approach might be a better idea.

If you have a design that you cannot modify, then you can use global variables to pass additional data. Global variables can be very problematic because they introduce a data flow dependency that is not obvious to readers of the code. This can cause coupling between distant parts of the code, and can lead to “action at a distance” that is hard to debug. But when you have no other option, it may be a legitimate solution.

To make the use of global variables more bearable, it should be possible to temporarily override the value of the variable. Furthermore you will want to encapsulate the variable behind some methods so that you can set a debugger breakpoint on access or change. Something like this:

static class DBFilePath {
  private string path;

  public static string Get() { ... }
  public static void Set(...) { ... }

  public static IDisposable TemporaryChange(string newPath) {
    var oldPath = Get();
    Set(newPath);
    return Disposable.Create(() => Set(oldPath));
  }
}

// some method accessing the global variable
void Foo() {
  ...
  var path = DBFilePath.Get();
  ...
}

// some method that overrides the path temporarily
void Bar() {
  ...
  using (DBFilePath.TemporaryChange("bar.db")) {
    Foo();  // or any other code that indirectly accesses the global var
  }
  ...
}

However, such techniques are mostly a hack of last resort when you cannot modify existing APIs because you need to maintain backwards compatibility for some external client which is not under your control. But in a way this is similar to using a global dependency injection container.

0

You know how you want to abstract away the passing of data but you also need to pass meta data about the way to store that data, which apparently was not needed before. Why not I ask?

You probably already have a database connection that was initialized at a higher level, that you have grown to take for granted. This is however your storage information that you may want to have in your interface too. Or maybe in a separate interface if you are anal about SRP and ISP.

Once you have that you can pass it along with the data as a separate argument. In the database case it will be used to set up a connection, in the xml case to provide a file path.

It can be as simple as a single string, where in the first case it is a connection string and in the second a file path. In the new setup a new connection would be set up for each copy operation, which would probably be preferable anyway.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.