老徐的Blog - asp.net
type: channel
link: https://bigger.ee/tag/asp-net/
items: 安装了.net framwork 4.x，但是IIS中没注册上的解决方法
items: ASP.NET Framework下的HttpClient性能优化问题
items: ASP.Net Core 2.0 在Linux下连接SQL Server数据库问题
items: 关于ASP.NET MVC中使用Forms验证的问题
items: ASP.NET Core 2.0下使用log4net记录文件日志
items: ASP.NET Core 2.0 WebAPI 跨域问题
items: .Net网站下使用Oracle数据库的环境设置
items: 在.Net Core 2.0中使用MySQL
items: .Net Core 2.0的一些不大一样的地方（二）——服务器环境
items: .Net Core 2.0的一些不大一样的地方（一）——基础身份认证

安装了.net framwork 4.x，但是IIS中没注册上的解决方法
type: item
date: 2022-10-21T15:15:00+08:00
description: 案例：Windows Server 2008 R2系统，安装好IIS之后，再依次安装.net 3.5和.net 4.6.2。安装完成后发现，.net 3.5的站点可以正常访问，.net 4的站点无法访问，显示403或404，网站的应用程序池中的设置无异常。检查站点的IIS配置，发现IIS中.Net编译无法打开，报类似“无法识别的元素 folderLevelBuildProviders Web.config 第xx行”错误；打开IIS的应用程序映射，发现里面缺少与.net 4.0相关的所有的映射信息。解决方法：重新安装了一次.net 4.6.2的SDK故障依旧，重启了IIS也是毫无作用。这个映射信息太多了，难道需要挨个手动添加吗？使用下面的方法可以完美解决这个问题：打开.net 4.0的安装路径C:\Windows\Microsoft.NET\Framework64\v4.0.30319\，按住键盘的Shift键，同时在该文件夹的空白处右键，打开命令行（CMD），输入下面的命令：aspnet_regiis.exe /iru之后回车，执行完成后，问题成功排除。如果提示执行时出现错误，按提示信息打开日志，按日志里把对应的错误处理掉，再重新执行就可以了。

ASP.NET Framework下的HttpClient性能优化问题
type: item
date: 2020-07-22T20:09:00+08:00
description: 一、背景公司的一个asp.net项目，需要请求Http接口，并发比较高，会经常出现接口服务无响应同时IIS连接数非常高的现象，但是在服务端观察请求只有几十个，而IIS上的总连接数达到了几万，影响了业务的正常使用。二、排查因为服务端是用C++自己写的一个Web服务，先是怀疑服务端的承载能力不够，用压测工具直接压服务端接口，吞吐量可以达到600QPS，可以满足目前的业务需求，说明瓶颈不在服务端，而在客户端。分别尝试了HttpWebRequest的同步方法和异步方法，以及HttpClient的异步方法，压测的结果显示这三种方法的性能从高到低顺序是：HttpWebRequest同步方法> HttpWebRequest异步方法>HttpClient异步方法。使用4000并发压测，HttpWebRequest同步方法吞吐量在120QPS左右，HttpWebRequest异步方法在100左右，HttpClient异步方法只有40个左右，相差非常大。三、解决1.分析要提高Http请求的效率，一般有两种方法，一是维护一个连接池，二是连接复用。HttpWebRequest自带连接池，所以在前面的测试结果中，HttpWebRequest的吞吐量比较高；而HttpWebRequest的异步方法很可能是伪异步，所以效果不是特别明显；HttpClient也只有异步方法，但是却是表现最差的，原因就在每次请求都会重新实例化，请求结束后释放资源，在实例化和释放资源上损耗大量性能。2.方案有两种方案，一是使用HttpWebRequest的同步方法，维护好连接池并调优参数，但是这对耐力是一个很大的考验，而且没有把握可以做到让人满意的结果；第二个方案就是复用HttpClient，但是对原有项目的改动比较大，但是更有把握成功。我这里选择的是复用HttpClient。3.过程1) 静态化HttpClient首先尝试静态化HttpClient来实现复用，测试效果，代码如下： public class DemoController : ApiController { private static readonly HttpClient httpClient = new HttpClient() { BaseAddress = new Uri("http://localhost") }; public async Task<string> Post() { using (var responseData = await httpClient.PostAsync("/", new StringContent(""))) { return await responseData.Content.ReadAsStringAsync(); } } } 测试发现返回的HashCode是一致的，说明实现了复用；并发1000压测之后会发现吞吐量可以达到700左右，但是因为本机性能和没有调优的原因响应会比较慢，但是没有请求失败的情况，这说明复用之后可以有效的提高性能。图1.1000并发压测结果2) 解决不同请求时复用的问题静态化可以解决HttpClient的复用问题，但是如果这个项目中需要请求多个不同的接口地址，那么只是一个静态是解决不了的，因为HttpClient静态实例化之后，参数不能再次修改，怎么解决这个问题呢？如果是在.Net Core 2.1及以上的项目中，可以直接使用 HttpClientFactory 来实现，具体 HttpClientFactory 的使用方法后面再讲。但是在.Net Framework项目中不支持 HttpClientFactory ，而且不直接支持注入，这里我用到另一个方法来实现类似的功能。3) 解决依赖注入如果我们把每个不同的请求都定义一个静态实例，那就完全没了写代码的乐趣。要解决这个问题，就需要依赖注入来实现。在.Net Framework中微软自身没有提供依赖注入的方法，我们可以借用第三方库来完成，我这里使用Autofac来实现，前提是项目为.Net Framework 4.6.1及以上，如果低于该版本，需要先对项目进行升级。使用nuget分别安装 Autofac 、 Autofac.Integration.Mvc 和 Autofac.Integration.WebApi 这三个扩展，个别情况还需要安装 System.ValueTuple ，因环境而异。创建类 IocConfig ，代码如下： public class IocConfig { public static void RegisterDependencies() { ContainerBuilder builder = new ContainerBuilder(); HttpConfigu…

ASP.Net Core 2.0 在Linux下连接SQL Server数据库问题
type: item
date: 2018-05-19T21:56:35+08:00
description: 在ASP.Net Core 2.0下，通过Dapper来使用SQL Server数据库，在Windows系统下完全正常，而部署到Linux服务器上会出现连不上数据库的情况，从日志里看，报下面的错误：Connection Timeout Expired. The timeout period elapsed during the post-login phase. The connection could have timed out while waiting for server to complete the login process and respond; Or it could have timed out while attempting to create multiple active connections. The duration spent while attempting to connect to this server was - [Pre-Login] initialization=23; handshake=365; [Login] initialization=0; authentication=2; [Post-Login] complete=28022;连接超时。开始的时候怀疑是防火墙的原因，检查了一下防火墙正常，而且通过telnet命令检查数据库的1433端口是通的，看来问题是出在.net core上。通过百度和Google搜索相关的关键字，找到了一篇帖子：《Timeout Connecting to SQL Server instance from Linux》，说的是只有SQL Server 2008 及之前的版本会有这问题，SQL Server 2012及之后修复了这个问题。检查了下自己的SQL数据库版本，是SQL Server 2008 R2 版的，正在此列。开了腾讯云的SQL Server云数据库连接测试，完全正常。以上。

关于ASP.NET MVC中使用Forms验证的问题
type: item
date: 2018-05-18T14:11:00+08:00
description: 表单验证(Forms验证)是一个基于票据（ticket-based）[也称为基于令牌(token-based)]的系统。这意味着当用户登录系统以后，他们得到一个包含基于用户信息的票据（ticket）。这些信息被存放在加密过的cookie里面，这些cookie和响应绑定在一起，因此每一次后续请求都会被自动提交到服务器。当用户请求匿名用户无法访问的ASP.NET页面时，ASP.NET运行时验证这个表单验证票据是否有效。如果无效，ASP.NET自动将用户转到登录页面。这时就该由你来操作了。你必须创建这个登录页面并且验证由登录页面提交的凭证。如果用户验证成功，你只需要告诉ASP.NET架构验证成功（通过调用FormsAuthentication类的一个方法），运行库会自动设置验证cookie（实际上包含了票据）并将用户转到原先请求的页面。通过这个请求，运行库检测到验证cookie中包含一个有效票据，然后赋给用户对这个页面的访问权限。下面，就是具体的实现。我们先写一个生成用户票据的方法：//生成Token并保存到Cookies中 private void CreateToken(Models.User user) { string json = JsonConvert.SerializeObject(user); FormsAuthentication.SetAuthCookie(user.UserName.Trim(), true, FormsAuthentication.FormsCookiePath); FormsAuthenticationTicket Ticket = new FormsAuthenticationTicket(1, user.UserName, DateTime.Now, DateTime.Now.AddHours(12), false, json); Biz126.Cache.SetCookies.AddCookie(FormsAuthentication.FormsCookieName, FormsAuthentication.Encrypt(Ticket)); }其中Biz126.Cache.SetCookies.AddCookie是我自己封装的保存Cookie方法。生成Token并保存到Cookie中的方法我们已经写好了，只要在登录成功之后直接调用这个方法就可以了。下面是验证票据，验证票据，我们放在过滤器中进行验证。新建AuthenticationAttribute类，继承ActionFilterAttribute，重写OnActionExecuting方法： /// /// 检查用户是否有该Action执行的操作权限 /// /// public override void OnActionExecuting(HttpActionContext filterContext) { Models.ResultModel result = new Models.ResultModel (); result.code = -401; result.message = "请登录后进行操作"; result.status = false; //如果存在身份信息 if (!HttpContext.Current.User.Identity.IsAuthenticated) { filterContext.Response = filterContext.Request.CreateResponse(HttpStatusCode.Unauthorized, result, "application/json"); } else { BLL.IManager manager = new BLL.Manage.Manager(); result = manager.Detail(); //取当前登录的用户信息，进行其他的操作，比如判断有没有权限等，这里省略 //。。。省略掉其他的操作 if (!result.status) { filterContext.Response = filterContext.Request.CreateResponse(HttpStatusCode.Unauthorized, result, "application/json"); //filterContext.Result = redirect; } } }之后，在要在需要用户登录的Action上面，加上[Authentication]就可以了，也可以新加一个控制器，如BaseWebApiController，在这个控制器上加上[Authentication]，其他的控制器都继承BaseWebApiController，在不需要验证的…

ASP.NET Core 2.0下使用log4net记录文件日志
type: item
date: 2018-05-13T22:25:00+08:00
description: 我们知道log4net的日志功能非常强大，而使用方法也比较复杂；在ASP.NET Core 2.0下，可以通过一个第三方的扩展方法来降低我们的使用难度，具体使用方法如下：我们先新建一个自己的静态类Log4Net，用于之后调用记录日志： public static class Log4Net { private static readonly log4net.ILog log = log4net.LogManager.GetLogger(typeof(Log4Net)); static void SetConfig() { XmlDocument log4netConfig = new XmlDocument(); log4netConfig.Load(File.OpenRead("log4net.config")); var repo = log4net.LogManager.CreateRepository( Assembly.GetEntryAssembly(), typeof(log4net.Repository.Hierarchy.Hierarchy)); log4net.Config.XmlConfigurator.Configure(repo, log4netConfig["log4net"]); } /// /// 信息 /// /// public static void LogInfo(string Message) { if (!log.IsInfoEnabled) SetConfig(); log.Info(Message); } /// /// 信息 /// /// /// public static void LogInfo(string Message, Exception ex) { if (!log.IsInfoEnabled) SetConfig(); log.Info(Message, ex); } /// /// 错误日志 /// /// public static void ErrorInfo(string Message) { if (!log.IsErrorEnabled) SetConfig(); log.Error(Message); } /// /// 错误日志 /// /// /// public static void Error(Exception ex) { if (!log.IsInfoEnabled) SetConfig(); log.Error("异常", ex); } /// /// 错误日志 /// /// /// public static void ErrorInfo(string Message, Exception ex) { if (!log.IsErrorEnabled) SetConfig(); log.Error(Message, ex); } /// /// Debug日志 /// /// public static void DebugInfo(string Message) { if (!log.IsDebugEnabled) SetConfig(); log.Debug(Message); } /// /// Debug日志 /// /// /// public static void DebugInfo(string Message, Exception ex) { if (!log.IsDebugEnabled) SetConfig(); log.Debug(Message, ex); } /// /// 警告 /// /// public static void WarnInfo(string Message) { if (!log.IsWarnEnabled) SetConfig(); log.Warn(Message); } /// /// 警告 /// /// /// public static void WarnInfo(string Message,Exception ex) { if (!log.IsWarnEnabled) SetConfig(); log.Warn(Message,ex); } /// /// 致命错误 /// /// public static void FataInfo(string Message) { if (!log.IsFatalEnabled) SetConfig(); log.Fatal(Message); } /// /// 致命错误 /// /// /// public static void FataInfo(string Message,Exception ex) { if …

ASP.NET Core 2.0 WebAPI 跨域问题
type: item
date: 2018-05-09T09:45:00+08:00
description: 关于 asp.net core 2.0 webapi的跨域，我们这里使用CORS来实现，不使用旧的JSONP，可以这样配置：打开 Startup.cs文件，转到ConfigureServices(IServiceCollection services) 中，增加：services.AddCors(options => { options.AddPolicy("AnyOrigin", builder => { builder .AllowAnyOrigin() .AllowAnyMethod(); }); });再转到Configure(IApplicationBuilder app, IHostingEnvironment env)中，增加：app.UseCors("AnyOrigin");就可以开启CORS跨域了。关于ASP.Net Core的CORS跨域问题详细的使用方法，请参考《Enable Cross-Origin Requests (CORS) in ASP.NET Core》一文。下面测试一下： 直接双击通过浏览器打开这个页面，可以看到，已经读到远程接口的数据了：以上。

.Net网站下使用Oracle数据库的环境设置
type: item
date: 2017-10-12T09:29:00+08:00
description: 在.Net网站下使用Oracle，需要网站服务器安装Oracle数据库的客户端，这是目前必须要安装的。以下是在Windows Server 2008 R2服务器环境下的设置方法及注意事项，其他系统可能会略有不同。1.服务器需要安装Oracle数据库的客户端，版本为Oracle Database 11g Release 1，而且只能使用这个版本，其他版本不可行，另外需要与你的网站应用池的位数（32位或64位）一致；2.安装时，安装类型选择“运行时”；3.安装完成后，查看环境变量中，有没有把安装目录下的BIN文件夹添加到环境变量中，如：D:\app\Administrator\product\11.2.0\client_1\bin;；4.打开安装目录右键属性，切换到“安全”选项卡，编辑，选中“Authenticated Users“，把”读取和执行“权限去掉，之后再重新勾选，之后”应用“；5.最后一步，也是重要的一步，那就是重启服务器，不重启服务器不会生效。这就可以了。

在.Net Core 2.0中使用MySQL
type: item
date: 2017-09-24T00:02:00+08:00
description: 在之前，我简单的介绍过在.net core中使用Mongodb（见文章《.Net Core系列教程（三）——使用Mongodb》）,也使用过PostgreSQL（但是没有写文章介绍怎么使用，只是在文章《.Net Core系列教程（一）——环境搭建》中简单介绍过如何安装）。当然，我的文章质量都不高，只是把自己平时遇到的问题记录下来，很多问题是自己经历过之后在网上苦苦的寻找答案但都不适用或者不明了的情况下，自己摸索出来的解决方案，这也算是防止自己再次误入坑，也给遇到同样问题的朋友一点帮助吧。下面说下怎样在.net core中使用MySQL，这个问题网上随便一搜有很多，我的当然也是从网上搜索来的，只是用自己的语言再次整理下而已。在使用MySQL时，需要使用到MySQL的驱动，之前MySQL官方没有出驱动的时候，需要使用第三方的，不过现在有官方的驱动，还是尽量使用官方的吧，我这里也以官方的为准。另外还用到了Dapper这个小型ORM，这两个都可以通过Nuget来安装。需要注意的是，MySQL.Data需要安装最新版的（现在是6.10.3-rc版），旧版本不支持.net core 2.0先在appsettings.json文件中，添加数据库的配置： "ConnectionStrings": { "MySqlConnection": "server=127.0.0.1;userid=root;pwd=root's password;port=3306;database=database's name;sslmode=none;Charset=utf8;" }之后，增加数据库连接的Model类： public class ConnectionStrings { public string MySqlConnection { get; set; } }在Startup.cs文件中的ConfigureServices方法里，在services.AddMvc();之前增加添加调用：services.Configure<Models.ConnectionStrings>(Configuration.GetSection("ConnectionStrings"));这样就会把appsettings.json中的数据库连接配置注入到Models.ConnectionStrings实体类中。在控制器中，添加：private readonly IOptions<Models.ConnectionStrings> _settings;之后控制器的构造函数： public NewsController(IOptions<Models.ConnectionStrings> settings) { _settings = settings; }这里我是直接把数据库连接的实体类传给了DAL层，这样写： public NewsController(IOptions<Models.ConnectionStrings> settings) { _settings = new BLL.ServiceImp.News(settings.Value); }其中的settings.Value就是数据库连接实体类了之后在DAL层中使用，同时也附带了几个数据库操作的实例： private string ConnString; public News(Models.ConnectionStrings Connection) { ConnString = Connection.MySqlConnection; } /// /// 取前top条新闻 /// /// <returns></returns> public async Task<Models.ResultModel<List<Models.News>>> Newslatest(int top) { Models.ResultModel<List<Models.News>> result = new ResultModel<List<Models.News>>(); using (var Conn = new MySqlConnection(ConnString)) { string sql = "select Id,title,datetime,writer,remark,body,cid,status,isimage,thumb,category from news where status=1 order by datetime desc limit 0,@top"; var data = await Conn.QueryAsync<Models.News>(sql, new { top = top }); if(data.…

.Net Core 2.0的一些不大一样的地方（二）——服务器环境
type: item
date: 2017-09-23T19:13:00+08:00
description: 我在《.Net Core 2.0的一些不大一样的地方（一）——基础身份认证》中，说了下关于Cookies的不同之处，在这篇文章中，我说下关于服务器的相关问题。服务器的环境安装，在官网中介绍的.Net Core 2.0环境的安装方法，是通过源的方法来安装，我尝试了下，失败了，可能是和我的服务器中有之前的.Net Core 1.0环境有关。把原来的环境删掉，先删除/usr/local/bin/dotnet软链接，之后再删除掉.net core的文件夹/opt/dotnet。不过我后面再没有试通过源的方式安装是否没问题，这里我还是使用.net core 1.0的时候安装的方法，下载.net core 2.0 sdk，之后解压缩设置软链接的方式安装。微软官方给出的通过源安装的方法，参见：《.NET and C# - Get Started in 10 Minutes》一文，自行选择自己使用的操作系统，来按步骤安装。如果在程序中，使用了身份认证（如Cookies等，见《.Net Core 2.0的一些不大一样的地方（一）——基础身份认证》），那么还需要在Startup.cs文件的Configure方法中，在调用UseAuthentication之前调用UseForwardedHeaders，如下：app.UseForwardedHeaders(new ForwardedHeadersOptions { ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto }); app.UseAuthentication();这样，在做身份验证的时候，能够通过反代服务器取到用户访问的URL，不然的话，会因为URL不一致（用户访问的是Nginx服务器上绑定的域名，而.net core服务器使用的是localhost）导致验证失败。nginx服务器的安装及反代服务配置与之前的版本没有什么区别，有区别的是守护程序的配置，使用的还是systemd(systemctl)，配置中，需要多设置一项：WorkingDirectory=站点的根目录如：[Unit] Description=Example .NET Web API Application running on Ubuntu [Service] WorkingDirectory=/var/aspnetcore/hellomvc ExecStart=/usr/local/bin/dotnet /var/aspnetcore/hellomvc/hellomvc.dll Restart=always RestartSec=10 # Restart service after 10 seconds if dotnet service crashes SyslogIdentifier=dotnet-example User=www-data Environment=ASPNETCORE_ENVIRONMENT=Production [Install] WantedBy=multi-user.target如果不设置这项的话，所有的静态资源文件都请求不到，返回502或404错误。好了，基本就这些了吧，遇到其他的再继续补充。

.Net Core 2.0的一些不大一样的地方（一）——基础身份认证
type: item
date: 2017-09-23T14:00:00+08:00
description: 近日，把之前使用.net core 1.0写的网站，使用.net core 2.0改写了一下，发现一些不大一样的地方，如果不注意的话，会出现些问题。一、先说下关于使用Cookie来验证用户登录的地方：在.net core 1.x时代，具体作法如我前面的文章《.Net Core系列教程（四）—— 基础身份认证》所说，这里我就不重新写了而在.net core 2.0中，需要做以下调整：1）在Startup.cs文件中，ConfigureServices方法下添加： services.AddAuthentication(options=> { options.DefaultChallengeScheme = "Cookie"; options.DefaultSignInScheme = "Cookie"; options.DefaultAuthenticateScheme = "Cookie"; }) .AddCookie("Cookie", m => { m.LoginPath = new PathString("/Manage/CPanel/Login"); m.AccessDeniedPath = new PathString("/Manage/CPanel/Forbidden"); m.LogoutPath = new PathString("/Manage/CPanel/Logout"); m.Cookie.Path = "/"; });完整的代码如下： public void ConfigureServices(IServiceCollection services) { services.AddApplicationInsightsTelemetry(Configuration); services.AddAuthentication(options=> { options.DefaultChallengeScheme = "Cookie"; options.DefaultSignInScheme = "Cookie"; options.DefaultAuthenticateScheme = "Cookie"; }) .AddCookie("Cookie", m => { m.LoginPath = new PathString("/Manage/CPanel/Login"); m.AccessDeniedPath = new PathString("/Manage/CPanel/Forbidden"); m.LogoutPath = new PathString("/Manage/CPanel/Logout"); m.Cookie.Path = "/"; }); services.AddOptions(); services.Configure<Models.ConnectionStrings>(Configuration.GetSection("ConnectionStrings")); services.AddAuthorization(); //Form基础验证 services.AddMvc(); }之后在Configure方法下添加：app.UseAuthentication();完整代码： public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) { loggerFactory.AddConsole(Configuration.GetSection("Logging")); loggerFactory.AddDebug(); app.UseForwardedHeaders(new ForwardedHeadersOptions { ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto }); app.UseAuthentication(); //app.UseApplicationInsightsRequestTelemetry(); if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); app.UseBrowserLink(); } else { app.UseExceptionHandler("/Home/Error"); } //app.UseApplicationInsightsExceptionTelemetry(); app.UseStaticFiles(); app.U…