选择top1 with Linq

时间:2015-11-27 20:59:38

标签: c# sql-server asp.net-mvc linq unit-of-work

实际上我正在使用Linq和UOW(工作单元),而且我正在使用linq轻松访问bbdd。我知道如果我想获得一个表的第一行,我可以这样做:

int test4 = (from p 
             in uow.ProductR.context.product 
             where p.Id == 1715 select p.Id).FirstOrDefault();

这将在SQL Server中执行:

SELECT TOP (1) 
    [Extent1].[Id] AS [Id]
    FROM [dbo].[product] AS [Extent1]
    WHERE 1715 = [Extent1].[Id]

我的问题是,我可以使用LINQ对我的UOW的通用存储库进行反对吗?我的意思是,当我执行

int test2 = uow.ProductR.Get(p => p.Id == 1715).Select(p => p.Id).FirstOrDefault();

或者

var test3 = uow.ProductR.Get(p => p.Id == 1715).Select(p => new { p.Id });

在SQL Server中我得到:

SELECT 
[Extent1].[Id] AS [Id], 
[Extent1].[Name] AS [Name], 
FROM [dbo].[product] AS [Extent1]
WHERE 1715 = [Extent1].[Id]

当然,用第二种方式,当数据库有500k行时,它会很慢。 (我有更多专栏,不仅仅是2个)

编辑:这是带有GET声明的类

public class GenericRepository<TEntity> : IGenericRepository<TEntity> where TEntity : class
{

    internal contextEntities context;
    internal DbSet<TEntity> dbSet;

    public GenericRepository(contextEntities context)
    {
        this.context = context;
        this.dbSet = context.Set<TEntity>();
    }

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

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

        foreach (var includeProperty in includeProperties.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
        {
            query = query.Include(includeProperty);
        }

        if (orderBy != null)
        {
            return orderBy(query).AsQueryable();
        }
        else
        {
            return query.AsQueryable();
        }
    }
}

我希望我能解释清楚。

2 个答案:

答案 0 :(得分:5)

Get需要像现在一样返回IQueryable,而不是IEnumerable。然后,Get的参数也变得无用,因为调用者只能执行Get().Where(...)。 API表面变得更清晰,因为您可以删除参数。

但是你正在失去对数据库查询方式的控制。我假设你正在为测试目的做一个存储库(如果不是它可能是一个不好的选择)。测试以这种方式执行的查询会变得更难。

答案 1 :(得分:0)

返回IQueryable将为您提供更大的灵活性,但它也暴露了随意修改您在存储库中定义的查询的可能性。如果您想为使用您的存储库返回前N行的人提供标准机制,您可以添加一些额外的可选属性(请注意,如果您还希望允许通过您的分页,则Take与Skip一起使用是一种有用的机制存储库,无需创建单独的机制或公开底层的IQueryable)。

您可以将Get方法签名更改为以下内容:

public virtual IEnumerable<TEntity> Get(
    Expression<Func<TEntity, bool>> filter = null,
    Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
    string includeProperties = "", int? maxResults = null)

然后在你的Get方法中(这应该被重构,但你明白了):

    if (orderBy != null)
    {
        return maxResults.HasValue() ? orderBy(query).Take((int)maxResults).ToList() : orderBy(query).ToList();
    }
    else
    {
        return maxResults.HasValue() ? query.take((int)maxResults).ToList() : query.ToList();
    }

请注意,由于IOrderedQueryable和IQueryable都实现了IEnumerable,因此不需要调用AsQueryable()。相反,调用ToList()以实现结果集以便它执行。