简体   繁体   English

我如何首先使用可重用的查询/表达式和EF代码在父级中获取子实体作为DTO?

[英]How can I fetch child entities as DTO in parent using reusable queries/Expression's with EF code first?

I am having issues finding a good way to convert Entities with their children to DTO objects. 我在寻找将实体及其子实体转换为DTO对象的好方法时遇到问题。 For this post I've created pseudo code which is a simplified example that leaves out the database context, DTO objects. 在本文中,我创建了伪代码,这是一个简化的示例,省略了数据库上下文DTO对象。 Assuming I have a parent Entity and child Entity: 假设我有一个父实体和一个子实体:

public class Parent {
    int Id;
    string Name;
    List<Child> Children;
}

public class Child {
    int Id;
    string Name;
    Parent Parent;
    int ParentId;
}

I've looked at two possibilities, but I haven't been able to find a good solution. 我研究了两种可能性,但我一直找不到一个好的解决方案。 Please have a look at the two examples and where I got stuck. 请查看两个示例以及我遇到的问题。

1. Example using select queries 1.使用选择查询的示例

To retreive all the parent entities as DTO's, I could then in a Controller do: 要检索所有父实体作为DTO,可以在Controller中执行以下操作:

public IHttpActionResult GetParents()
{
    var children = from c in _context.Children
    select new ChildDTO()
    {
        Id = c.Id,
        Name= c.Name
    };

    var parents = from p in _context.Parents
    select new ParentDTO()
    {
        Id = p.Id,
        Name = p.Name       
        Children = children.ToList(),
    };

    return parents;
}

This will return the parent DTO object with all its children as DTO objects. 这将返回父DTO对象及其所有子对象作为DTO对象。 If I wanted to create a new function to get just Parent with id '1', I would at the moment have to duplicate the select statement to add a where clause: 如果我想创建一个新函数以仅获取ID为'1'的Parent,那么我现在必须复制select语句以添加where子句:

public IHttpActionResult GetParent(int parentId)
{
    var parents = from p in _context.Parents where p.id == parentId
...

And there might also be cases where I do not want the child objects back if I just want to display a list of parents. 在某些情况下,如果我只想显示父母列表,我也不想让孩子反对。 Which would mean that I would basically have to duplicate the code and change the select to this: 这意味着我基本上必须复制代码并将选择内容更改为此:

select new ParentDTO()
{
    Id = p.Id,
    Name = p.Name       
    //Removed the Children
    //Children = children.ToList(), 
};

In this example I do not see a good way to reuse code as much as possible, so that I don't end up writing the same basic select statements over and over. 在此示例中,我没有看到一种尽可能多地重用代码的好方法,因此,我最终不会一遍又一遍地编写相同的基本select语句。

2. Example using Expressions 2.使用表达式的示例

I could also create Expressions for the parent and child, but I would not know 我也可以为父母和孩子创建表达式,但是我不知道

private static readonly Expression<Func<Child, ChildDTO>> AsChildDTO =
p => new ChildDTO()
{
    Id = p.Id,
    Name = p.Name
};

private static readonly Expression<Func<Parent, ParentDTO>> AsParentDTO =
p => new ParentDTO()
{
    Id = p.Id,
    Name = p.Name
};

To get the parents I could then in my controller do: 为了得到父母,我可以在控制器中执行以下操作:

...
//Get list of parents
var parents = _context.Parents.Select(AsParentDTO);

//Or: Get only parent with Id
var specificParent= _context.Parents
.Select(AsParentDTO)
.Where(p => p.Id == 1);

return parents;
...

This solution seems good to me since I can reuse the Epressions and extend them if I want. 这个解决方案对我来说似乎很好,因为我可以重复使用Epressions并根据需要扩展它们。 I only do not seem to be able to Include the children to the parent this way: 我似乎只能通过这种方式将孩子包括在父母中:

...
var parents = _context.Parents
.Include(p => p.Children)
//I have no idea if it is possible to Invoke the child Expression here...
.Select(p => p.Children= AsChildDTO.Invoke()) //<-- this does not work
.Select(AsParentDTO)
...

As I wrote in the comment above; 正如我在上面的评论中所写; I have no idea if it is possible to somehow invoke the Child Expression here. 我不知道是否有可能在这里调用子表达式。

Outro 结尾

These are the two things I tried but got stuck with. 这是我尝试过但仍然坚持的两件事。 But it could also be that I am missing a very obvious solution. 但是也可能是我错过了一个非常明显的解决方案。 My Question is how do I solve this issue in a way that I can reuse as much code as possible? 我的问题是,如何以可以重用尽可能多的代码的方式解决此问题?

I think you are vastly over complicating it. 我认为您过于复杂了。

var results=_context.Parents
  .Include(p=>p.Children);

will return your EF objects. 将返回您的EF对象。 That's what you should be working with. 那就是你应该使用的。 If you want to convert the EF objects to DTO objects, save that for the final projection (I rarely use DTO objects as the POCO objects from EF are usually just fine). 如果要将EF对象转换为DTO对象,请将其保存为最终投影(我很少使用DTO对象,因为来自EF的POCO对象通常就可以了)。

var parents=results.Select(p=>new ParentDTO
  { id=p.id,name=p.name,children=p.Children.ToList()}
);

If you just want parent 1, then: 如果只需要父母1,则:

var parent=results.Where(p=>p.id==1);

if you want it as parentDTO: 如果您希望将其作为parentDTO:

var parent=results.Where(p=>p.id==1).Select(p=>new ParentDTO {
  { id=p.id,name=p.name,children=p.Children.ToList()}
);

You could use things like AsParentDto, but doesn't that imply that you are going to be copying the entire Parent properties? 您可以使用AsParentDto之类的东西,但这是否意味着您将要复制整个Parent属性? (In your simple case -- id and name). (在您的简单情况下-ID和名称)。 And if you are copying the entire property list, why are you creating a new object with all the same properties as the EF object instead of just reusing the EF object? 并且,如果要复制整个属性列表,为什么要创建一个具有与EF对象相同的属性的新对象,而不仅仅是重用EF对象? The only time I'd use a Dto object is if I wanted to pass around a parent that only has some of the properties and I wanted to save myself from retrieving the additional properties from the database, in which case, I'd still use the original database query and just project into it as the final step. 我唯一使用Dto对象的情况是,如果我想传递仅具有某些属性的父对象,并且希望避免自己从数据库中检索其他属性,在这种情况下,我仍然会使用原始数据库查询,然后将其投影到最后一步。

var slimparent=results.Where(p=>p.id==1).Select(p=>new SlimParentDto {
  id=p.id });

Of course, if all I wanted was the parent id's then I'd just use an even simplier IQueryable<int> like: 当然,如果我想要的只是父ID,那么我将使用甚至更简单的IQueryable<int>

var parentids=results.Where(p=>p.id==1).Select(p=>p.id);

--- TL;DR --- -TL; DR-

Create a single method to retrieve your object will all the properties included. 创建单个方法来检索您的对象将包括的所有属性。 Everything then should use that as it's base, and attach further refinements in your controller to filter it down to just the data subset you want. 然后,所有内容都应以此为基础,并在控制器中附加进一步的细化以将其过滤为仅所需的数据子集。 Then as a last step, project the result into whatever DTO you want. 然后,作为最后一步,将结果投影到所需的任何DTO中。 Never use any methods to cause the IQueryable to be enumerated until you've done the projection. 在完成投影之前,切勿使用任何方法来枚举IQueryable。 EF/LINQ will then generate an optimal query for you, just retrieving the properties required to fill your DTO. EF / LINQ随后将为您生成最佳查询,只需检索填充DTO所需的属性即可。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何首先使用带有EF代码的wcf数据服务公开dto对象? - How can i expose dto objects using wcf data service with ef code first? 多个父实体首先在EF代码中包含一个子实体 - multiple parent Entities with one child entity in EF code first 如何在使用EF代码的.SaveChanges()期间记录所有实体更改? - How can I log all entities change, during .SaveChanges() using EF code first? 如何以EF6代码优先的方式删除实体之间的关系? - How can I remove relationship between entities in EF6 code-first? 如何首先使用EF代码为混合实体的集合建模 - How to model collection of mixed entities using EF code first 我可以重复使用代码来为具有 EF Core 的子属性选择自定义 DTO object 吗? - Can I reuse code for selecting a custom DTO object for a child property with EF Core? 在EF中更新父/子实体的正确方法是什么? - What's the proper way to update parent/child entities in EF? 如何在EF中更新相关实体(代码优先) - How to update related entities in EF (code first) EF代码优先-具有子实体的序列号 - EF Code First - Having Sequence Number for child entities 避免使用 EF Core 在子实体上附加父实体 - Avoid attaching parent entity on child entities using EF Core
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM