数据访问层的静态方法与依赖注入

static methods vs dependency injection for data access layer

对于我的 ASP.NET MVC 项目,我曾经严重依赖静态方法来访问数据,有点像这样:

public class MyTable
{

    public static IEnumerable<MyTable> findAll()
    {
        using (var connection = new SqlConnection(Provider.connString))
        {
            //implementation here
        }


        return datas;
    }

    public static MyTable find(guid id)
    {
        using (var connection = new SqlConnection(Provider.connString))
        {
            //implementation here
        }

    }

}

所以我可以在我的控制器中这样调用它们:

var datas = MyTable.findAll();

我对它很满意,但我怀疑我是否在使用它进行数据访问方面做得很好。

现在,我正在尝试将依赖注入应用于我的数据访问 class 只是为了学习目的,就像这样:

public class MyTable
{

    public IDatabaseEngine _engine = null;

    public MyTable()
    {
    }

    public MyTable(IDatabaseEngine engine)
    {
        _engine = engine;
    }

    public IEnumerable<MyTable> findAll()
    {
        using (var connection = _engine.getConnection())
        {
            //implementation here
        }
        return datas;
    }

    public MyTable find(guid id)
    {
        using (var connection = _engine.getConnection())
        {
            //implementation here
        }
        return data;
    }
}

问题是,这意味着我必须创建一个 MyTable class 的实例来调用我的控制器上的数据访问方法,如下所示:

var table = new MyTable(new MSSQLEngine(provider.connString));
var datas = table.findAll(); 

好吧,要么是我没有足够的经验来解决这个问题,要么是我做错了,我觉得为我需要的每个数据访问实例化对象感到不舒服。

我的问题是

  1. 我是不是做错了依赖注入? (我想我是)
  2. 创建数据访问对象 class 以调用是好的做法吗 控制器或其他 classes?
  3. 中的数据访问方法
  4. 改用静态方法是好的做法吗? (就像我 在第一个例子中做)
  5. 如果我的 DI 实现是错误的,我该如何纠正?

您问题的答案:

  1. 没有,详见下文
  2. DI 的主要优点是依赖于抽象而非实现。您不需要创建 classes 的实例,DI 会为您完成。您只需要将接口注入您的 classes 并在您的 IoC 中注册它们。
  3. 不,因为您将无法为您的方法编写单元测试。
  4. 见下文。

要正确使用 DI,首先您需要将 MyTable class 提取到接口中,然后将该接口注入您的控制器。

public interface IMyTable 
{
    IEnumerable<MyTable> findAll();
    // other methods
}

public class MyTable : IMyTable 
{
    // your implementation
}

那么你的控制器应该是这样的:

public class YourController : Controller
{ 
    private IMyTable myTable;
    public YourController(IMyTable myTable)
    {
        this.myTable = myTable;
    }

    public ActionResult YourAction()
    {
        var result = myTable.findAll();
        // ...
    }
}

我个人使用 Castle Windsor 作为 IoC 容器,here is an example 在 ASP.NET MVC 应用程序中使用 Castle Windsor

  1. 依赖注入只是一种在实例字段中存储依赖项的技术,因此您基本上是在 MyTable 实现中正确执行它。

  2. 如果您在控制器的构造函数中获得 IDatabaseEngine 的实例,如果您要在控制器的测试中模拟数据库引擎,则可以从依赖注入中受益。您仍然需要一种方法在您的应用程序中 注入 实例。至于现在,您正在控制器中创建一个具体的 class MSSQLEngine 实例,因此没有任何好处。

  3. 在这种情况下使用静态方法并不好,因为在您决定实施与数据库无关的测试时,您需要重构为使用实例。从最小的测试设置开始是一个很好的做法,这样您就可以在项目生命周期的早期阶段实现可测试性。我建议在你的代码库变大之前完成这个困难的部分。

  4. 了解如何在控制器中注入 IDatabaseEngine 的实例,并编写一个利用依赖注入的最小测试。大多数 MVC 框架都允许设置 IoC 容器作为工厂。在测试中,您需要使用一些模拟实现或模拟框架,例如 Moq 来构成 IDatabaseEngine.

  5. 的实例

虽然 DI 在某些情况下很酷,但在大多数情况下是过度工程!

我解释一下。如何创建静态方法。只需将 "static" 放在方法前面即可。您可以通过调用 Class.Method() 轻松调用它。此外,它对系统来说是高效的,因为该方法只创建一次。

亲:效率高。 缺点:不可变

虽然DI,你可能需要一些容器,然后是一个接口,你可以添加一个class,任何实现接口的class。而且,在代码的某些部分,您将需要创建 class 的实例(因此创建该方法的新实例)。

Pro: 是可变的 缺点:效率不高,冗长。