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

本文关键字:依赖 注入 静态方法 访问 数据 | 更新日期: 2023-09-27 18:19:47

对于我的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();

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

现在,我正试图将依赖项注入应用于我的数据访问类,例如

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类的实例,以调用方法对我的控制器进行数据访问,如下所示:

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

好吧,要么我没有经验,要么我做错了,我觉得为我需要的每一次数据访问实例化对象很不舒服。

我的问题是

  1. 我做依赖注入错误了吗?(我想我是)
  2. 创建要调用的数据访问类的对象是一种好的做法吗控制器或其他类中的数据访问方法
  3. 用静态方法代替它是一种好的做法吗?(和我一样在第一个示例中执行)
  4. 如果我的DI实现是错误的,我该如何使它正确

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

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

我解释。如何创建静态方法。只需将"静态"放在方法前面。通过调用Class.Method()可以很容易地调用它。此外,它对系统很有效,因为该方法只创建一次。

Pro:效率很高。缺点:不可变

对于DI,您可能需要一些容器,然后是一个接口,并且您可以添加一个类,任何实现接口的类。而且,在代码的某些部分,您将需要创建类的实例(ergo方法的新实例)。

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

问题答案:

  1. 否,请参阅下面的详细答案
  2. DI的主要优点是依赖于抽象而不是实现。您不需要创建类的实例,DI会为您创建。您只需要将接口注入到类中,并将它们注册到IoC中
  3. 不,因为您将无法为方法编写单元测试
  4. 请参见下文

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

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容器,这里有一个在ASP.NET MVC应用程序中使用Castle温莎的示例

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

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

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

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