我正在为项目使用ASP .NET MVC(C#)和EntityFramework(数据库优先)。
假设我正在“电影细节”页面上,该页面显示了我的数据库的一部电影的细节。我可以点击每部电影并编辑每部电影。 因此,我有一个Movie类,以及一个用EF生成的Database.Movie类。
我的索引操作如下:
public ActionResult MovieDetail(int id)
{
Movie movie = Movie.GetInstance(id);
return View("MovieDetail", movie);
}
GetInstance方法应该返回一个Movie类的实例,目前看起来像这样:
public static Movie GetInstance(int dbId)
{
using (var db = new MoviesEntities())
{
Database.Movie dbObject = db.Movies.SingleOrDefault(r => r.Id == dbId);
if (dbObject != null)
{
Movie m = new Movie(dbObject.Id, dbObject.Name, dbObject.Description);
return m;
}
return null;
}
}
它工作正常,但这是实现它的好方法吗?有没有其他更简洁的方法来获取我的Movie类实例? 感谢
答案 0 :(得分:3)
is this a good way to implement it?
这是一个非常主观的问题。这是有效的,这个实现在技术上没有任何问题。对于我的小型家庭项目,我使用过类似的东西。
但对于业务应用程序,最好使您的实体与MVC应用程序无关。这意味着您的数据上下文+ EF +生成的实体应保存在一个单独的项目中(让我们将其称为“数据项目”),实际数据以DTO的形式传递
因此,如果您的实体类似于:
public class Person {
public int Id { get; set; }
public string Name { get; set; }
}
您希望有一个能够传递该数据的等效DTO类:
public class PersonDTO {
public int Id { get; set; }
public string Name { get; set; }
}
这意味着您的数据'项目仅回复DTO类,而不回复实体。
public static MovieDTO GetInstance(int dbId)
{
...
}
最有意义的是,您的DTO也在一个单独的项目中。所有这些抽象的原因是当你必须更改你的datacontext 时(例如应用程序将开始使用不同的数据源),你只需要确保新的数据项目也与相同的DTO。它在内部的工作原理以及它使用的实体只与项目内部相关。从外部(例如,从您的MVC应用程序),您获取数据如何并不重要,只有您以MVC项目已经理解的形式传递它(DTO类)
所有MVC控制器逻辑都不必更改,因为DTO对象没有改变。这可以节省你的时间。如果您将实体链接到Controller和View,如果您突然决定更改实体,则必须重写两者。
如果您担心为将实体转换为DTO而必须编写的代码量,反之亦然,您可以查看Automapper等工具。
主要问题:这需要吗?
这又是一个非常主观的问题。它与项目范围有关,也与应用程序的预期寿命有关。如果它应该被使用很长时间,那么保持一切可以互换可能是值得的。如果这是一个规模小,寿命短的项目,实现这一目标的额外时间可能不值得。
我无法就此给你一个明确的答案。评估您希望应用程序适应变化的程度,以及应用程序将来发生变化的可能性。
免责声明:这就是我们在我工作的公司所做的事情。这不是解决此类问题的唯一方法,但它是我熟悉的问题。就个人而言,除非有功能性原因,否则我不喜欢抽象。
答案 1 :(得分:1)
一些事情:
您正在使用的命名有点尴尬和混乱。通常,您不希望项目中的两个类名相同,即使它们位于不同的名称空间中。它在技术上没有任何问题,但它会造成混乱。我需要哪个Movie
?如果我正在处理Movie
个实例,那么它是Movie
还是Database.Movie
?如果您坚持使用Movie
和MovieDTO
或Movie
和MovieViewModel
等名称,则类名清楚地表明目的(缺少后缀表示数据库支持的实体)。< / p>
特别是如果你来自另一个MVC框架,比如Rails或Django,ASP.NET的MVC的特殊风格可能有点迷失方向。大多数其他MVC框架都有一个真正的模型,一个单独的类充当所有业务逻辑的容器,并且还充当存储库(在某种意义上可以被视为业务逻辑)。 ASP.NET MVC并不是那样工作的。您的实体(代表数据库表的类)应该是愚蠢的。它们只是实体框架的一个地方,可以填充从数据库中提取的数据。您的模型(MVC中的M)实际上是您的视图模型和服务/ DAL层的组合。你的Movie
类(不要与Database.Movie
混淆......看看为什么命名位很重要)另一方面是尝试做三重任务,充当实体,视图模型和存储库。这太过分了。你的课程应该做一件事,做得好。
同样,如果您有一个将作为服务或存储库的类,它应该是一个实际的服务或存储库,包含这些模式所暗示的所有内容。即使这样,您也不应该在方法中实例化上下文。处理它的最简单正确方法是简单地将您的上下文设置为类实例变量。类似的东西:
public class MovieRepository
{
private readonly MovieEntities context;
public MovieRepository()
{
this.context = new MovieEntities();
}
}
更好的是,虽然是使用控制反转并传递上下文:
public class MovieRepository
{
private readonly MovieEntities context;
public MovieRepository(MovieEntities context)
{
this.context = context;
}
}
然后,您可以使用依赖注入框架(如Ninject或Unity)来满足您的依赖关系(最好使用请求范围的对象),只要您需要MovieRepository
的实例。如果你刚开始的话,这有点高级,所以如果你暂时不去全力以赴,这是可以理解的。但是,您仍然应该考虑到这一点来设计您的类。控制反转的重点是抽象依赖项(类似于需要从数据库中提取实体的类的上下文),以便在需要时可以切换出这些依赖项(比如说,如果有时间的话)您将从Web API而不是通过Entity Framework检索实体,或者即使您决定切换到不同的ORM,例如NHibernate。在您的代码的当前迭代中,您必须触及每个方法(并且通常会更改您的类,违反开放式关闭)。
答案 2 :(得分:0)
实体模型永远不应该充当视图模型。向视图提供数据是视图模型的重要角色。视图模型很容易被识别,因为除了保存仅依赖于该数据的数据和业务规则之外,它没有任何其他角色或责任。因此,它具有任何其他纯模型的所有优点,例如单元可测试性。
可以在Dino Esposito的The Three Models of ASP.NET MVC Apps.
中找到对此的一个很好的解释答案 3 :(得分:0)
您可以使用AutoMapper
什么是AutoMapper?
AutoMapper是一个简单的小型库,用于解决一个看似复杂的问题 - 摆脱将一个对象映射到另一个对象的代码。这种类型的代码是相当沉闷和无聊的写,所以为什么不发明一个工具来为我们做?
如何开始?
我在哪里可以获得它?
首先,安装NuGet。然后,从包管理器控制台安装AutoMapper:
PM> Install-Package AutoMapper