ASP.NET MVC实体框架中使用DTO的最佳实践

时间:2016-05-27 12:04:22

标签: asp.net-mvc entity-framework

使用Entity Framework和DTO的首选方法是什么?

让我们说在映射后我有像:

这样的对象
Author
    int id
    sting name
    List<Book> books

Book
    int id
    string name
    Author author
    int authorID

我的DTO

AuthorDTO
    int id
    sting name

BookDTO
    int id
    string name
    int authorID

由于作者可以拥有大量书籍,我不想检索所有书籍,例如我只对作者感兴趣。

但有时我可能想要获得少数作者,过滤书籍或所有书籍。 我可以使用多个查询AuthorDTO GetAuthor(int id) List<BookDTO> GetBooks(int authorID)。但这意味着可以访问数据库。

我看到它的方式:

  1. 如果我在AuthorDTO字段List<BookDTO> books,则可以完成工作。但有时我会把这个列表保留为空,例如我只列出了作者。这意味着要记住一些不一致,混乱和许多细节。

  2. 返回Tuple<AuthorDTO, List<BookDTO>>可能会有点混乱。

  3. 定义新的DTO。

    AuthorAndBooksDTO
        AuthorDTO author
        List<BookDTO> books
    

4 个答案:

答案 0 :(得分:2)

我认为对所涉问题的一些澄清实际上会解决你的困惑。

首先也是最重要的是,您的实体类 DTO。事实上,那是所有。它们是表示数据库中表结构的类,因此来自实体框架查询的数据可以映射到它们。换句话说,它们是字面上传输数据的对象。微软以及随后太多的MVC开发人员的失败是将它们与MVC模式描述的大型M模型混为一谈。

因此,使用Entity Framework返回实体的一个或多个实例,然后在最终在代码中使用它之前将其映射到另一个DTO类,这绝对没有意义。你所做的只是创建一个毫无意义的抽象层次,它不会给你的应用程序增加任何东西,但还有另外一点需要维护。

就关系而言,这就是Entity Framework的懒惰/急切加载的地方。但是,为了利用它,表示关系的属性必须遵循一个非常具体的约定:

public virtual ICollection<Book> Books { get; set; }

如果您将其键入List<Book>之类的内容,则实体框架根本不会触及该关系。它不会加载相关实体,并且在将实体保存回数据库时不会保留对该属性所做的更改。 virtual关键字允许Entity Framework动态子类化您的实体并覆盖collection属性以添加延迟加载的逻辑。如果没有它,只有在您明确使用EF API中的Load时才会加载相关实体。

假设您的财产以这种方式定义,那么您将获得整个世界的能力。如果您想要所有属于作者的书籍,您可以直接与author.Books进行交互(迭代,查询,等等)。在执行需要评估查询集的操作之前,不会进行任何查询。 EF根据您从数据库请求的信息发出即时查询。如果您确实想要在检索作者的同时加载所有相关图书,则可以在查询中使用Include

var author = db.Authors.Include(m => m.Books).SingleOrDefault(m => m.Id == id);

答案 1 :(得分:2)

坚持使用作者DCD并有选择地填写名单的问题在于,您现在被迫跟踪DTO的来源。书籍清单是不是水合的,或者这个作者根本没有书籍?我是否必须返回我的控制器并调用另一种方法来获得相同DTO的不同状态?消费者的立场缺乏清晰度。

根据我的经验,我倾向于使用更多DTO,而不是尝试重复使用一组基本DTO来表示多个不同的数据集。它需要更多的“样板”,必须在DTO和实体之间建立一大堆类似的DTO和映射,但最终的特异性和清晰度使代码库更易于阅读和管理。

答案 2 :(得分:1)

我的第一个问题是问你为什么要创建DTO?另一方是否有使用此数据的消费者?这是一个屏幕吗?您是否正在建立DTO只是为了构建DTO?

由于您将问题标记为MVC,我将假设您正在向视图发送数据。您可能想要一个ViewModel。此ViewModel应包含使用它的View上显示的所有数据。然后使用实体框架填充视图模型。这可以通过使用投影或复杂事物的单个查询来完成。

所以那就是诅咒。我会说你想要选项3.

答案 3 :(得分:0)

就像其他人说的那样,为了清楚起见,你应该避免创建&#34; generic&#34; DTO针对具体案例。

如果您希望有时会有作者和他们的一些书籍,那么为此建模DTO。 当您只需要作者时,再创建另一个更适合的DTO。 或许你不需要DTO,也许包含他们名字的List就足够了。或者也许您实际上可以使用匿名类型,例如new { AuthorId = author.Id, AuthorName = author.Name }。这取决于实际情况。

如果您正在使用ASP.NET MVC,那么您想要的DTO实际上是一个最能代表您的页面的ViewModel。

根据您所描述的内容,您可以查看模型

public class BookViewModel{
    public int Id {get;set;}
    public string Name {get;set;}
}
public class AuthorViewModel{
    public int Id {get;set;}
    public string Name {get;set;}
    public List<BookViewModel> Books {get;set;} = new List<BookViewModel>();
}

public class AuthorsViewModel
{
    public List<AuthorViewModel> Authors {get;set;} = new List<AuthorViewModel>();

    //add in this class other properties, like the filters used on the page...

    public void Load(){
        //here you can retrieve the data from your database. 
        //you could do like this:
        //step 1: retrieve data from DB via EF
        //step 2: fill in the Authors view models from the data at step 1
    }

}

//and in your controller you're calling the Load method to fill you're viewmodel with data from db. 
public class AuthorsController{
    public ActionResult Index(){
        AuthorsViewModel model = new AuthorsViewModel();
        model.Load();
        return View(model);
    }
}
相关问题