在数据库连接字符串可设置是否将 tinyint(1) 映射为 bool,否则为 sbyte:
TreatTinyAsBoolean=false/true
tinyint(1) 一般用于表示 bool 型字段,存储内容为 0 或 1,但有时候也用来存储其它数字。
以 Discuz! 的表 pre_forum_post 为例,字段 first 和 invisible 都是 tinyint(1),但 first 只储存 0 和 1,invisible 却有 -1、-5 之类的值。
因此我们一般设置 TreatTinyAsBoolean=false,程序中 first 与 invisible 均以 sbyte 处理。
设置 TreatTinyAsBoolean=true 后,EF 或 EF Core 自动将该类型映射为 bool,方便在程序中作进一步处理。
一旦设置 TreatTinyAsBoolean=true,那么所有查询结果中 tinyint(1) 字段的返回值永远只有 1 和 0(即 True/False),即使真实值为 -1,也返回 1。
因为我们必须在确保所有的 tinyint(1) 类型字段都仅表示布尔值是才设置 TreatTinyAsBoolean=true。
一旦部分 tinyint(1) 类型字段用于存放 0 和 1 以外的数,那么就应该设置 TreatTinyAsBoolean=false。
在数据库优先的项目中,以 TreatTinyAsBoolean=false 生成数据模型后,可将明确为布尔类型的字段改为 bool。列出 MySQL 数据库中所有表所有字段中类型为 tinyint(1) 的字段值
以 .edmx 为例:
在项目中搜索该字段名,在搜索结果中找到 .edmx 文件中的两处。
.edmx 文件中的注释已经表明其包含 SSDL、CSDL、C-S mapping 三块内容,
在 SSDL content 下方找到该字段:
<Property Name="字段名" Type="tinyint" Nullable="***" />
改为
<Property Name="字段名" Type="bool" Nullable="***" />
在 CSDL content 下方找到该属性:
<Property Name="属性名" Type="SByte" Nullable="***" />
改为
<Property Name="属性名" Type="Boolean" Nullable="***" />
在解决方案管理器中展开 .edmx ->库名.tt -> 表名.cs 文件,
将模型类中的属性
public sbyte invisible { get; set; }
改为
public bool invisible { get; set; }
或 sbyte? 改为 bool?。
在 EF Core 中,直接打开对应数据表的 .cs 文件,更改属性类型即可。
相关报错:
错误: 指定的成员映射无效。类型中的成员的类型“Edm.SByte[Nullable=False,DefaultValue=]”与类型中的成员的“MySql.bool[Nullable=False,DefaultValue=]”不兼容。
InvalidOperationException: The property '***' is of type 'sbyte' which is not supported by the current database provider. Either change the property CLR type, or ignore the property using the '[NotMapped]' attribute or by using 'EntityTypeBuilder.Ignore' in 'OnModelCreating'.
尝试先连接一次能解决此问题(概率),非常的莫名其妙:
using Data.Discuz.db_bbs2021Context dbd = new();
var conn = dbd.Database.GetDbConnection();
conn.Open();
conn.Close();
参考:
https://mysqlconnector.net/connection-options/
https://stackoverflow.com/questions/6656511/treat-tiny-as-boolean-and-entity-framework-4
2023年1月注:本文适用于 Pomelo.EntityFrameworkCore.MySql 6.0,升级到 7.0 后会出现:
System.InvalidOperationException:“The 'sbyte' property could not be mapped to the database type 'tinyint(1)' because the database provider does not support mapping 'sbyte' properties to 'tinyint(1)' columns. Consider mapping to a different database type or converting the property value to a type supported by the database using a value converter. See https://aka.ms/efcore-docs-value-converters for more information. Alternately, exclude the property from the model using the '[NotMapped]' attribute or by using 'EntityTypeBuilder.Ignore' in 'OnModelCreating'.”

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>
这样就能缓存标签内的内容。
安装最新的:MySQL for Visual Studio
安装版本一致的:MySQL Connector Net、MySql.Data 和 MySql.Data.Entity
若仍闪退,在 MySQL Application Configuration 中勾选 Entity Framework 的 Use MySQL with Entity Framework
或直接升级 MySql.Data 到 8.x

当 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 就选哪个吧。

本文使用 Pomelo 提供的 Pomelo.EntityFrameworkCore.MySql,如使用 MySql.Data.EntityFrameworkCore 请移步。
对比 MySql.Data.EntityFrameworkCore 与 Pomelo.EntityFrameworkCore.MySql
本文以 Visual Studio 2019、ASP.NET Core 3.0 开发环境为例。
新建 ASP.NET Core Web 应用程序。
安装 NuGet 包:
Microsoft.EntityFrameworkCore.Tools
Pomelo.EntityFrameworkCore.MySql(3.0.0 预发行版以上)
根据已有数据库创建数据模型。在 NuGet 的程序包管理(Package Manager)控制台中(PowerShell)执行命令:
Scaffold-DbContext "server=数据库服务器;uid=数据库用户名;pwd=数据库密码;database=数据库名;" Pomelo.EntityFrameworkCore.MySql -OutputDir Data -Force
.Net Core CLi:
dotnet ef dbcontext scaffold "server=数据库服务器;uid=数据库用户名;pwd=数据库密码;database=数据库名;" Pomelo.EntityFrameworkCore.MySql -o Data -f
搞定。
补充:其它数据库提供程序请参考:https://docs.microsoft.com/zh-cn/ef/core/providers/
代码参数说明:
-OutputDir (-o) *** 实体文件所存放的文件目录
-ContextDir *** DbContext文件存放的目录
-Context *** DbContext文件名
-Schemas *** 需要生成实体数据的数据表所在的模式
-Tables(-t) *** 需要生成实体数据的数据表的集合
-DataAnnotations
-UseDatabaseNames 直接使用数据库中的表名和列名(某些版本不支持)
-Force (-f) 强制执行,重写已经存在的实体文件
更多高级用法请参考官方文档。

思路:使用 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);
}
}

前言:
ChatGPT 给了 3 条建议:
在应用程序中正确释放数据库连接。确保在使用完数据库连接后,将其关闭并将其返回到连接池中。您可以使用 using 语句来确保连接在使用完毕后被正确释放。
调整连接池的大小。默认情况下,连接池的最大大小为 100。如果您的应用程序需要更多的连接,则可以增加连接池的大小。您可以在连接字符串中设置 Max Pool Size 属性来调整连接池的大小。
调整连接池的超时时间。默认情况下,连接池中的连接在 30 秒钟内未使用时将被关闭。如果您的应用程序需要更长的连接时间,则可以增加连接池的超时时间。您可以在连接字符串中设置 Connection Lifetime 属性来调整连接池的超时时间。
亲测有效,尤其是第 3 条,原因是 Connection Lifetime 的默认值是 0,即没有超时限制。
—— 2023.5
一般地,我们使用 EF 连接数据库前会先初始化一个数据库上下文:
dbEntities db = new dbEntities();
虽然 ASP.NET 会在查询完毕后自动关闭该连接,但是在什么情况下回收等都是不确定的,所以会导致 MySQL 中出现很多 Sleep 的连接(执行命令 SHOW FULL PROCESSLIST
可见),占用数据库的连接数,除非主动调用 Dispose():
db.Dispose();
官方建议的写法是使用 using
语法:
using (dbEntities db = new dbEntities())
{
}
using 会自动调用 Dispose()。这样对减少连接数是很有效的,但官方提示为了提高下一次连接的速度,并不会完全关闭所有连接。
C# 8 建议写法:
using dbEntities db = new dbEntities();
在实际项目中(该项目有 500+处数据库连接)测试结果,在不执行 Dispose() 时稳定为 140 个左右连接数,使用 using 或 Dispose() 后稳定变为 40 个左右。
如果不小心在 using 外部或 Dispose() 后再次对该上下文执行查询操作会出现异常:
无法完成该操作,因为 DbContext 已释放。
或
此 ObjectContext 实例已释放,不可再用于需要连接的操作。
所以要避免出现这种情况。这里还有一种另类的解决方法(不建议),根据上下文的特性,只要在 using 内查询一次(譬如视图中需要用到的导航属性,即外键关联的表),就可以在外部使用这个属性。
(建议)在 ASP.NET MVC 或 Web API 项目中,如果一个控制器中仅在 Action 外部定义一个 DbContext,那么,只要重写该控制器的 Dispose() 方法即可:
private dbEntities db = new dbEntities();
protected override void Dispose(bool disposing)
{
if (disposing)
{
db.Dispose();
}
base.Dispose(disposing);
}
上下文使用 private 修饰即可。
