【问题标题】:using IQueryable deferred execution使用 IQueryable 延迟执行
【发布时间】:2014-02-21 00:24:33
【问题描述】:

我正在研究一个简单的映射 EntityFramework DTO's ,它可以完美地工作,除了延迟执行,我有以下代码:

public abstract class Assembler<TDto, TEntity> : IAssembler<TDto, TEntity>
    where TEntity : EntityBase , new ()
    where TDto : DtoBase, new ()
{
    public abstract TDto Assemble(TEntity domainEntity);
    public abstract TEntity Assemble(TEntity entity, TDto dto);

    public virtual IQueryable<TDto> Assemble(IQueryable<TEntity> domainEntityList)
    {
        List<TDto> dtos = Activator.CreateInstance<List<TDto>>();
        foreach (TEntity domainEntity in domainEntityList)
        {
            dtos.Add(Assemble(domainEntity));
        }
        return dtos.AsQueryable();
    }

    public virtual IQueryable<TEntity> Assemble(IQueryable<TDto> dtoList)
    {
        List<TEntity> domainEntities = Activator.CreateInstance<List<TEntity>>();
        foreach (TDto dto in dtoList)
        {
            domainEntities.Add(Assemble(null, dto));
        }
        return domainEntities.AsQueryable();
    }
}

示例汇编器:

public partial class BlogEntryAssembler : Assembler<BlogEntryDto, BlogEntry>, IBlogEntryAssembler
{
    public override BlogEntry Assemble(BlogEntry entity, BlogEntryDto dto)
    {
        if (entity == null)
        {
            entity = new BlogEntry();
        }
        /*
        entity.Id = dto.Id;
        entity.Created = dto.Created;
        entity.Modified = dto.Modified;
        entity.Header = dto.Header;
        */
        base.MapPrimitiveProperties(entity, dto);

        this.OnEntityAssembled(entity);
        return entity;
    }

    public override BlogEntryDto Assemble(BlogEntry entity)
    {
        BlogEntryDto dto = new BlogEntryDto();

        //dto.Id = entity.Id;
        //dto.Modified = entity.Modified;
        //dto.Created = entity.Created;
        //dto.Header = entity.Header;

        base.MapPrimitiveProperties(dto, entity);

        dto.CategoryName = entity.Category.Name;
        dto.AuthorUsername = entity.User.Username;
        dto.AuthorFirstName = entity.User.FirstName;
        dto.AuthorLastName = entity.User.LastName;

        dto.TagNames = entity.Tags.Select(t => t.Name)
            .ToArray();

        dto.TagIds = entity.Tags.Select(t => t.Id)
            .ToArray();

        dto.VotedUpUsernames = entity.BlogEntryVotes.Where(v => v.Vote > 0)
            .Select(t => t.User.Username)
            .ToArray();

        dto.VotedDownUsernames = entity.BlogEntryVotes.Where(v => v.Vote < 0)
            .Select(t => t.User.Username)
            .ToArray();

        // Unmapped
        dto.FileCount = entity.BlogEntryFiles.Count();
        dto.CommentCount = entity.BlogEntryComments.Count();
        dto.VisitCount = entity.BlogEntryVisits.Count();
        dto.VoteCount = entity.BlogEntryVotes.Count();
        dto.VoteUpCount = entity.BlogEntryVotes.Count(v => v.Vote.Equals(1));
        dto.VoteDownCount = entity.BlogEntryVotes.Count(v => v.Vote.Equals(-1));
        dto.VotePuntuation = entity.BlogEntryVotes.Sum(v => v.Vote);
        dto.Published = entity.Visible && entity.PublishDate <= DateTime.Now;

        this.OnDTOAssembled(dto);
        return dto;
    }
}

我的服务等级:

    public virtual PagedResult<BlogEntryDto> GetAll(bool includeInvisibleEntries, string tag, string search, string category, Paging paging)
    {
        var entries = this.Repository.GetQuery()
            .Include(b => b.Tags)
            .Include(b => b.User)
            .Include(b => b.Category)
            .Include(b => b.BlogEntryFiles)
            .Include(b => b.BlogEntryComments)
            .Include(b => b.BlogEntryPingbacks)
            .Include(b => b.BlogEntryVisits)
            .Include(b => b.BlogEntryVotes)
            .Include(b => b.BlogEntryImages)
           .AsNoTracking();

        if (!includeInvisibleEntries)
        {
            entries = entries.Where(e => e.Visible);
        }

        if (!string.IsNullOrEmpty(category))
        {
            entries = entries.Where(e => e.Category.Name.Equals(category, StringComparison.OrdinalIgnoreCase));
        }

        if (!string.IsNullOrEmpty(tag))
        {
            entries = entries.Where(e => e.Tags.Count(t => t.Name.Equals(tag, StringComparison.OrdinalIgnoreCase)) > 0);
        }

        if (!string.IsNullOrEmpty(search))
        {
            foreach (var item in search.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries))
            {
                entries = entries.Where(e => e.Header.Contains(item));
            }
        }

        return this.Assembler.Assemble(entries).GetPagedResult(paging);
    }

当我调用 GetAll 方法时,它返回并将表中的所有实体转换为 Dto,然后才分页生成的集合,当然这不是我所期望的。我想在我的 Assemble 中执行代码分页完成后的方法,有什么想法吗?

PS : 我知道我可以使用 Automapper,只是想了解内部结构。

【问题讨论】:

    标签: c# entity-framework automapper dto mapper


    【解决方案1】:

    在您的汇编程序中,您将投影的 DTO 添加到 List&lt;TDto&gt;。这在两个方面是有害的。首先是强制执行,因为列表被填满然后返回。其次,在那一刻,您从 LINQ 切换到实体,再从 LINQ 切换到对象,并且没有退路。您可以通过AsQueryable 再次将列表转换为IQueryable,但这不会重新注入EF 查询提供程序。事实上,转换是没有用的。

    这就是 AutoMapper 的 ProjectTo&lt;T&gt; 声明如此酷的原因。它将To 之后的表达式一直传输回原始IQueryable,因此,它的查询提供程序。如果这些表达式包含分页语句 (Skip/Take),它们将被翻译成 SQL。所以我认为你很快就会得出结论,毕竟你最好使用 AutoMapper。

    【讨论】:

      【解决方案2】:
      public virtual IQueryable<TDto> Assemble(IQueryable<TEntity> domainEntityList)
      {
          List<TDto> dtos = Activator.CreateInstance<List<TDto>>();
          foreach (TEntity domainEntity in domainEntityList)
          {
              dtos.Add(Assemble(domainEntity));
          }
          return dtos.AsQueryable();
      }
      

      上述代码中的 foreach 循环是您在数据库服务器上执行查询的点。

      从这一行可以看出:

      返回 this.Assembler.Assemble(entries).GetPagedResult(paging);

      此方法在 GetPagedResult(paging) 之前被调用。所以这就是在整个结果集上发生分页的原因。

      您应该了解枚举查询 (foreach) 需要运行查询。您的 foreach 循环会处理该查询返回的每条记录。此时 Paging 方法无法阻止它为时已晚!

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2019-12-05
        • 2011-02-16
        • 1970-01-01
        • 1970-01-01
        • 2011-03-04
        • 2011-05-07
        • 2011-01-25
        相关资源
        最近更新 更多