【问题标题】:Using AutoMapper to lookup related object and add it as a property使用 AutoMapper 查找相关对象并将其添加为属性
【发布时间】:2016-10-21 13:39:44
【问题描述】:

我有一个在 Entity Framework 6, Model First 上运行的应用程序,在旧数据库上运行。

在应用程序中,我们有一个Person 对象,该对象具有一个或多个Address 对象。这些使用额外的表格链接:PersonXAddress,如下所示:

Person
    Id
    Name
    ...


PersonXAddress
    PersonId
    AddressId

Address
   Id
   Street
   PostalCode
   ...

使用AutoMapper,我将一个属性添加到我的PersonDto“HomeAddres”,即带有AddressType == 1 的地址,如下所示:

Mapper.CreateMap<Person, PersonDto>()
   .ForMember(x => x.HomeAddress,
       o => o.MapFrom(y => y.PersonXAddresses
              .Where(a => a.Address.AddressTypeId == 1)
              .Select(x => x.Address).FirstOrDefault();

所以在我的代码中,我永远不必遍历所有不同的地址来找到那个人的 HomeAddress。

但是,我发现这很慢。它创建了一个巨大的 sql 查询(大约 50 行 sql),第一次运行这个查询可能需要 30 秒。第一次触发后,查询可能需要将近 1 秒才能从数据库中获取一些 Person 对象。 我发现如果我注释掉上面的映射,生成的查询会在不到一秒的时间内启动,并在几毫秒内返回结果。

有没有更好的自动查找 HomeAddress 的方法?你能帮我改进一下吗?

非常感谢

【问题讨论】:

  • 如果您创建外键引用,您将获得可用的地址。你没有写任何额外的代码来获取地址。
  • 从个人到地址的外键是不可能的,因为可能有多个地址。额外的代码是为了确保我不必每次都迭代每个 PersonXAddress 对象来找到 HomeAddress
  • 您可以使用多对多关系,这可能有助于包含两个数据库主键的第三类
  • 如果一个地址可能属于多个人,如何将其键入为“家庭地址”?如果它属于拥有另一个家庭住址的其他人怎么办?也许“家庭地址”标志应该在路口记录中。
  • 我还怀疑这是一个索引问题,而不是 SQL 生成问题。考虑到SELECT 中的每一列占用一行,50 行实际上并没有那么多。

标签: c# asp.net sql-server entity-framework-6 automapper


【解决方案1】:

您看起来有建模问题。您的 Person 模型(和数据库表)上应该有一个 HomeAddress。您在该 LINQ 查询中假设有零个或一个家庭地址。只需在数据库级别明确建模即可。

【讨论】:

    【解决方案2】:

    尝试只使用 LINQ:

    var personDTO = context.Person
                   .Where(p => p.Id == 1)
                   .Select(p => new {
                      person = p,
                      homeAddress = p.PersonXAddresses
                                     .FirstOrDefault(px => px.Address.AddressTypeID == 1)
                                     .Address })
                   .Select(pd > new PersonDto {
                       Id = person.Id
                       Name = person.Name,
                       ...
                       HomeAddress = homeAddress
                   }).ToList();
    

    另外,请确保您有正确的索引(AddressTypeID 等)。

    【讨论】:

      【解决方案3】:

      现在可以使用 Automapper 可查询扩展库中的 AutoMapper ProjectTo&lt;&gt; 扩展来解决这个问题。

      您的查询现在看起来像这样(为方便起见,使用 EF Plus IncludeFilter 扩展,并假设您的 EF 架构已抽象出交叉引用表):

      context.Persons
             .IncludeFilter(p => p.Addresses.Where(a => a.AddressTypeId == 1))
             .ProjectTo<PersonDTO>();
      

      您的地图将如下所示:

      Mapper.CreateMap<Person, PersonDTO>()
            .ForMember(x => x.HomeAddress,
                       o => o.MapFrom(y => y.Addresses.FirstOrDefault()));
      

      使用ProjectTo&lt;&gt; 扩展的优点是对数据库本身的查询将只包含映射所需的字段,避免了超额订阅问题。与往常一样,请务必为表连接编制索引以提高性能。

      【讨论】:

        猜你喜欢
        • 2020-09-01
        • 1970-01-01
        • 2012-12-15
        • 2021-06-07
        • 1970-01-01
        • 2021-12-07
        • 2020-10-31
        • 2019-12-27
        • 2021-02-19
        相关资源
        最近更新 更多