使用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)
。但这意味着可以访问数据库。
我看到它的方式:
如果我在AuthorDTO
字段List<BookDTO> books
,则可以完成工作。但有时我会把这个列表保留为空,例如我只列出了作者。这意味着要记住一些不一致,混乱和许多细节。
返回Tuple<AuthorDTO, List<BookDTO>>
可能会有点混乱。
定义新的DTO。
AuthorAndBooksDTO
AuthorDTO author
List<BookDTO> books
答案 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);
}
}