一个从fw 迁移到 core 2.1 的项目,一套电商系统, 一个正常的列表查询页面非常慢,所以自定义了个中间件, 并且在 action filter 和 action 的function 开始结束都加了 时间log
中间件是自定义的,但是也仅仅是 log的作用。
startup类里边,都是些常规注入,仅有的几个filter 也试过注释掉,但是消耗依旧差不多
Program 类 没有做修改
1 public class Startup 2 { 3 public static ILoggerRepository repository { get; set; } 4 //public Startup(IConfiguration configuration) 5 //{ 6 // Configuration = configuration; 7 //} 8 9 public Startup(IConfiguration configuration) 10 { 11 Configuration = configuration; 12 repository = LogManager.CreateRepository("NETCoreRepository"); 13 XmlConfigurator.Configure(repository, new FileInfo("log4net.config")); 14 SDKProperties.LogRepository = repository;//类库中定义的静态变量 15 } 16 17 18 public IConfiguration Configuration { get; } 19 20 public IContainer ApplicationContainer { get; private set; } 21 22 // This method gets called by the runtime. Use this method to add services to the container. 23 public IServiceProvider ConfigureServices(IServiceCollection services) 24 { 25 services.Replace(ServiceDescriptor.Transient<IControllerActivator, ServiceBasedControllerActivator>()); 26 services.AddMvc(options => 27 { 28 options.Filters.Add<ModelValidationFilter>(); 29 options.Filters.Add<HttpGlobalExceptionFilter>(); 30 options.Filters.Add<MobileTemplateAttribute>(); 31 }).AddJsonOptions(op => op.SerializerSettings.ContractResolver = 32 new Newtonsoft.Json.Serialization.DefaultContractResolver()); ; 33 services.AddAutoMapper(); 34 services.AddUEditorService(); 35 //添加Session功能 36 services.AddSession(); 37 services.AddMemoryCache(); 38 //启用目录浏览 services.AddDirectoryBrowser(); 39 services.AddSingleton<IConfiguration>(Configuration); 40 services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>(); //使用 HttpContext 注入对象 41 services.AddSingleton<IActionContextAccessor, ActionContextAccessor>(); 42 services.AddSingleton<IUrlHelperFactory, UrlHelperFactory>().AddScoped(it => it.GetService<IUrlHelperFactory>() 43 .GetUrlHelper(it.GetService<IActionContextAccessor>().ActionContext)); 44 45 46 #region Auth 授权cookie相关配置 47 services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme) 48 .AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, option => 49 { 50 //系统默认无指定 Authorize 跳转登录 51 option.LoginPath = new PathString("/Account/Login"); //设置登陆失败或者未登录授权的情况下,直接跳转的路径这里 52 option.AccessDeniedPath = new PathString("/Error/Forbidden"); //没有权限时的跳转页面 53 54 //设置cookie只读情况 55 option.Cookie.HttpOnly = true; 56 //cookie过期时间 57 option.Cookie.Expiration = new TimeSpan(4, 0, 0); 58 option.ExpireTimeSpan = new TimeSpan(4, 0, 0); 59 }) 60 .AddCookie(AdminAuthorizeAttribute.AdminAuthenticationScheme, option => 61 { 62 //Areas: Admin 模块 设置LoginPath等,因为不同系统使用 不同Scheme做区分验证 63 option.LoginPath = new PathString("/Admin/Login/Index"); 64 //设置cookie只读情况 65 option.Cookie.HttpOnly = true; 66 //cookie过期时间 67 option.Cookie.Expiration = new TimeSpan(4, 0, 0); 68 option.ExpireTimeSpan = new TimeSpan(4, 0, 0); 69 }) 70 .AddCookie(SellerAdminAuthorizeAttribute.SellerAdminAuthenticationScheme, option => 71 { 72 //Areas: SellerAdmin 模块 设置LoginPath等,因为不同系统使用 不同Scheme做区分验证 73 option.LoginPath = new PathString("/SellerAdmin/Login/Index"); 74 //设置cookie只读情况 75 option.Cookie.HttpOnly = true; 76 //cookie过期时间 77 option.Cookie.Expiration = new TimeSpan(4, 0, 0); 78 option.ExpireTimeSpan = new TimeSpan(4, 0, 0); 79 }); 80 #endregion 81 82 83 var builder = CreateBuilder(services);//实例化 AutoFac 容器 84 85 ApplicationContainer = builder.Build(); 86 return new AutofacServiceProvider(ApplicationContainer);//第三方IOC接管 core内置DI容器 87 } 88 89 public ContainerBuilder CreateBuilder(IServiceCollection services) 90 { 91 var builder = new ContainerBuilder(); 92 builder.Populate(services); 93 94 var assemblyServices = Assembly.Load("PPX.Mall.Service"); 95 builder.RegisterAssemblyTypes(assemblyServices).PropertiesAutowired() 96 .Where(t => typeof(IService).IsAssignableFrom(t) && !t.GetTypeInfo().IsAbstract) 97 .AsImplementedInterfaces().InstancePerLifetimeScope(); 98 99 var assemblyCore = Assembly.Load("PPX.Mall.Core"); 100 builder.RegisterAssemblyTypes(assemblyCore).PropertiesAutowired() 101 .Where(m => typeof(IStrategy).IsAssignableFrom(m) && !m.GetTypeInfo().IsAbstract) 102 .AsImplementedInterfaces().InstancePerLifetimeScope(); 103 104 //builder.RegisterAssemblyTypes(Assembly.Load("PPX.Mall.API")).Where(t => 105 // typeof(Controller).IsAssignableFrom(t) && !t.GetTypeInfo().IsAbstract).PropertiesAutowired(); 106 107 //builder.RegisterAssemblyTypes(Assembly.Load("PPX.Mall.SmallProgAPI")).Where(t => 108 // typeof(Controller).IsAssignableFrom(t) && !t.GetTypeInfo().IsAbstract).PropertiesAutowired(); 109 110 builder.RegisterAssemblyTypes(Assembly.Load("PPX.Mall.Web")).Where(t => 111 typeof(Controller).IsAssignableFrom(t) && !t.GetTypeInfo().IsAbstract).PropertiesAutowired(); 112 113 return builder; 114 } 115 116 // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 117 public void Configure(IApplicationBuilder app, IHostingEnvironment env) 118 { 119 if (env.IsDevelopment()) 120 { 121 app.UseBrowserLink(); 122 app.UseDeveloperExceptionPage(); 123 } 124 else 125 { 126 app.UseExceptionHandler("/Home/Error"); 127 } 128 app.UseResponseTime(); 129 app.UseSession(); 130 131 #region 设置静态资源访问 132 app.UseStaticFiles(); 133 //app.UseStaticFiles(new StaticFileOptions 134 //{ 135 // FileProvider = new PhysicalFileProvider( 136 // Path.Combine(Directory.GetCurrentDirectory(), "Storage")), 137 // RequestPath = "/Storage" 138 //}); 139 //启用目录浏览 140 //app.UseDirectoryBrowser(new DirectoryBrowserOptions 141 //{ 142 // FileProvider = new PhysicalFileProvider( 143 // Path.Combine(Directory.GetCurrentDirectory(), "Storage")), 144 // RequestPath = "/Storage" 145 //}); 146 #endregion 147 148 app.UseStaticHttpContext(); 149 app.UseAuthentication(); 150 151 //此处代码,是将程序管道放到一个公共静态类中,以便其他程序集,可以通过管道直接获取注入的实例对象 152 //避免有些基类等地方,不方便使用 构造函数或者属性注入的方式获取 实例对象 153 PPXMallServiceProvider.ServiceProvider = app.ApplicationServices; 154 155 //RegistAtStart.RegistStrategies(); 156 //Plugin.RegistAtStart.RegistPlugins(); //TODO 插件未移植完,暂时注释 157 158 app.UseMvc(routes => 159 { 160 //routes.Routes.Add(new PayRoute( 161 // routes.DefaultHandler, 162 // "/common/site/pay")); 163 164 routes.MapRoute( 165 "Mobile_default", 166 "m-{platform}/{controller}/{action}/{id?}", 167 new { area = "Mobile", controller = "Home", action = "Index" }); 168 169 routes.MapRoute( 170 name: "area", 171 template: "{area:exists}/{controller=Home}/{action=Index}/{id?}"); 172 173 routes.MapRoute( 174 "Mobile_default2", 175 "m/{controller}/{action}/{id?}", 176 new { area = "Mobile", controller = "Home", action = "Index", platform = "Mobile" }); 177 178 routes.MapRoute( 179 "Mobile_isshare", 180 "m-{platform}/{controller}/{action}/{id?}/{isShare}", 181 new { controller = "Home", action = "Index" }); 182 183 routes.MapRoute( 184 "Mobile_openid_isshare", 185 "m-{platform}/{controller}/{action}/{id?}/{openId}/{isShare}", 186 new { controller = "Home", action = "Index" }); 187 188 routes.MapRoute( 189 "Web_default", 190 "{controller}/{action}/{id?}", 191 new { area = "Web", controller = "Home", action = "Index" }); 192 193 routes.MapRoute( 194 "common_default", 195 "common/{controller}/{action}/{id?}", 196 new { controller = "Home", action = "Index" }); 197 198 routes.MapRoute( 199 name: "default", 200 template: "{controller=Home}/{action=Index}/{id?}"); 201 }); 202 203 204 } 205 }
所以,这一个仅仅 分页查询的request,从Action filter Executed 到 Middleware _next.Invoke(context); 执行完中间有什么环节 有可能产生这么大的消耗? 已经排除了 filter
问题应该出在列表查询页面的数据库操作,建议提供那部分代码
dudu 大神,消耗的点已经找到了,是因为ef 查询的获取结果的时候 耗时太多了,我尝试提前进行了 tolist的操作,发现这一步操作 耗时巨大。
但是有个现象,部署在同一个服务器IIS上的两套程序,一个是 迁移之前的framework版本, 一个是迁移之后的 core,同样的一个 列表查询页面,同样的 ef 代码,fw的 查询非常快,页面响应1秒以内, core 要 好几秒, 是因为IIS 针对fw 托管的类型,有什么相关的缓存优化吗?
我尝试修改了ef部分的代码,做了优化,之前会产生几十条 sql 语句,现在 只有两三条,但是响应时间还在2s 左右,达不到预期的效果,主表的数据量也才几百条而已
@死神的背影: 看一下是不是这个问题引起的? 使用Entity Framework Core需要注意的一个全表查询问题
建议EF只用于修改、插入、删除、简单的单表查询操作。查询用Dapper.NET之类更灵活的工具。
比如:命令查询职责分离(CQRS)模式,EF用于命令的终端执行,Dapper.NET只用于查询