我想让我的应用程序尽可能灵活,但不要让我的界面过于具体而让自己陷入困境。
存储库的最佳对象类型是什么?IEnumerable、IQueryable 还是列表?
我正在考虑使用的技术是
Azure 应用结构缓存
实体框架 4.1
可能是 Windows Server AppFabric
我想让我的应用程序尽可能灵活,但不要让我的界面过于具体而让自己陷入困境。
存储库的最佳对象类型是什么?IEnumerable、IQueryable 还是列表?
我正在考虑使用的技术是
Azure 应用结构缓存
实体框架 4.1
可能是 Windows Server AppFabric
这取决于您是否希望将来对实体执行任何查询以及这些查询是否应该在内存中:
我会说使用 IQueryable 构建你的 DAL,并传递它,确保你的对象上下文生命周期是请求。这样,您将受益于延迟执行,但面临数据库查询效率低下的风险。
然后确保您对应用程序(或至少最有可能获得流量的部分)进行性能测试并查看数据访问模式。在 DAL 中创建专门的方法来检索完全物化的对象并将这些查询作为预编译查询。
存储库接口的示例就像
public interface IDataContext
{
void Add<T>(T entity) where T : BaseEntity;
void Delete<T>(T entity) where T : BaseEntity;
IQueryable<T> Find<T>(Expression<Func<T, bool>> where) where T : BaseEntity;
}
其中 BaseEntity 是我们所有类的基类,看起来,这个类没有映射到 DB 中的任何表
public abstract class BaseEntity
{
public int Id { get; set; }
public DateTime CreateDateTime { get; set; }
public string CreateUser { get; set; }
public DateTime ModDateTime { get; set; }
public string ModUser { get; set; }
public byte[] RowVersion { get; set; }
}
Expression<Func<T, bool>>
会将整个表达式传递给您的存储库,而不仅仅是 Func,因为 EF 处理表达式以生成 SQL 查询,典型的用法是
ICollection<WFGroup> wgGroups = this.dataContext.Find<WFGroup>((w) => true).ToList();
其中 WFGroup 是从 BaseEntity 派生的类,我通常使用延迟加载和代理,并且不会将对象分离/附加到上下文。
您需要从 DAL 中返回自定义实现IEnumerable
(不是集合)的可能性有多大?(要回答这个问题,请查看您以前的项目并计算您周围有多少个或多少个项目yield return
。)
如果答案是“不是非常”,我将只返回ICollection
甚至是数组(如果您想防止查询结果被无意修改。)在紧要关头,如果您需要将查询更改为“流”结果自定义IEnumerable
,您始终可以让旧方法调用新方法并具体化结果以保持与旧客户端的兼容性。
最近有一篇很好的文章涵盖了这一点,即在“返回 IQueryable 的存储库”标题下。这就是它所说的:
我们使用存储库模式的原因之一是封装胖查询。这些查询使 ASP.NET MVC 控制器中的操作难以阅读、理解和测试。此外,随着应用程序的增长,您在多个地方重复胖查询的机会也会增加。使用存储库模式,我们将这些查询封装在存储库类中。结果是更苗条、更清洁、更易于维护和更易于测试的操作。考虑这个例子:
var orders = context.Orders .Include(o => o.Details) .ThenInclude(d => d.Product) .Where(o => o.CustomerId == 1234);
这里我们直接使用没有存储库模式的 DbContext。当您的存储库方法返回 IQueryable 时,其他人将获取该 IQueryable 并在其之上编写查询。结果如下:
var orders = repository.GetOrders() .Include(o => o.Details) .ThenInclude(d => d.Product) .Where(o => o.CustomerId == 1234);
你能看出这两个代码片段之间的区别吗?唯一的区别在于第一行。在第一个示例中,我们使用context.Orders,在第二个示例中,我们使用repository.GetOrders()。那么,这个存储库解决了什么问题呢?没有!
您的存储库应返回域对象。因此,GetOrders()方法应该返回一个IEnumerable。有了这个,第二个例子可以重写为:
var 订单 = repository.GetOrders(1234);
看到不同?
因此,我在团队中添加了以下编码约定:
对于存储库类方法,永远不要返回IQueryable对象。始终先枚举或转换它(例如,ToArray、ToList、AsEnumerable)。
原因是IQueryable将允许调用者在此基础上构建并最终修改在数据库上执行的 SQL 查询。就数据库性能而言,这可能是危险的,但更多的是关于 SoC。调用者不关心数据源;它只想要数据。