ASP.NET Core 缓存 Caching 提供了包括但不限于以下几种存储方式:
内存缓存:https://xoyozo.net/Blog/Details/aspnetcore-memory-cache
SQL Server 缓存:https://xoyozo.net/Blog/Details/aspnetcore-sql-cache
Redis 缓存:https://xoyozo.net/Blog/Details/aspnetcore-redis-cache
MySQL 缓存:https://xoyozo.net/Blog/Details/aspnetcore-mysql-cache
本文介绍内存缓存
Memory Caching
1.新建一个 ASP.NET Core 项目,选择 Web 应用程序,将身份验证 改为 不进行身份验证。
2.添加引用
Install-Package Microsoft.Extensions.Caching.Memory
3.使用
在 Startup.cs 中 ConfigureServices
public void ConfigureServices(IServiceCollection services)
{
services.AddMemoryCache();
// Add framework services.
services.AddMvc();
}然后在
public class HomeController : Controller
{
private IMemoryCache _memoryCache;
public HomeController(IMemoryCache memoryCache)
{
_memoryCache = memoryCache;
}
public IActionResult Index()
{
string cacheKey = "key";
string result;
if (!_memoryCache.TryGetValue(cacheKey, out result))
{
result = $"LineZero{DateTime.Now}";
_memoryCache.Set(cacheKey, result);
}
ViewBag.Cache = result;
return View();
}
}这里是简单使用,直接设置缓存。
我们还可以加上过期时间,以及移除缓存,还可以在移除时回掉方法。
过期时间支持相对和绝对。
下面是详细的各种用法。
public IActionResult Index()
{
string cacheKey = "key";
string result;
if (!_memoryCache.TryGetValue(cacheKey, out result))
{
result = $"LineZero{DateTime.Now}";
_memoryCache.Set(cacheKey, result);
//设置相对过期时间2分钟
_memoryCache.Set(cacheKey, result, new MemoryCacheEntryOptions()
.SetSlidingExpiration(TimeSpan.FromMinutes(2)));
//设置绝对过期时间2分钟
_memoryCache.Set(cacheKey, result, new MemoryCacheEntryOptions()
.SetAbsoluteExpiration(TimeSpan.FromMinutes(2)));
//移除缓存
_memoryCache.Remove(cacheKey);
//缓存优先级 (程序压力大时,会根据优先级自动回收)
_memoryCache.Set(cacheKey, result, new MemoryCacheEntryOptions()
.SetPriority(CacheItemPriority.NeverRemove));
//缓存回调 10秒过期会回调
_memoryCache.Set(cacheKey, result, new MemoryCacheEntryOptions()
.SetAbsoluteExpiration(TimeSpan.FromSeconds(10))
.RegisterPostEvictionCallback((key, value, reason, substate) =>
{
Console.WriteLine($"键{key}值{value}改变,因为{reason}");
}));
//缓存回调 根据Token过期
var cts = new CancellationTokenSource();
_memoryCache.Set(cacheKey, result, new MemoryCacheEntryOptions()
.AddExpirationToken(new CancellationChangeToken(cts.Token))
.RegisterPostEvictionCallback((key, value, reason, substate) =>
{
Console.WriteLine($"键{key}值{value}改变,因为{reason}");
}));
cts.Cancel();
}
ViewBag.Cache = result;
return View();
}Distributed Cache Tag Helper
在 ASP.NET Core MVC 中有一个 Distributed Cache 我们可以使用。
我们直接在页面上增加 distributed-cache 标签即可。
<distributed-cache name="mycache" expires-after="TimeSpan.FromSeconds(10)">
<p>缓存项10秒过期-LineZero</p>
@DateTime.Now
</distributed-cache>
<distributed-cache name="mycachenew" expires-sliding="TimeSpan.FromSeconds(10)">
<p>缓存项有人访问就不会过期,无人访问10秒过期-LineZero</p>
@DateTime.Now
</distributed-cache>这样就能缓存标签内的内容。

使用 myssl.com 检测证书状态,显示握手失败(No FS No SNI):

发现 XP 上的 IE6 和 IE8 均无法访问该站点,原因是 XP 不支持 FS,不支持 SNI。
可以尝试在 IIS 的站点的“绑定”中更改“需要服务器名称指示”来实现正常访问。
步骤参阅:https://xoyozo.net/Blog/Details/wxpay-notify-url-https
当然,前提是服务器上必须已启用相关的协议版本和加密方式(按需启用),建议使用 IISCrypto。

An unhandled exception occurred while processing the request.
InvalidOperationException: RenderBody has not been called for the page at '/Areas/Admin/Views/Shared/_Layout.cshtml'. To ignore call IgnoreBody().
Microsoft.AspNetCore.Mvc.Razor.RazorPage.EnsureRenderedBodyOrSections()
解决方法一:
在 return; 前执行 IgnoreBody();
解决方法二:
不要在 @RenderBody() 之前执行 return;
首先在项目根目录中创建目录 Areas。
在 Areas 中添加区域,以“Admin”、“Console”两个区域为例:

VS 提醒在 Startup.cs 的 Configure() 方法中添加 app.UseMvc,此与 app.UseEndpoints 冲突,我们改为在 app.UseEndpoints 中添加 MapControllerRoute(放在 default 之前):
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "MyArea",
pattern: "{area:exists}/{controller=Home}/{action=Index}/{id?}");
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});给区域的控制器添加属性,如:
[Area("Admin")]
public class HomeController : Controller
{
public IActionResult Index()
{
return View();
}
}
打开 appsettings.json,添加一项配置(如下方示例中的“SiteOptions”项)

* 注意,如需配置开发环境与生产环境不同的值,可单独在 appsettings.Development.json 文件中配置不同项,格式层次须一致;
C# 中习惯用强类型的方式来操作对象,那么在项目根目录添加类(类名以 SiteOptions为例),格式与 appsettings.json 中保持一致:
public class SiteOptions
{
public ERPOptions ERP { get; set; }
public WeixinOpenOptions WeixinOpen { get; set; }
public WeixinMPOptions WeixinMP { get; set; }
public SMSOptions SMS { get; set; }
public AliyunOSSOptions AliyunOSS { get; set; }
/// <summary>
/// 单个文件上传的最大大小(MB)
/// </summary>
public int MaxSizeOfSigleFile_MB { get; set; }
/// <summary>
/// 单个文件上传的最大大小(字节)
/// </summary>
public int MaxSizeOfSigleFile_B => MaxSizeOfSigleFile_MB * 1024 * 1024;
public class ERPOptions
{
public int ChannelId { get; set; }
public string AppKey { get; set; }
}
public class WeixinOpenOptions
{
public string AppId { get; set; }
public string AppSecret { get; set; }
}
public class WeixinMPOptions
{
public string AppId { get; set; }
public string AppSecret { get; set; }
}
public class SMSOptions
{
public string AppKey { get; set; }
}
public class AliyunOSSOptions
{
public string Endpoint { get; set; }
public string AccessKeyId { get; set; }
public string AccessKeySecret { get; set; }
public string BucketName { get; set; }
/// <summary>
/// 格式://域名/
/// </summary>
public string CdnUrl { get; set; }
}
}在 Startup 中注入 IConfiguration,并在 ConfigureServices() 方法中添加服务(注意使用 GetSection() 映射到自命名的“SiteOptions”项)

在控制器中使用:
在控制器类中键入“ctor”,并按两次 Tab 键,创建构造函数

在构造函数中注入“IOptions”,并在 Action 中使用
using Microsoft.Extensions.Options;
public class TestController : Controller
{
private readonly IOptions<SiteOptions> options;
public TestController(IOptions<SiteOptions> options)
{
this.options = options;
}
public IActionResult Index()
{
return View(options.Value.ERP.ChannelId.ToString());
}
}在视图中使用:
@using Microsoft.Extensions.Options
@inject IOptions<SiteOptions> options
@options.Value.ERP.ChannelId* 本文凡使用变量 httpContextAccessor 或 _httpContextAccessor 的地方都需要需要注入 Microsoft.AspNetCore.Http.IHttpContextAccessor
* 在视图(view)中应以 ViewContext 类引出
获取当前页面网址:
using Microsoft.AspNetCore.Http.Extensions;
string url = Request.GetDisplayUrl();获取当前访问的域名和端口:

获取来源:
Request.GetTypedHeaders().Referer
判断 Scheme 是否为 https:
Request.IsHttps获取客户端IP和端口、服务器IP和端口:

获取浏览器用户代理(UserAgent):
httpContextAccessor.HttpContext.Request.Headers[HeaderNames.UserAgent];获取当前请求的唯一标识:
httpContextAccessor.HttpContext.TraceIdentifier返回结果:80000564-0002-f700-b63f-84710c7967bb
用途:可作为生成随机数的种子。
获取客户端IP地址:
httpContextAccessor.HttpContext.Connection.RemoteIpAddress获取当前项目根目录磁盘路径:
AppContext.BaseDirectory // 以“\”结尾注意:此路径到项目根目录,而非网站根目录,网站根目录请自行追加,默认为 wwwroot\
编码/解码:(参数和返回值均为 string?)
using System.Net;
WebUtility.HtmlEncode(myString)
WebUtility.HtmlDecode(myString)
WebUtility.UrlEncode(myString)
WebUtility.UrlDecode(myString)更多:https://xoyozo.net/Blog/Index/Core
本文介绍 ASP.NET 的 Swagger 部署,若您使用 ASP.NET Core 应用程序,请移步 ASP.NET Core Web API Swagger 官方文档:
https://github.com/domaindrivendev/Swashbuckle.AspNetCore
安装
NuGet 中搜索安装 Swashbuckle,作者 Richard Morris
访问
http://您的域名/swagger
配置
显示描述
以将描述文件(xml)存放到项目的 bin 目录为例:
打开项目属性,切换到“生成”选项卡
在“配置”下拉框选择“所有配置”
更改“输出路径”为:bin\
勾选“XML 文档文件”:bin\******.xml,(默认以程序集名称命名)
打开文件:App_Start/SwaggerConfig.cs
取消注释:c.IncludeXmlComments(GetXmlCommentsPath());
添加方法:
public static string GetXmlCommentsPath() { return System.IO.Path.Combine( System.AppDomain.CurrentDomain.BaseDirectory, "bin", string.Format("{0}.xml", typeof(SwaggerConfig).Assembly.GetName().Name)); }其中 Combine 方法的第 2 个参数是项目中存放 xml 描述文件的位置,第 3 个参数即以程序集名称作为文件名,与项目属性中配置一致。
如遇到以下错误,请检查第 2、3、4 步骤中的配置(Debug / Release)
500 : {"Message":"An error has occurred."} /swagger/docs/v1使枚举类型按实际文本作为参数值(而非转成索引数字)
打开文件:App_Start/SwaggerConfig.cs
取消注释:c.DescribeAllEnumsAsStrings();
思路:使用 SHOW PROCESSLIST 命令列出数据库当前的所有连接,筛选相关进程,使用 KILL 命令结束进程。
首先创建 SHOW PROCESSLIST 结果集的模型:
class ProcessItem
{
public int Id { get; set; }
public string User { get; set; }
public string db { get; set; }
public string Command { get; set; }
public int Time { get; set; }
}查询、筛选、执行:
using (var db = new dbEntities())
{
foreach (var p in db.Database.SqlQuery<ProcessItem>("SHOW PROCESSLIST").ToList())
{
if (p.Id > 0 && p.User == "用户名" && p.db == "库名" && p.Command == "Sleep" /*&& p.Time >= minSecondsToExpire*/)
{
pids.Add(p.Id);
}
}
foreach (var pid in pids)
{
db.Database.ExecuteSqlCommand("KILL " + pid);
}
}
完整操作步骤如下:
安装 NuGet 包:
Microsoft.AspNet.Web.Optimization打开
Views目录(如果是应用于区域,则为区域的Views目录)中的 web.config,在<namespaces />节点中添加<add namespace="System.Web.Optimization" />在
App_Start目录中创建类文件BundleConfig.cs,更改其命名空间为应用程序的默认命名空间(即移除.App_Start),创建方法:public static void RegisterBundles(BundleCollection bundles) { bundles.Add(new StyleBundle("~/虚拟路径(不能有点)").Include("~/CSS文件路径")); bundles.Add(new ScriptBundle("~/虚拟路径(不能有点)").Include("~/JS文件路径")); }虚拟路径应避免与页面访问路径相同。Include 可包含多个文件,效果是合并输出,注意引用的顺序。
打开
Global.asax,在Application_Start()事件中添加代码BundleTable.EnableOptimizations = true; // 该设置使在开发模式中实现压缩代码,不设置则仅在发布后压缩代码 BundleConfig.RegisterBundles(BundleTable.Bundles);视图页面中引用样式表或脚本
@Styles.Render("~/CSS虚拟路径") @Scripts.Render("~/JS虚拟路径")使用 Render 的好处是,ASP.NET 会自动给引用地址加上参数,可在更改脚本或样式表内容后更改这些参数使浏览器缓存立即失效。
如果你的项目中已经安装并使用 Bundle,那么只需要参考第 4 步,将 BundleTable 的 EnableOptimixations 设为 true。
以下是一些常见异常的解决方法:
The name 'Styles' does not exist in the current context
The name 'Scripts' does not exist in the current context
解决:参步骤 2。
引用的样式表或脚本不存在(报 404 错误)
解决:步骤 3 中的虚拟路径不规范。
获取控制器(Controller)名称
控制器的 Action 中
RouteData.Values["controller"].ToString()
或
RouteData.Route.GetRouteData(HttpContext).Values["controller"].ToString()
视图中
ViewContext.RouteData.Values["controller"].ToString()
或
ViewContext.RouteData.Route.GetRouteData(Context).Values["controller"].ToString()
公共方法中
HttpContext.Current.Request.RequestContext.RouteData.Values["controller"].ToString()
过滤器中
以 ActionFilterAttribute 为例,实现一个继承类,重写相关方法,在重写的方法中获取控制器名称
/// <summary>
/// 可用于检查用户是否已经登录(在 Action 之前执行)
/// </summary>
/// <param name="filterContext"></param>
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
base.OnActionExecuting(filterContext);
string controller = filterContext.RouteData.Values["controller"].ToString();
}
获取 Action 名称
将以上代码中的 "controller" 替换成 "action" 即可获取 Action 名称。
获取区域(Area)名称
控制器的 Actione 中
RouteData.DataTokens["area"]?.ToString()
注意:应使用空传播运算符
获取所有 Action 参数
RouteData.Values