Nuget 安装:X.PagedList.Mvc.Core
控制器:
using X.PagedList;
public IActionResult Index(int page = 1)
{
……
return View(q.ToPagedList(page, size));
}视图:
@using X.PagedList
@using X.PagedList.Mvc.Core
@model IPagedList<xxx>
@Html.PagedListPager(Model, page => Url.Action("Index", new { page }))自定义(options 默认值):
@Html.PagedListPager(
Model,
page => Url.Action("Index", new { page }),
new X.PagedList.Mvc.Common.PagedListRenderOptionsBase
{
HtmlEncoder = HtmlEncoder.get_Default(),
DisplayLinkToFirstPage = PagedListDisplayMode.IfNeeded,
DisplayLinkToLastPage = PagedListDisplayMode.IfNeeded,
DisplayLinkToPreviousPage = PagedListDisplayMode.IfNeeded,
DisplayLinkToNextPage = PagedListDisplayMode.IfNeeded,
DisplayLinkToIndividualPages = true,
DisplayPageCountAndCurrentLocation = false, // 显示总页数和当前页码
MaximumPageNumbersToDisplay = 10, // 最多显示页码数
DisplayEllipsesWhenNotShowingAllPageNumbers = true,
EllipsesFormat = "…",
LinkToFirstPageFormat = "<<",
LinkToPreviousPageFormat = "<",
LinkToIndividualPageFormat = "{0}",
LinkToNextPageFormat = ">",
LinkToLastPageFormat = ">>",
PageCountAndCurrentLocationFormat = "Page {0} of {1}.",
ItemSliceAndTotalFormat = "Showing items {0} through {1} of {2}.",
FunctionToDisplayEachPageNumber = null,
ClassToApplyToFirstListItemInPager = null,
ClassToApplyToLastListItemInPager = null,
ContainerDivClasses = new string[1]
{
"pagination-container"
},
UlElementClasses = new string[1]
{
"pagination"
},
LiElementClasses = Enumerable.Empty<string>(),
PageClasses = Enumerable.Empty<string>(),
UlElementattributes = null,
ActiveLiElementClass = "active",
EllipsesElementClass = "PagedList-ellipses",
PreviousElementClass = "PagedList-skipToPrevious",
NextElementClass = "PagedList-skipToNext",
})保留地址栏参数:
@{
string query = Context.Request.QueryString.Value;
}
@Html.PagedListPager(Model, page => Regex.IsMatch(query, @"[?&]page=\d+")
? Regex.Replace(query, @"([?&])page=\d+", $"$1page={page}")
: (query.StartsWith("?") ? $"{query}&page={page}" : $"{query}?page={page}"),
new X.PagedList.Web.Common.PagedListRenderOptionsBase
{
DisplayPageCountAndCurrentLocation = true,
MaximumPageNumbersToDisplay = 5,
})这里从查询字符串中判断并替换 page 值,如果有更简单的方法敬请告知。比如 Webdiyer 的分页组件会自动携带所有参数。
更多使用方式参官方文档:https://github.com/dncuug/X.PagedList
附应用于 Unify Template(一款基于 Bootstrap 的 HTML 模板)中的配置:
<style>
.u-pagination-v1-1--active .u-pagination-v1-1 { color: #fff; border-color: #72c02c; }
.PagedList-pageCountAndLocation { float: right !important; }
</style>
@{
string query = Context.Request.QueryString.Value;
}
@Html.PagedListPager(Model, page => Regex.IsMatch(query, @"[?&]page=\d+")
? Regex.Replace(query, @"([?&])page=\d+", $"$1page={page}")
: (query.StartsWith("?") ? $"{query}&page={page}" : $"{query}?page={page}"),
new X.PagedList.Web.Common.PagedListRenderOptionsBase
{
DisplayPageCountAndCurrentLocation = true,
MaximumPageNumbersToDisplay = 5,
UlElementClasses = new string[] { "list-inline" },
LiElementClasses = new string[] { "list-inline-item" },
PageClasses = new string[] { "u-pagination-v1__item", "u-pagination-v1-1", "g-pa-7-14" },
ActiveLiElementClass = "u-pagination-v1-1--active",
EllipsesElementClass = "g-pa-7-14",
})安装 Nuget 包:
项目根目录创建绑定配置文件:bundleconfig.json
示例:
[
{
"outputFileName": "wwwroot/css/site.min.css",
"inputFiles": [
"wwwroot/css/site.css",
"wwwroot/css/custom.css"
]
},
{
"outputFileName": "wwwroot/js/site.min.js",
"inputFiles": [
"wwwroot/js/site.js"
],
"minify": {
"enabled": true,
"renameLocals": true
},
"sourceMap": false
}
]配置文件中所有路径都相对于项目根目录(而非静态文件根目录 wwwroot),因此配置的路径都要以“wwwroot”开头。
outputFileName 是压缩合并后的文件,inputFiles 是被压缩合并的原始文件集合。
对于 js 配置部分,minify.enabled 配置是否缩小,renameLocals 配置是否修改变量名,sourceMap 配置是否生成 map 映射文件。
引用示例:
<link rel="stylesheet" href="~/css/site.min.css" asp-append-version="true" />asp-append-version 表示是否在引用路径添加版本参数,可实现在文件有修改时及时在客户端浏览器中生效。
* 注意:有时候“生成”不一定生效,“重新生成”肯定会生效。
更多高级用法请参考官方文档。
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
Nuget 安装:Pomelo.Extensions.Caching.MySql
Nuget 安装:Pomelo.Extensions.Caching.MySqlConfig.Tools(用于在 MySql 数据库中创建表和索引以进行分布式缓存的命令行工具(dotnet-mysql-cache))
2019.11 MySqlConfig.Tools 最新版本(2.0.2)不支持 .NetCore 3.0,暂时可手动创建表和索引。
2021.11 MySqlConfig.Tools 最新版(2.1.0)不支持 .Net 6.0。
未完待续,后续步骤类似于:https://xoyozo.net/Blog/Details/aspnetcore-sql-cache
services.AddDistributedMySqlCache()
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
Nuget 安装:Microsoft.Extensions.Caching.SqlServer
执行 .NET Core CLI 命令,在数据库中创建表“TestCache”
dotnet sql-cache create "数据库连接字符串" dbo TestCache
若提示
找不到 "dotnet sql-cache" 命令,请运行以下命令进行安装
则运行
dotnet tool install --global dotnet-sql-cache
表和索引创建成功提示:
Table and index were created successfully.
表结构:
在 Startup.cs 文件的方法 ConfigureServices() 中添加:
services.AddDistributedSqlServerCache(options =>
{
options.ConnectionString = "数据库连接字符串";
options.SchemaName = "dbo";
options.TableName = "TestCache";
});
在控制器中注入 IDistributedCache:
public class TestController : Controller
{
private readonly IDistributedCache cache;
public TestController(IDistributedCache cache)
{
this.cache = cache;
}
public IActionResult Index()
{
string k = "count";
int v = Convert.ToInt32(cache.GetString(k));
v++;
cache.SetString(k, v.ToString());
return Content(v.ToString());
}
}
结果:
更多用法详见官方文档:https://docs.microsoft.com/zh-cn/aspnet/core/performance/caching/distributed?view=aspnetcore-3.0
若要实现将 Session 保存至 SQL Server 中,参此文:https://xoyozo.net/Blog/Details/aspnetcore-session-sql
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
Nuget 安装:Microsoft.Extensions.Caching.StackExchangeRedis
在 Startup.cs 文件的方法 ConfigureServices() 中添加:
services.AddStackExchangeRedisCache(options =>
{
// 连接字符串
options.Configuration = "192.168.111.134,password=aW1HAyupRKmiZn3Q";
// 键名前缀
options.InstanceName = "xoyozo_";
});在控制器中注入 IDistributedCache:
public class TestController : Controller
{
private readonly IDistributedCache cache;
public TestController(IDistributedCache cache)
{
this.cache = cache;
}
public IActionResult Index()
{
string k = "count";
int v = Convert.ToInt32(cache.GetString(k));
v++;
cache.SetString(k, v.ToString());
return Content(v.ToString());
}
}结果:

更多用法详见官方文档:https://docs.microsoft.com/zh-cn/aspnet/core/performance/caching/distributed?view=aspnetcore-3.0
注:.NET 6 已不支持 Redis on Windows 3.0。
经过以上配置,Session 也会保存到 Redis 中,键名是 前缀_Guid 格式。
关于 Session,参此文:https://xoyozo.net/Blog/Details/aspnetcore-session-redis
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>这样就能缓存标签内的内容。
首先在项目根目录中创建目录 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();
}
}
| 小米电视 5 | 小米电视 5 Pro | |
| 亮点 | 全面屏、超薄金属机身、小爱同学、远场语音、PatchWall、4K、四单元喇叭、3GB+32GB | 量子点、MEMC运动补偿、4K+HDR10+、全面屏、小爱同学、远场语音、PatchWall、4GB+64GB |
| 分辨率 | 3840x2160(4K) | |
| 背光 | 侧入式 | |
| 可视角度 | 178° | |
| 刷新率 | 60Hz | |
| 广色域 | NTSC 85% | NTSC 108% |
| CPU | Cortex A55 | |
| GPU | Mali-G31 MP2 | |
| 内存 | 3GB | 4GB |
| 闪存 | 32GB | 64GB |
| 解码能力 | 8K 视频解码 | |
| 无线 | 2.4GHz/5GHz、蓝牙、红外 | |
| 尺寸 | 55 英寸 长 1227mm 高 717.8mm 含脚架高 773.2mm 65 英寸 长 1445.9mm 高 831.4mm 含脚架高 898mm 75 英寸 长 1676.5mm 宽 973.3mm 含脚架高 1037.7mm | |
| 首发价格 | 55" ¥2999 65" ¥3999 75" ¥7999 | 55" ¥3699 65" ¥4999 75" ¥9999 |
| 首发价格(舍入) | 55" ¥3000 65" ¥4000 75" ¥8000 | 55" ¥3700 65" ¥5000 75" ¥10000 |
* 本文凡使用变量 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
当 MySQL 中使用 tinyint(1) 作为布尔值类型时,ASP.NET Core DBFirst 创建模型时会将其定义为 sbyte,运行时会抛出异常:
InvalidCastException: Unable to cast object of type 'System.Boolean' to type 'System.SByte'.
我们可以在数据库连接字符串中加入 TreatTinyAsBoolean=false 来实现不抛出异常,但程序中赋值和判断该字段时还是会比较麻烦。
因此我更倾向于将数据库中该字段类型改为 bit(1)。
2019.11.28 注:这回遇到 tinyint(1) 映射到了 C# 的 bool,而 bit(1) 却映射到了 ulong。具体什么原因未作排查,反正哪个最终为 bool 就选哪个吧。