运行Linq语句时出现Timeout异常。

4

这个问题刚开始出现。该语句已经运行了几个月,现在我一直收到下面的超时错误。当我在SSMS上直接执行相同的语句时,它在一秒钟内返回。该表有44000条记录,并且对5列进行了索引,其中一个是州。

select distinct(state) from [ZipCodeDatabase]

我正在运行以下Linq语句

states = ZipCodeRepository.Get(orderBy: z => z.OrderBy(o => o.State)).Select(z => z.State).Distinct().ToList();

当我运行这个linq语句时,我一直得到超时错误,而且不知道为什么,因为之前是正常工作的。
我包括了Get()函数,它是一个通用的仓储函数,但也许我漏掉了什么,导致延迟。
获取函数:
public virtual IEnumerable<TEntity> Get(
Expression<Func<TEntity, bool>> filter = null,
Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
string includeProperties = "") //params Expression<Func<TEntity, object>>[] includes
{
    IQueryable<TEntity> query = 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).ToList();
    }
    else
    {
        return query.ToList();
    }
}

超时错误:

System.Data.SqlClient.SqlException (0x80131904):操作未完成或服务器无响应,超时时间已过。---> System.ComponentModel.Win32Exception (0x80004005):等待操作超时。 at System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection, Action1 wrapCloseInAction) at System.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection, Action 1 wrapCloseInAction) at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj, Boolean callerHasConnectionLock, Boolean asyncClose) at System.Data.SqlClient.TdsParser.TryRun(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj, Boolean& dataReady) at System.Data.SqlClient.SqlDataReader.TryCloseInternal(Boolean closeReader) at System.Data.SqlClient.SqlDataReader.Close() at System.Data.Common.DbDataReader.Dispose(Boolean disposing) at System.Data.Common.DbDataReader.Dispose() at System.Data.Common.Internal.Materialization.Shaper1.Finally() at System.Data.Common.Internal.Materialization.Shaper1.SimpleEnumerator.Dispose() at System.Collections.Generic.List1..ctor(IEnumerable1 collection) at System.Linq.Enumerable.ToList[TSource](IEnumerable1 source) at ClientsToProfitsKendoUI.DAL.GenericRepository1.Get(Expression1 filter, Func2 orderBy, String includeProperties)

注:此处的超时错误是指数据访问中的操作未能在规定的时间内完成,导致连接被中断。

2
这通常意味着SQL中正在进行类型转换。当将datetime转换为nvarchar时,我曾经看到过这种情况发生。在查询窗口中少于1毫秒的操作,在通过代码发送时会超时。唯一的检查方法是使用查询分析器并查看发生了什么。 - Simon Whitehead
3
你是不是因为需要获取州名而将全部的4.4万条记录都实例化了? - McAden
2个回答

5
你的get方法总是调用ToList,这将在此时材料化查询。也就是说,在执行任何过滤之前,它将从数据库中获取所有44k的记录。
你还在选择任何数据之前按照州排序,这是对任意数据进行排序的无用练习。
从你的Get方法中删除ToList调用。通过先选择State,然后调用Distinct,最后排序来优化查询。这样允许数据库以最佳方式进行操作。
如果你需要完全材料化的列表,请在最后调用ToList。但在此之前,请继续使用查询返回的IQueryable。这样你就可以尽可能地将 Entity Framework 的负担转移给数据库。

1
谢谢,我也遵循了这个方法,并从 Get 方法中删除了所有 to list。 - ChampChris

2
你正在将数据库中的每一行都具体化以选择不同的状态。让数据库为你完成繁重工作,只需返回状态,而无需实现所有内容,然后由 .Net 完成。
请在 ZipCodeRepository 中添加以下内容。
public IEnumerable<string> GetStates()
{
    return dbSet.OrderBy(e => e.State).Select(e => e.State).Distinct();
}

请注意,导致材料化(和大量开销)的原因是ToList()。通常情况下,将集合保留为IQueryable,直到您真正准备使用结果为止。一旦您实际枚举集合,EF将为您执行查询。或者,如果您删除ToList()调用并更改Get函数的返回类型为IQueryable<TEntity>,也应该解决此问题。

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