我目前正在使用C#学习.Net MVC。我很好奇人们如何使用他们的实体框架上下文。下面列出了几个相关问题。我希望你能提供反馈。
应该在控制器中实例化上下文吗?在模特?在另一层抽象中?什么是适当的封装?
在进行简单的CRUD操作时,是否应该在控制器中调用context.Add(entity)?
在创建对上下文的查询时,是否应在控制器中完成这些查询?还是在模特?如果在模型中使用静态方法吗?
我希望我的问题很清楚。一般来说,我很感兴趣应该如何与数据库进行交互,以及如何从应用程序中正确地进行抽象。欢迎任何与此相关的信息或建议。
答案 0 :(得分:4)
全部取决于您的应用程序的大小。但是对于大型应用程序,通常不会在控制器中实例化任何依赖项。它使您的代码tightly coupled成为某些特定的数据访问提供程序实现。如果你明天搬到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>();
单位testing:
也很容易[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));
}
答案 1 :(得分:1)
这是我不久前发的一个主题。最后,我认为在separete层中做那些东西要好得多。
我们在控制器中创建上下文,然后将其传递给控制器使用的其他层。
在最简单的情况下,有人可以说,如果你只添加实体,那就没关系。但是,如果你有某种验证怎么办?我并不是指字段值,如果已经采用了用户名,则无法添加新用户。如果将context.Add(entity)方法放在控制器中,那么很快就会看到具有100多行代码的控制器方法,这些方法执行逻辑,验证等。
由于我们的查询非常复杂,因此我们有“查询”命名空间,每个查询包含一个类。这被描述为here。我不太了解静态方法的问题。
您必须记住,控制器中的所有代码都将难以进行单元测试 - 您必须设置一些上下文等等。在我的选择中,除了ASP.NET MVC中的展示应用程序之外,“瘦控制器,thich模型”模式是必不可少的。
此外,这完全取决于您的“模型”图层。您使用的是ViewModels,还是封装了所有逻辑的模型?
答案 2 :(得分:1)
要获得适当的抽象级别,您必须定义一个接口。例如: IDataBaseService ,其中定义了所有必需的操作。 如:嗯..
IEnumerable<BugItem> ReadAllBugs();
IEnumerable<BugItem> ReadAllBugsForProject(string project name);
等...
然后,您可以使用任何ioc容器,在运行时注入IDataBaseService的实现。
和控制器将使用IDataBaseService接口而不是DB Context。
PS:有时这种抽象程度有点矫枉过正。 这取决于应用程序。