【问题标题】:Converting Entity to DTO along with child entities将实体与子实体一起转换为 DTO
【发布时间】:2019-05-01 08:22:01
【问题描述】:

尝试将实体对象转换为本地对象,以便我可以将其用于进一步的转换。

这是我用来转换实体对象的代码;

IEnumerable<SystemArea> result = (from sa in CurrentContext.systemarea                                 
                                 select new SystemArea
                                 {
                                     SystemAreaId = sa.SystemAreaId,
                                     SystemAreaCode = sa.SystemAreaCode,
                                     SystemAreaType = sa.SystemAreaType,
                                     SystemAreaDescription = sa.SystemAreaDescription,
                                     SystemAreaCreatedDate = sa.SystemAreaCreatedDate,
                                     SystemAreaUpdateDate = sa.SystemAreaUpdateDate,
                                     SystemAreaStatus = sa.SystemAreaStatus,
                                     Count = sa.systemareafunctionality.Count,
                                     SystemAreaFunctionality = sa.systemareafunctionality.Select(e => new SystemAreaFunctionality { SystemAreaCode =e.SystemAreaCode })
                                 }).ToList();

这里的count变量是为了确认里面是否有子数据。

SystemAreaFunctionality 是我尝试使用 SELECT 函数在此处转换的子对象,但它始终是空白集合。剩余数据被分配给父对象,但这里唯一缺少的是子表记录。我哪里错了,请帮忙!

生成的 SQL:

SELECT
`Project3`.`C1`, 
`Project3`.`SystemAreaId`, 
`Project3`.`SystemAreaCode`, 
`Project3`.`SystemAreaType`, 
`Project3`.`SystemAreaDescription`, 
`Project3`.`SystemAreaCreatedDate`, 
`Project3`.`SystemAreaUpdateDate`, 
`Project3`.`SystemAreaStatus`, 
`Project3`.`C3` AS `C2`, 
`Project3`.`C2` AS `C3`, 
`Project3`.`SystemAreaCode1`
FROM (SELECT
`Project1`.`SystemAreaId`, 
`Project1`.`SystemAreaCode`, 
`Project1`.`SystemAreaType`, 
`Project1`.`SystemAreaDescription`, 
`Project1`.`SystemAreaCreatedDate`, 
`Project1`.`SystemAreaUpdateDate`, 
`Project1`.`SystemAreaStatus`, 
1 AS `C1`, 
`Project2`.`SystemAreaCode` AS `SystemAreaCode1`, 
`Project2`.`C1` AS `C2`, 
`Project1`.`C1` AS `C3`
FROM (SELECT
`Extent1`.`SystemAreaId`, 
`Extent1`.`SystemAreaCode`, 
`Extent1`.`SystemAreaType`, 
`Extent1`.`SystemAreaDescription`, 
`Extent1`.`SystemAreaCreatedDate`, 
`Extent1`.`SystemAreaUpdateDate`, 
`Extent1`.`SystemAreaStatus`, 
(SELECT
COUNT(1) AS `A1`
FROM `systemareafunctionality` AS `Extent2`
 WHERE `Extent1`.`SystemAreaCode` = `Extent2`.`SystemAreaCode`) AS `C1`
FROM `systemarea` AS `Extent1`) AS `Project1` LEFT OUTER JOIN (SELECT
`Extent3`.`SystemAreaCode`, 
1 AS `C1`
FROM `systemareafunctionality` AS `Extent3`) AS `Project2` ON `Project1`.`SystemAreaCode` = `Project2`.`SystemAreaCode`) AS `Project3`
 ORDER BY 
`Project3`.`SystemAreaCode` ASC, 
`Project3`.`C2` ASC

JSON 输出:

[{"SystemAreaId":1,"SystemAreaCode":"KIO","SystemAreaType":"KIOSK","SystemAreaDescription":"任务 相关 接待员","SystemAreaCreatedDate":"/Date(1543421018000)/","SystemAreaUpdateDate":"/Date(1543421018000)/","SystemAreaStatus":true,"SystemAreaFunctionality":[],"Count":1}]

PS:请不要推荐自动映射器或扩展方法。谢谢!

【问题讨论】:

  • sa.systemareafunctionality 是 IQueryable 吗?
  • 没有它的ICollection
  • 如果它不存在,你会打赌一个错误。添加 ToList() : SystemAreaFunctionality = sa.systemareafunctionality.Select(e => new SystemAreaFunctionality { SystemAreaCode =e.SystemAreaCode }).ToList()
  • 这是什么 EF(6 或 Core)?
  • @IvanStoev 很抱歉延迟回答,我已经发布了我的经历并做出了相应的回答。也请随时纠正我。

标签: c# mysql entity-framework linq


【解决方案1】:

使用这个:

CurrentContext.systemarea.Include('systemareafunctionality')

IEnumerable<SystemArea> result = (from sa in CurrentContext.systemarea    
                             join systemareafunctionality in CurrentContext.systemareafunctionality on sa.systemareafunctionalityID equals systemareafunctionality.ID
                             select new SystemArea
                             {
                                 SystemAreaId = sa.SystemAreaId,
                                 SystemAreaCode = sa.SystemAreaCode,
                                 SystemAreaType = sa.SystemAreaType,
                                 SystemAreaDescription = sa.SystemAreaDescription,
                                 SystemAreaCreatedDate = sa.SystemAreaCreatedDate,
                                 SystemAreaUpdateDate = sa.SystemAreaUpdateDate,
                                 SystemAreaStatus = SystemAreaStatus,
                                 Count = systemareafunctionality.Count,
                                 SystemAreaFunctionality = systemareafunctionality.Select(e => new SystemAreaFunctionality { SystemAreaCode =e.SystemAreaCode })
                             }).ToList();

IEnumerable<SystemArea> result = (from sa in CurrentContext.systemarea    
                         join systemareafunctionality in CurrentContext.systemareafunctionality on sa.systemareafunctionalityID equals systemareafunctionality.ID into item1 from subitem1 in item1.DefaultIfEmpty() 
                         select new SystemArea
                         {
                             SystemAreaId = sa.SystemAreaId,
                             SystemAreaCode = sa.SystemAreaCode,
                             SystemAreaType = sa.SystemAreaType,
                             SystemAreaDescription = sa.SystemAreaDescription,
                             SystemAreaCreatedDate = sa.SystemAreaCreatedDate,
                             SystemAreaUpdateDate = sa.SystemAreaUpdateDate,
                             SystemAreaStatus = SystemAreaStatus,
                             Count = systemareafunctionality.Count,
                             SystemAreaFunctionality = systemareafunctionality.Select(e => new SystemAreaFunctionality { SystemAreaCode =e.SystemAreaCode })
                         }).ToList();

【讨论】:

  • 我认为 join 在这里没有任何区别,因为我在使其成为普通选择查询之前尝试了 join。
  • 您是否尝试过这样的左连接:在 CurrentContext.systemareafunctionality 中加入 systemareafunctionality on sa.systemareafunctionalityID 等于 systemareafunctionality.ID 从 item1.DefaultIfEmpty() 中的 subitem1 进入 item1
  • 是的,我做到了,无论如何它是一个子表,所以 ef 上下文将在其父表中包含数据
  • 您是否尝试将 LazyLoadingEnabled 设置为 true?
【解决方案2】:

显然你的CurrentContext 是一个Dbcontext 至少有一个SystemAreas 的表和一个SystemAreaFunctionalities 的表。

似乎每个SystemArea 都有零个或多个SystemAreaFunctionalities;每个SystemAreaFunctionality 都恰好属于一个SystemArea,这是一种使用外键的简单的一对多关系。

注意:可能是多对多关系,答案会类似

唉,你忘了写你的课程,所以我试一试:

class SystemArea
{
    public int Id {get; set;}
    ... // other properties

    // every SystemArea has zero or more SystemAreaFunctionalities (one-to-many)
    public virtual ICollection<SystemAreaFunctionality> SystemAreaFunctionalities {get; set;}
}

class SystemAreaFunctionality
{
    public int Id {get; set;}
    ... // other properties

    // every SystemAreaFunctionality belongs to exactly one SystemArea, using foreign key
    public int SystemAreaId {get; set;}
    public virtual SystemArea SystemArea {get; set;}
}

在实体框架中,表的列由非虚拟属性表示,虚拟属性表示表之间的关系。 (一对多,多对多,...)

为了完整性:

class CurrentContext : DbContext
{
    public DbSet<SystemArea> SystemAreas {get; set;}
    public DbSet<SystemAreaFunctionality> SystemAreaFunctionalities {get; set;}
}

如果人们想要带有子项目的项目,例如Schools 和他们的StudentsCustomers 和他们的Orders 等等,人们倾向于执行(group)Join。但是,很少需要使用实体框架连接。改为使用 ICollections。实体框架知道关系并知道要执行哪个(组)加入。

我经常看到人们使用Include,但如果你这样做,你会选择完整的对象,效率不高。假设你有一个SystemArea,Id = 10,1000 SystemAreaFunctionalities,你知道每个SystemAreaFunctionality 都有一个外键SystemAreaId,值为10。而不是将这个值作为@987654341 的主键只发送一次@, Include 也会选择所有 1000 个外键,这个值为 10。多么浪费处理能力!

查询数据时,始终使用 Select 并仅选择您实际计划使用的属性。仅当您计划更新包含的对象时才使用 Include。

你写道:

SystemAreaFunctionality 是我在这里尝试转换的子对象...

不清楚你真正想要什么。您想要所有使用过的 SystemAreaCodes 的集合吗?或者你真的想要一个只填充一个字段的新 SystemAreaFunctionalities 集合:SystemAreaCode?由于您使用单数属性名称,您似乎不想要一个集合,而只想要一个项目。

var result = currentContext.SystemAreas.Select(systemArea => new
{
     Id = systemArea.Id,
     Code = systemArea.Code,
     ...

     // if you want a collection of SystemAreaFunctionalities
     // where every SystemAreaFunctionality is filled with only SysemAreaCode
     // do the following:
     SystemAreaFunctionalities = systemArea.SystemAreaFunctionalities
          .Select(systemAreaFunctionality => new SystemAreFunctionality
          {
               SystemAreaCode = systemAreaFunctionality.SystemAreaCode,
          })
          .ToList(),   // DON'T FORGET THIS, OR YOU WON'T GET RESULTS!
    })
    .ToList()
}

我认为您的 SystemAreaFunctionalities 为空的原因是因为您忘记执行 ToList()。

因为您使用了 ToList(),所以您会自动获得所选 SystemAreaFunctionalities 的计数。不需要单独选择这个Count。

数据库查询中较慢的部分之一是将所选数据从数据库管理系统传输到本地进程。最好只选择您实际计划使用的数据

您的查询效率不高,因为您选择了完整的 SystemAreaFunctionalities 并仅填写 SystemAreaCode。所有其他字段将使用默认值填充。除了较慢的查询之外,您还给调用者一种印象,即他们得到了正确填充的 SystemAreaFunctionalities。改进的版本是:

var result = currentContext.SystemAreas.Select(systemArea => new
{
     // select only the properties you actually plan to use
     Id = systemArea.Id,
     Code = systemArea.Code,
     ...


     // if you only need the SystemAreaCodes, select only that property
     SystemAreaCodes = systemArea.SystemAreaFunctionalities
          .Select(systemAreaFunctionality => systemAreaFunctionality.SystemAreaCode)
          .ToList(),
    })
    .ToList()
};

当然,如果您需要 mrre 不仅仅是 SystemAreaCodes,而是几个 SystemAreaFunctionalities,请继续,选择它们:

...
SystemAreaFunctionalities = systemArea.SystemAreaFunctionalities
    .Select(systemAreaFunctionality => new
    {
        // again: select only the properties you plan to use!
        Id = systemAreaFunctionality.Id
        SystemAreaCode = systemAreaFunctionality.SystemAreaCode,
     })

【讨论】:

  • 试过你提到的并没有产生任何结果,SystemArea 只与 SystemAreaFunctionality 有一对多的关系,我的查询也很完美,因为我需要所有可用的信息,我只是没有将其添加到相关代码中。
【解决方案3】:

意见:

我花了两天时间让 MySQL(最新版本)与 EF 一起工作,相信我这是很辛苦的,相反,EF 与 MSSQL 是如此简单且易于实现。

我经历过的一件事是,Oracle 对提供免费版本的 MySQL 不感兴趣,因此他们对新版本的文档很草率,并且提供不稳定的 .NET 连接器。

实际答案:

EF 的行为很奇怪,只有当我要求 EF 加载子实体的子实体(即 SystemAreaFunctionalityEmployeeRoleMapping,它是 SystemAreaFunctionality 的子实体)时,它才会加载子实体(SystemAreaFunctionality)中的数据,这也意味着我不得不采取不必要的数据。

所以我的链接查询看起来像这样:

var result = (from sa in CurrentContext.systemarea
                          select new SystemArea
                          {
                              SystemAreaId = sa.SystemAreaId,
                              SystemAreaType = sa.SystemAreaType,
                              Count = sa.systemareafunctionality.Count,
                              SystemAreaFunctionalities = sa.systemareafunctionality.Select(saf => new SystemAreaFunctionality
                              {
                                  SystemAreaId = saf.SystemAreaId,
                                  SystemAreaFunctionalityController = saf.SystemAreaFunctionalityController,
                                  SystemAreaFunctionalityAction = saf.SystemAreaFunctionalityAction,
                                  SystemAreaFunctionalityType = saf.SystemAreaFunctionalityType,
                                  SystemAreaFunctionalityEmployeeRoleMappings = saf.systemareafunctionalityemployeerolemapping.Select(saferm => new SystemAreaFunctionalityEmployeeRoleMapping
                                  {
                                      SystemAreaFunctionalityEmployeeRoleMappingId = saferm.SystemAreaFunctionalityEmployeeRoleMappingId,
                                      SystemAreaFunctionalityCreatedDate = saferm.SystemAreaFunctionalityCreatedDate
                                  })
                              })
                          }).ToList();

或者:

这次尝试使用相同的 linq 查询(发布在 OP 中)和不同的数据库,使用 PostgreSQL 和 npgsql 连接器,令人惊讶的是,EF 为我提供了我想要的东西而没有额外的包袱。 最重要的是,PostgreSQL 使用 EF 提供了比 MySQL 更好的性能。所以我认为切换到 PostgreSQL 会是一个更好的选择。

PS: 如果您决定使用开源 DBMS,请在使用 MySQL 之前参考此内容:

【讨论】:

    【解决方案4】:

    包含应该像这样为您解决问题:

        IEnumerable<SystemArea> result = (from sa in CurrentContext.systemarea.Include("systemareafunctionality")                                 
                                 select new SystemArea
                                 {
                                     SystemAreaId = sa.SystemAreaId,
                                     SystemAreaCode = sa.SystemAreaCode,
                                     SystemAreaType = sa.SystemAreaType,
                                     SystemAreaDescription = sa.SystemAreaDescription,
                                     SystemAreaCreatedDate = sa.SystemAreaCreatedDate,
                                     SystemAreaUpdateDate = sa.SystemAreaUpdateDate,
                                     SystemAreaStatus = sa.SystemAreaStatus,
                                     Count = sa.systemareafunctionality.Count,
                                     SystemAreaFunctionality = sa.systemareafunctionality.Select(e => new SystemAreaFunctionality { SystemAreaCode =e.SystemAreaCode })
                                 }).ToList();
    

    【讨论】:

      猜你喜欢
      • 2016-06-10
      • 2019-04-22
      • 1970-01-01
      • 2014-07-20
      • 2021-12-15
      • 2018-09-14
      • 1970-01-01
      • 1970-01-01
      • 2015-04-26
      相关资源
      最近更新 更多