3

我目前正在使用 C# 学习 .Net MVC。我很好奇人们如何使用他们的实体框架上下文。下面列出了几个相关的问题。我希望你能提供反馈。

  1. 上下文是否应该在控制器中实例化?在模型中?完全在另一层抽象中?什么是适当的封装?

  2. 在进行简单的 CRUD 操作时,是否应该在控制器中调用说 context.Add(entity) 的调用?

  3. 在创建上下文查询时,这些查询是否应该在控制器中完成?还是在模型中?如果在模型中使用这些是否应该使用静态方法?

我希望我的问题很清楚。一般来说,我很感兴趣应该如何与数据库交互,以及如何从应用程序中正确地抽象出来。欢迎任何与此相关的信息或建议。

4

3 回答 3

5

一切都取决于您的应用程序的大小。但是对于大型应用程序,您通常不会在控制器中实例化任何依赖项。它使您的代码与某些特定的数据访问提供程序实现紧密耦合。如果你明天搬到MongoDB怎么办?考虑一下您需要在应用程序中更改哪些地方。

当您对控制器进行单元测试时,这与数据访问提供者的模拟有关。您应该能够通过一些模拟实现来切换真实对象,该对象在您的应用程序中进行持久性。如果你要让你的控制器依赖于上下文并且你要在控制器中实例化上下文,那将是不可能的(或者至少非常困难)。

传统方法如下:

  • 创建数据访问抽象,您的控制器将依赖它。通常存储库就是这样的抽象。
  • 并使用一些依赖注入框架将此抽象的实现注入控制器。

因此,您的控制器将只知道抽象,很容易更改数据访问提供者(只需为 MongoDB 创建存储库并将该实现提供给控制器)​​,当您进行单元测试时,也很容易提供数据访问提供者的模拟实现的控制器。

样本:

public class SalesController : Controller
{
    private IOrderRepository _repository; // depend on interface

    // inject some implementation of dependency into controller
    public SalesController(IOrderRepository repository)
    {
       _repository = repository;
    }

    public ActionResult Index()
    {
        var orders = _repository.FindAll();
        return View(orders);
    }
}

您将使用具体存储库实现的唯一地方是依赖注入框架的配置,更改实现非常容易:

Bind<DbContext>().To<ShopEntities>();
Bind<IOrderRepository>().To<EFOrderRepository>();

进行单元测试也很容易:

[Test]
public void ShoulReturnAllOrders()
{
    List<Order> orders = CreateListOfOrders();
    var mock = new Mock<IOrderRepository>();
    mock.Setup(r => r.FindAll()).Returns(orders);

    var controller = new SalesController(mock.Object);
    var result = (ViewResult)controller.Index();

    mock.VerifyAll();
    Assert.That(result.Model, Is.EqualTo(orders));
}
于 2013-07-29T07:20:14.493 回答
1

这是我不久前遇到的一个问题。最后,我认为在单独的层中做这些事情要好得多。

  1. 我们在控制器中创建上下文,然后将其传递给控制器​​使用的其他层。

  2. 在最简单的情况下,有人可能会说,如果您只添加实体,那就可以了。但是如果你有某种验证呢?我不是指字段值,而是类似的-如果用户名已被占用,则无法添加新用户。如果您将 context.Add(entity) 方法放在控制器中,那么很快您将看到具有 100 多行代码的控制器方法,它们执行逻辑、验证等。

  3. 由于我们的查询非常复杂,我们有一个“查询”命名空间,每个查询包含一个类。这在此处进行了描述。我不太了解您对静态方法的问题。

你必须记住,控制器中的所有代码都将难以进行单元测试——你必须设置一些上下文等等。在我看来,除了 ASP.NET MVC 中的展示应用程序之外,“薄控制器,厚模型”模式是必须具备的。

此外,这完全取决于您的“模型”层是什么。您使用的是 ViewModel,还是封装了所有逻辑的模型?

于 2013-07-29T07:19:52.727 回答
1

要获得适当的抽象级别,您必须定义一个接口。例如: 定义了所有必需操作的IDataBaseService 。如:嗯..

IEnumerable<BugItem> ReadAllBugs();
IEnumerable<BugItem> ReadAllBugsForProject(string project name); 

ETC...

然后,您可以使用任何 ioc 容器,在运行时注入 IDataBaseService 的实现。

控制器将使用 IDataBaseService 接口而不是 DB Context。

PS:有时这种抽象级别有点矫枉过正。这取决于一个应用程序。

  1. 在控制器中使用 IDataBaseService 是可以的。
  2. 见前文。不会有这样的电话。您必须在界面中定义 UpdateBugItemForProject 方法。
  3. 和以前一样
于 2013-07-29T07:20:52.660 回答