简体   繁体   English

使用IQueryable延迟执行

[英]using IQueryable deferred execution

I am working on a simple mapping EntityFramework <> DTO's , it's working perfecly excepto for the deferred execution , I have the following code : 我正在一个简单的映射EntityFramework <> DTO上,它正为延迟执行而完美地工作,我有以下代码:

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();
    }
}

Sample Assembler : 样本汇编器:

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;
    }
}

my service class : 我的服务等级:

    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);
    }

When I call the GetAll method it returns and converts all the entities in the table to Dto's and only then it pages the resulting collection, of course that's not what I was expecting.. I would like to execute the code inside my Assemble method once the paging has been done, any idea? 当我调用GetAll方法时,它返回并将表中的所有实体转换为Dto,然后才分页结果集合,这当然不是我所期望的。.我想在分页已经完成,有什么主意吗?

PS : I know I could use Automapper, just trying to learn the internals. PS:我知道我可以使用Automapper,只是尝试学习内部知识。

In your assembler you add the projected DTOs to a List<TDto> . 在汇编器中,将投影的DTO添加到List<TDto> That's detrimental in two ways. 这从两个方面来说都是有害的。 First, it is forced execution, because the list is filled and then returned. 首先,它被强制执行,因为列表已填充,然后返回。 Second, at that moment you switch from LINQ to Entities to LINQ to objects and there is no way back. 其次,在那一刻,您从LINQ切换到Entities,再从LINQ切换到对象,并且没有退路。 You can convert the list to IQueryable again by AsQueryable , but that does not re-inject the EF query provider. 您可以通过AsQueryable再次将列表转换为IQueryable ,但这不会重新注入EF查询提供程序。 In fact, the conversion is useless. 实际上,转换是没有用的。

That's why AutoMapper's ProjectTo<T> statement is so cool. 这就是AutoMapper的ProjectTo<T>语句如此酷的原因。 It transfers expressions that come after the To all the way back to the original IQueryable and, hence, its query provider. 它将“ To之后的表达式一直传递回原始IQueryable并由此传递其查询提供程序。 If these expressions contain paging statements ( Skip/Take ) these will be translated into SQL. 如果这些表达式包含分页语句( Skip/Take ),则将它们转换为SQL。 So I think that you'll quickly come to the conclusion you better use AutoMapper after all. 因此,我认为您很快就会得出一个结论,那就是最好还是使用AutoMapper。

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();
}

The foreach loop in the above code is the point at which you're executing the query on the database server. 上面代码中的foreach循环是您在数据库服务器上执行查询的地方。

As you can see from this line: 从这一行可以看到:

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

This method is getting called before GetPagedResult(paging).. so that is the reason paging happens on the full result set. 在GetPagedResult(paging)..之前调用此方法,因此这是对整个结果集进行分页的原因。

You should understand that enumerating a query (foreach) requires the query to run. 您应该了解,枚举查询(foreach)需要运行查询。 Your foreach loop processes each and every record returned by that query.. it's too late at this point for a Paging method to do anything to stop it! 您的foreach循环处理该查询返回的每条记录。.现在,分页方法无法采取任何措施停止它了!

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

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM