C# 在存储库模式中使用构造函数的困惑

C# 在存储库模式中使用构造函数的困惑,c#,model-view-controller,C#,Model View Controller,我是C#的新手,我读过关于构造函数的书,我知道我们使用它们的原因是我们可以在构造时分配我们想要的任何东西,但我很难理解的是,我有一个像这样的学生: public interface IStudentRepository { object getAll(); } 我在StudentRepository中实现了它: public class StudentRepository:IStudentRepository { pu

我是C#的新手,我读过关于构造函数的书,我知道我们使用它们的原因是我们可以在构造时分配我们想要的任何东西,但我很难理解的是,我有一个像这样的学生:

    public  interface IStudentRepository
    {

        object getAll();
    }
我在StudentRepository中实现了它:

     public class StudentRepository:IStudentRepository
    {
       public testDBEntities DB = new testDBEntities();
        public object getAll()
        {
            var reslt = (from s in DB.Students
                     select new StudentViewModel
                     {
                         Name = s.Name,
                         Id=s.Id
                     });

            return reslt;
        }
    }
在我的homeController中,我可以执行以下操作:

    IStudentRepository _repository=new StudentRepository();   
    public JsonResult Index()
    {
       var m= _repository.getAll();
       return Json(m,JsonRequestBehavior.AllowGet);
    }
那我为什么要有一个构造函数呢?比如:

       IStudentRepository _repository=new StudentRepository();
    public HomeController(StudentRepository _repository)
    {
        this._repository = _repository;
    }
    public JsonResult Index()
    {
       var m= _repository.getAll();
       return Json(m,JsonRequestBehavior.AllowGet);
    }

如果我的问题是基本的,我很抱歉,但我找不到正确的答案,请提前感谢

我认为您的构造函数不正确。它一定像

IStudentRepository _repository
public HomeController(IStudentRepository _repository)
{
     this._repository = _repository;
}
事实上,最好在构造函数中传递
IStudentRepository
,以尊重
依赖项注入

您希望通过单元测试来测试控制器的映像。所以你想创建假数据。如果在控制器内创建新的
StudentRepository
,如何测试它

但是如果你经过构造函数,比如:

public class FakeStudentRepository : IStudentRepository 
{
     //return fake data
}

通过某种配置,第三个库“依赖注入”将把您的伪回购传递给控制器,您可以测试它

单一责任原则

第一个优点是您的
HomeController
不必担心创建
StudentRepository
对象。其他人已经实例化了它,从数据库、文件或用户输入中生成了它。这很好,因为控制器可以专注于它的实际工作,而不是如何实例化另一个对象。这是越来越接近的问题

依赖注入

然后,为了利用第二个接口,构造函数应该接收接口而不是类。这就是所谓的

如果以后出于某种原因,您决定以任何方式更改
StudentRepository
类,只要您仍然实现该接口,就不必更改控制器

因此,您的最终代码可能如下所示:

class HomeController
{
    private IStudentRepository _repository;
    public HomeController(IStudentRepository _repository)
    {
        //We don't know who instanciated the repo, but we don't care
        this._repository = _repository;
    }

    public JsonResult Index()
    {
        //We only know about the repo that it implements IStudentRepository
        //So we know we can call this method without a risk.
        var m= _repository.getAll();
        return Json(m,JsonRequestBehavior.AllowGet);
    }
}
public class TestStudentRepository : IStudentRepository
{
    private string MyDatabaseFilePath;

    public object getAll()
    {
        //Load everything from my text file
    }
}
当它改变时

为了说明一个变化,假设您首先想要测试您的repo,但没有正确的数据库连接。因此,您决定使用一个文件,您的类将如下所示:

class HomeController
{
    private IStudentRepository _repository;
    public HomeController(IStudentRepository _repository)
    {
        //We don't know who instanciated the repo, but we don't care
        this._repository = _repository;
    }

    public JsonResult Index()
    {
        //We only know about the repo that it implements IStudentRepository
        //So we know we can call this method without a risk.
        var m= _repository.getAll();
        return Json(m,JsonRequestBehavior.AllowGet);
    }
}
public class TestStudentRepository : IStudentRepository
{
    private string MyDatabaseFilePath;

    public object getAll()
    {
        //Load everything from my text file
    }
}
好的,这样行。现在假设您有一个到数据库的连接,但是在使用getAll()方法之前,该连接需要一些步骤。对于您的解决方案,您必须在
HomeController
中执行初始化步骤

现在,使用此模式,您可以执行以下操作:

public class StudentRepository : IStudentRepository
{
    private string _connectionString;

    public DatabaseStudentRepository(string ConnectionString)
    {
        _connectionString = ConnectionString;
    }

    public object getAll()
    {
        //Load everything from my text file
    }
}
然后,是对象的角色在使用控制器来确保它正确实例化了您的对象。控制器中没有任何更改

工厂模式

最后一步是为你的StudentRepository创建一个工厂。工厂的职责是实例化适当的回购:

public static class StudentRepositoryFactory
{
    public static IStudentRepository InstanciateRepo(bool FromDatabase)
    {
        if (FromDatabase)
        {
            return new DatabaseStudentFactory("ConnectionString To my server");
        }
        else
        {
            return new TestStudentRepository();
        }
    }
}

现在,无论你需要以何种方式启动回购协议,这都是工厂的职责;虽然控制器的角色是使用存储库的一种方法getAll()。

通过将存储库注入构造函数,您正在执行一种称为依赖项注入的操作。这消除了控制器类和特定的
IStudentRepository
实现之间的紧密耦合。构造函数注入是依赖项注入的一种方法(可能是最常见的方法)。例如,什么变化?即使我在Studentrepository中添加了更多接口,我仍然可以通过实例化访问它们,我说的对吗?谢谢你的好例子,如果我有多个存储库,我应该在homecontroller中也有多个构造函数吗?@mortezasol No,这就是这种构造的意义所在。你在HomeController中的构造函数只会收到一个IStudentRepository,不管它是什么类实现的。很抱歉Martin问了一些愚蠢的问题,如果我想在HomeController中使用其他存储库,最好的做法是什么?将它们也添加到此构造函数中?