Bartek Wójcik Bartek Wójcik - 3 months ago 12
ASP.NET (C#) Question

Seemann's DI Azure Table Data Access

In book "Dependency Injection in .Net" by Mark Seemann, in second chapter, is analysis of some badly written 3-layer asp.net application. The main point is: application fails because the lowest layer, data access, can not be converted from SQL with Entity Framework to Azure with no-SQL database. Here is exact quotation:


To enable the e-commerce application as a cloud application, the Data Access library
must be replaced with a module that uses the Table Storage Service. Is this possible?
From the dependency graph in figure 2.10, we already know that both User Interface and Domain libraries depend on the Entity Framework-based Data Access library.
If we try to remove the Data Access library, the solution will no longer compile,
because a required DEPENDENCY is missing.
In a big application with dozens of modules, we could also try to remove those
modules that don’t compile to see what would be left. In the case of Mary’s application, it’s evident that we’d have to remove all modules, leaving nothing behind.

Although it would be possible to develop an Azure Table Data Access
library that mimics the API exposed by the original Data Access
library, there’s no way we could inject it into the application.


Graph 2.10:

enter image description here

My question is - why module that is imitating previous behavior can not be injected into application, and what that really mean? Is it related to Azure specifics? I have not been working much with no-sql database before.

Answer

Essentialy, what he means is that your UI code is directly dependent on the code in the Data Access library. An example of how this might be used in the UI-layer:

public class SomeController : Controller
{
    [Route("someRoute")]
    [HttpGet]
    public ViewResult SomeRoute()
    {
        // Here we're using the data component directly
        var dataComponent = new DataAccessLayer.DataComponent();
        return View(dataComponent.GetSomeData());
    }
}

If we want to swap out the DataAccess-library it means we would have to go into all our controllers and change the code to use the new component (unless we create exactly the same class names in the same namespaces, but that's unlikely).

On the other hand, we could also write the controller like this:

public class SomeController : Controller
{
    IDataComponent _dataComponent;
    public SomeController(IDataComponent dataComponent)
    {
        _dataComponent = dataComponent;
    }
    [Route("someRoute")]
    [HttpGet]
    public ViewResult SomeRoute()
    {
        // Now we're using the interface that was injected
        return View(_dataComponent.GetSomeData());
    }
}

By defining the class like this, we can externally specify which concrete class that implements the IDataComponent interface should be injected into the constructor. This allows us to "wire" our application externally. We're injecting a concrete class into a class.

Dependency Injection is one way to make it easier to "program against an interface, not a concrete class" .

The example Mark Seemann gives relates to databases vs Azure Table Storage, but it's just that, an example. This is not related to NoSql (or storage mechanisms in general). The same principles apply for everything that depends on other classes (generally service-type classes).

EDIT after comments: It's indeed true that you could just modify the internals of the DataComponent (or repository if that's what you're using). However, using DI (and programming against an interface in general) gives you more options:

  • You could have various implementations at the same time and inject a different implementation depending on which controller it is (for example)
  • You could reuse the same instance in all your controllers by specifying the lifecycle in the registration (probably not usable in this case)
  • For testing purposes, you could inject a different implementation into the controller (such as a mock, which you can test for invocations)