在开发过程中,经常会遇到性能问题,尤其是当使用Entity Framework进行数据库操作时。LINQPad是一个强大的工具,可以帮助快速定位和解决这些问题。然而,如果使用的是ASP.NET Boilerplate (ABP)框架,情况可能会更加复杂。ABP框架采用了存储库模式,这与LINQPad以数据上下文为中心的方法并不完全兼容。本文将介绍两种使用LINQPad与ABP应用程序结合的方法,以解决性能问题。
在ASP.NET Boilerplate应用程序中使用的存储库和工作单元模式是一种很好的抽象,可以简化单元测试,启用基于注解的事务,并处理数据库连接管理。正如微软MVC文档中所描述的,这些模式旨在在应用程序的数据访问层和业务逻辑层之间创建一个抽象层。实现这些模式可以帮助应用程序免受数据存储变化的影响,并可以促进自动化单元测试或测试驱动开发(TDD)。
对于简单的查询,可以通过重写查询,不使用存储库模式,然后将其粘贴到LINQPad中来解决问题。以下是实现这一过程的步骤:
首先,需要使ABP的数据上下文支持一个接受连接字符串的构造函数。在数据上下文中添加以下代码:
#if DEBUG
private string _connectionString;
/// ...
public MyProjDbContext(string connectionString)
: base(new DbContextOptions())
{
_connectionString = connectionString;
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (_connectionString == null)
{
base.OnConfiguring(optionsBuilder);
// 正常操作
return;
}
// 有一个连接字符串
var dbContextOptionsBuilder = new DbContextOptionsBuilder();
optionsBuilder.UseSqlServer(_connectionString);
base.OnConfiguring(dbContextOptionsBuilder);
}
#endif
然后在LINQPad中,可以:
LINQPad将通过接受字符串的构造函数实例化DbContext,然后提供连接字符串。现在,当开始一个新的查询时,可以编写:
var thing = this.Things.Where(t => t.Id == 1);
thing.Dump();
如果运行它,将看到生成的SQL语句。这很简单。如果粘贴任何实际代码,需要添加using语句,并将_thingRepository.GetAll()替换为this.Things,将能够将LINQ转换为SQL。这确实很酷。它肯定适用于简单查询。
然而,在经验中,性能问题很少出现在系统的简单部分。性能问题总是出现在多个类交互的地方,因为作者无法将所有逻辑都塞进一个类中,并且还能睡个好觉。
为了使LINQPad能够直接调用ABP代码,需要设置依赖注入,定义一个启动核心模块的模块,指定一个当前用户和租户来模拟,并且以某种方式覆盖默认的工作单元,使用LINQPad的上下文而不是ABP的。这是很多工作。
幸运的是,刚刚发布了一个名为LINQPad.ABP的开源NuGet包,它为做了所有这些工作。要启用它,请按照以下步骤操作:
代码将如下所示:
// 可能需要在这里依赖额外的模块,例如,MyProjApplicationModule
[DependsOn(typeof(MyProjEntityFrameworkModule))]
// 这是一个仅为LINQPad的轻量级自定义模块
public class LinqPadModule : LinqPadModuleBase
{
public LinqPadModule(MyProjEntityFrameworkModule abpProjectNameEntityFrameworkModule)
{
// 告诉项目的EF模块不要播种数据库
abpProjectNameEntityFrameworkModule.SkipDbSeed = true;
}
public override void InitializeServices(ServiceCollection services)
{
// 在这里添加任何自定义依赖注入注册
IdentityRegistrar.Register(services);
}
}
async Task Main()
{
// LINQPad.ABP缓存(昂贵的)模块创建在LINQPad的缓存中
var abpCtx = Util.Cache(LinqPadAbp.InitModule(), "LinqPadAbp");
// 在这里指定想要模拟的租户或用户
using (var uowManager = abpCtx.StartUow(this, tenantId: 5, userId: 8))
{
// 使用LINQPad.ABP的上下文中的IocManager检索需要的东西
var thingService = abpCtx.IocManager.Resolve();
var entity = await thingService.GetEntityByIdAsync(1045);
entity.Dump();
}
}