我正在实现一个工作单元模式(不是我的选择,我知道它被一些人认为是反模式)。
我遇到了一个我不能完全理解的情况。
我的通用repo构造函数:
this.context = context;
this.dbSet = context.Set<T>();
通用方法:
public virtual async Task<IEnumerable<T>> All()
{
return await dbSet.ToListAsync();
}
像这样使用:
var languages = await _unitOfWork.Languages.All();
languages = languages.OrderBy(x => x.Order);
如上面所示,我需要往下走一行,这样我就可以使用OrderBy,我不明白为什么。
第二个问题是,ToListAsync
应该返回一个列表,为什么我得到一个IEnumerable
?
第二个问题是,ToListAsync应该返回一个列表,为什么我得到一个IENUMERABLE?
List
是IEnumerable
的实现。检查"程序到接口"是什么意思
如上所示,我需要往下走一行,这样我就可以使用OrderBy,我不知道为什么。
_unitOfWork.Languages.All()
返回一个Task
,你应该得到IEnumerable
的解包裹结果来应用OrderBy
。
要使此工作如您所期望的那样,您应该在等待的结果上应用OrderBy
:
(await _unitOfWork.Languages.All()).OrderBy(x => x.Order);
你的困惑,只要有他们在同一行似乎是如何应用await
。这应该可以正常工作:
var languages = (await _unitOfWork.Languages.All()).OrderBy(x => x.Order);
您需要这样做,因为您的All
函数返回Task
,而OrderBy
不是。
另外,你可能一开始就不想调用.ToListAsync()
,那只是一个没有任何限制的SELECT *
或where子句,可能不是"世界末日"。但往往会对你的表现极为不利。
至于你的第二个问题,为什么它返回IEnumerable<T>
,这是因为List<T>
实现了IEnumerable<T>
,你的函数签名表明它返回IEnumerable<T>
public virtual async Task<IEnumerable<T>> All()
我强烈建议不要处理.ToListAsync
或IEnumerable<T>
,让IQueryable<T>
对数据库做正确的工作。
编辑在评论中有人问为什么使用IQueryable<T>
很重要。答案是因为利用.Where
,.Any()
等…对IQueryable
的查询将根据DB建模底层查询。
例如,假设您要查找具有123
的Id
的单个实体。如果您留下您的.ToListAsync()
没有任何其他修改,它将拉回数据库中的每一行到您的程序在内存中,然后迭代每一行寻找那一行。
如果,相反,你使用IQueryable<T>
-在你应用.FirstOrDefault(e => e.Id == 123)
的地方,它将应用于数据库,它将像SELECT TOP(1) * FROM MyEntity WHERE [Id] = 123
一样应用-拉回单行而不是现有的每一行。
EDIT2注意,这也适用于投影。这意味着像.Select(e => new { FullName = e.FirstName + " " + e.LastName, State = e.Address.State})
这样的东西只会拉这2列,而不是所有列和任何导航属性,你包括。在.ToList()
或.ToListAsync
之后这样做将拉回所有列/实体并迭代它们以创建一组全新的其他实体。这可能导致两种方法之间存在巨大的CPU/内存差异。