在LINQPad中征服ASP.NET Boilerplate查询性能

目录

储存库模式101

撤消存储库模式

进入:LINQPad.ABP


有没有在生产中发现代码在规模上失败得很惨?当性能问题出现,并且有一个名字叫EntityFramework时,只有一个工具可用解决此问题:LINQPad

但是,如果您使用的是ASP.NET BoilerplateABP)框架,那么情况就更糟了。这是因为ABP使用存储库模式,事实证明,该存储库模式与LINQPadDataContext中心方法不兼容。

在本文中,我将介绍两种将LINQPadABP应用程序结合使用来解决性能问题的方法:

  1. 重写基于存储库模式的查询,以在具有数据上下文的LINQPad中运行。这适用于小问题。
  2. 借助LINQPad,我刚刚发布的名为LINQPad.ABPNuGet Packge可以直接调用您的代码,支持身份验证,多租户以及工作单元和存储库模式。这极大地有助于解决更复杂的性能问题。

储存库模式101

ASP.NET Boilerplate应用程序中使用的存储库工作单元模式是一种出色的抽象,可简化单元测试,启用基于注释的事务以及处理数据库连接管理。如Microsoft MVC 文档中的本文所述:

存储库和工作单元模式旨在在应用程序的数据访问层和业务逻辑层之间创建一个抽象层。实施这些模式可以帮助您的应用程序与数据存储中的更改隔离开来,并可以促进自动化的单元测试或测试驱动的开发(TDD)。

该文档提供了一个很好的图表,以显示具有和没有存储库模式的体系结构之间的区别:

但是,这些抽象为LINQPad带来了问题。当使用LINQPad诊断性能问题时,直接调用应用程序的代码以查看将查询转换为SQL并执行查询通常会非常方便。但是,即使LINQPad理解了依赖注入,也不知道如何填充IRepository,如何处理[UnitOfWork(TransactionScopeOption.RequiresNew)]属性或为IAbpSession.UserIdIAbpSession.TenantId返回什么值。幸运的是,我刚刚发布了一个NuGet软件包以使其变得容易。

但是首先,解决单个查询问题的最简单方法就是重写没有存储库模式的查询并将其粘贴到LINQPad中。

撤消存储库模式

第一步是使ABP的数据上下文支持采用连接字符串的构造函数。如果将以下代码添加到你的DataContext中:

#if DEBUG
        private string _connectionString;

        ///
        /// For LINQPad
        ///

        public MyProjDbContext(string connectionString)
            : base(new DbContextOptions())
        {
            _connectionString = connectionString;
        }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            if (_connectionString == null)
            {
                base.OnConfiguring(optionsBuilder); // Normal operation
                return;
            }

            // We have a connection string
            var dbContextOptionsBuilder = new DbContextOptionsBuilder();
            optionsBuilder.UseSqlServer(_connectionString);
            base.OnConfiguring(dbContextOptionsBuilder);
        }

#endif

然后在LINQPad中,您可以:

  1. 添加连接
  2. 使用您自己的程序集中的类型化数据上下文
  3. 选择自定义程序集的路径,例如MyProjSolution\server\src\MyProj.Web.Host\bin\Debug\netcoreapp2.1\MyProj.EntityFrameworkCore.dll
  4. 输入完整DbContext类型名称,如MyProj.EntityFrameworkCore.MyAppDbContext
  5. 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的上下文。这是很多工作。

幸运的是,我刚刚发布了LINQPad.ABP,这是一个为您完成所有这些工作的开源NuGet。要启用它:

  1. LINQPad中,添加对LINQPad.ABP的引用。
  2. 添加一个依赖于您项目的特定EF模块的自定义模块。
  3. 创建并缓存LINQPad ABP上下文。
  4. 启动UnitOfWork并指定您要模拟的用户和租户。

该代码将如下所示:

// you may need to depend on additional modules here e.g., MyProjApplicationModule
[DependsOn(typeof(MyProjEntityFrameworkModule))]
// this is a lightweight custom module just for LINQPad
public class LinqPadModule : LinqPadModuleBase
{
    public LinqPadModule(MyProjEntityFrameworkModule abpProjectNameEntityFrameworkModule)
    {
        // tell your project's EF module to refrain from seeding the DB
        abpProjectNameEntityFrameworkModule.SkipDbSeed = true;
    }

    public override void InitializeServices(ServiceCollection services)
    {
        // add any custom dependency injection registrations here
        IdentityRegistrar.Register(services);
    }
}

async Task Main()
{
    // LINQPad.ABP caches (expensive) module creation in LINQPad's cache
    var abpCtx = Util.Cache(LinqPadAbp.InitModule(), "LinqPadAbp");

    // specify the tenant or user you want to impersonate here
    using (var uowManager = abpCtx.StartUow(this, tenantId: 5, userId: 8))
    {
        // retrieve what you need with IocManager in LINQPad.ABP's context
        var thingService = abpCtx.IocManager.Resolve();
        var entity = await thingService.GetEntityByIdAsync(1045);
        entity.Dump();
    }
}

那可能看起来很多代码,但是请相信我,它比以前要简单得多。现在,您可以调用代码并观看每个查询以及如何将查询转换为SQL。我希望这可用更快的帮助你跟踪到困难的bug

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值