LINQ性能比较

3

我正在使用Entity Framework,并想知道以下代码是否有任何区别。第一和第二个代码似乎都获取了所有项目,然后通过Entidad.Nombre ==“Empresa”进行过滤,最后一个代码似乎只获取了Entidad.Nombre ==“Empresa”的项目。 我错了吗?哪个更好?

var listFields = from b in unitOfWork.PropiedadRepository.Get()
                         where b.Entidad.Nombre == "Empresa"
                         select b;
var listFields2 = unitOfWork.PropiedadRepository.Get().Where(x => x.Entidad.Nombre == "Empresa");
var listFields3 = unitOfWork.PropiedadRepository.Get(x => x.Entidad.Nombre == "Empresa");

这是通用存储库类。所有的存储库都继承自这个类。
public sealed class GenericRepository<TEntity> where TEntity : class
{
    private readonly ConfigurationDbDataContext _context;
    private readonly DbSet<TEntity> _dbSet;

    public GenericRepository(ConfigurationDbDataContext context)
    {
        _context = context;
        _dbSet = context.Set<TEntity>();
    }

    public IEnumerable<TEntity> Get(
        Expression<Func<TEntity, bool>> filter = null,
        Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
        string includeProperties = "")
    {
        IQueryable<TEntity> query = _dbSet;

        if (filter != null)
        {
            query = query.Where(filter);
        }

        query = includeProperties.Split(new[] {','}, StringSplitOptions.RemoveEmptyEntries).Aggregate(query, (current, includeProperty) => current.Include(includeProperty));

        // ReSharper disable once ConvertIfStatementToReturnStatement
        if (orderBy != null)
        {
            return orderBy(query).ToList();
        }
        else
        {
            return query.ToList();
        }
    }

    public TEntity GetById(object id)
    {
        return _dbSet.Find(id);
    }

    public void Insert(TEntity entity)
    {
        _dbSet.Add(entity);
    }

    public void Delete(object id)
    {
        var entityToDelete = _dbSet.Find(id);
        Delete(entityToDelete);
    }

    public void Delete(TEntity entityToDelete)
    {
        if (_context.Entry(entityToDelete).State == EntityState.Detached)
        {
            _dbSet.Attach(entityToDelete);
        }
        _dbSet.Remove(entityToDelete);
    }

    public void Update(TEntity entityToUpdate)
    {
        _dbSet.Attach(entityToUpdate);
        _context.Entry(entityToUpdate).State = EntityState.Modified;
    }
}

通常最好在数据库端进行过滤,以避免返回数据的开销,因此最后一个选项似乎是最佳选择。 - juharr
3个回答

5

你说得没错。

在最后一种情况中,linq to entities将where子句的评估结果交给数据服务器,客户端只接收经过筛选的数据。

在其他情况下,客户端接收全部数据,然后再对其进行筛选(linq to object)。

那么,我们谈论的性能是什么?CPU、网络、客户端、服务器端?

通常情况下,人们更愿意让服务器进行筛选,所以使用linq to entities,但这实际上取决于一些参数和目标。

请注意,根据我的个人经验,linq to entitieslinq to object之间最令人惊讶的不同之处是linq to object区分大小写。而对于linq to entities来说,则取决于数据库/表/列排序规则。

此外,正如Kaspars提醒的那样,对尾随空格的处理也是不同的。


3
不仅如此。如果您正在使用 SQL Server,另一个有趣的区别是如何处理尾随空格。在 Linq to Entities 中,dbSet.Where(x => x.Name == "test")dbSet.Where(x => x.Name == "test ") 将给出相同的结果,因为 SQL Server 忽略尾随空格,但是在 Linq to Objects 中,它们将给出不同的结果,因为尾随空格不会被忽略。 - Kaspars Ozols

3
var listFields3 = unitOfWork.PropiedadRepository
                            .Get(x => x.Entidad.Nombre == "Empresa");

最后一种情况更好。Entity Framework 推迟执行 SQL 查询,直到您采取某些措施将结果集实现化为止。它查看整个表达式,然后基于此构建查询。
在您的Get()方法中,您调用ToList(),这确实将结果集实现化。因此,如果您调用.Get().Where(...),则首先会调用没有过滤器的Get(),因为Get()调用了ToList(),它将所有数据拉回内存,然后针对该数据执行Where()
在最后一种情况下,在调用ToList()之前在您的Get()方法中调用了Where()。这意味着 where 子句将用于构建一个 SQL 查询,以在 SQL Server 上过滤数据,而不是在应用程序服务器上进行内存中过滤,这将更加高效。
在这里需要考虑的是,是否希望您的存储库返回IQueryable<T>而不是IEnumerable<T>。如果返回IQueryable,则语句不会执行,直到需要实现化结果集。如果Get()返回IQueryable而不是调用ToList(),则两个语句将执行相同的操作。

3

正如其他人已经说过的,第三个查询将是最快的,因为过滤是在数据库层面上完成的。只有相关记录被传输到客户端。

虽然主要问题已经得到解答,但我想提出一项基础仓库的变更,使其更具弹性。目前,您将所有可能的过滤选项传递给仓库的Get函数,并在那里应用它们。这似乎非常受限制和不必要。

如果在某些时候您想要执行其他操作(例如,GroupBy)在数据库层面上,除非您添加额外的参数到Get函数,否则您将无法这样做。

我建议将Get函数更改为GetAll并返回IQueryable。您可以在其中添加默认的过滤和排序规则。

public IQueryable<TEntity> GetAll()
{
    return _dbSet
        .Where(x => !x.IsDeleted)
        .OrderBy(x => x.Date);
}

通过这样做,您的所有查询都将始终在数据库级别执行过滤/排序/聚合。此外,这将更具弹性,因为您可以使用Linq to Entities提供的所有函数。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接