因 MySQL 8 默认使用 utf8mb4 字符集,如果是从 MySQL 5-7 等低版本迁移的,那么仍然是 utf8(相当于 utf8mb3)。
这在保存字符串时有可能会报错:
An error occurred while saving the entity changes. See the inner exception for details.
Incorrect string value: '\xF0\xA1\x90\x93\xE6\x9D...' for column 'Html' at row 1
即使连接字符串上加上 CharSet=utf8 或 CharSet=utf8mb3 或 CharSet=utf8mb4 也没用。
那么只能把数据库的字符集改为 utf8mb4。
更改字符集和排序方式前必须先备份数据库!
更改现有数据库的字符集和排序规则
ALTER DATABASE mydatabase CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
更改现有表的字符集和排序规则
ALTER TABLE mytable CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
更改现有列的字符集和排序规则
当列单独设置了字符集时执行,未设置的不需要执行,会继承表的字符集
ALTER TABLE mytable MODIFY mycolumn VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci
数据库中可能存在许多表,数据表中可能存在许多列,使用下面的命令可以生成批量执行的命令:
更改所有表的默认字符集和排序规则
USE database_name; -- 替换为你的数据库名 SELECT CONCAT('ALTER TABLE `', TABLE_SCHEMA, '`.`', TABLE_NAME, '` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;') FROM information_schema.TABLES WHERE TABLE_SCHEMA = 'database_name'; -- 再次替换为你的数据库名
更改所有列的字符集和排序规则
USE database_name; -- 使用你的数据库名 SELECT CONCAT('ALTER TABLE `', TABLE_SCHEMA, '`.`', TABLE_NAME, '` MODIFY `', COLUMN_NAME, '` ', COLUMN_TYPE, ' CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;') FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = 'database_name'; -- 使用你的数据库名
另外给出几条查询语句:
查看特定数据库的字符集和排序规则
SELECT DEFAULT_CHARACTER_SET_NAME, DEFAULT_COLLATION_NAME FROM information_schema.SCHEMATA WHERE SCHEMA_NAME = 'your_database_name';
查看特定表的字符集和排序规则
SELECT TABLE_SCHEMA, TABLE_NAME, TABLE_COLLATION FROM information_schema.TABLES WHERE TABLE_SCHEMA = 'your_database_name';
查看特定列的字符集和排序规则
SELECT COLUMN_NAME, CHARACTER_SET_NAME, COLLATION_NAME FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = 'your_database_name' AND TABLE_NAME = 'your_table_name';
Pomelo.EntityFrameworkCore.MySql 升级到 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'.”
对比 6.0 生成的 DbContext.cs 发现缺少了 UseLoggerFactory,按旧版修改即可。
找到 OnConfiguring 方法,上方插入:
public static readonly LoggerFactory MyLoggerFactory = new LoggerFactory(new[] {
new DebugLoggerProvider()
});
在 OnConfiguring 方法中将:
optionsBuilder.UseMySql("连接字符串");
改为:
optionsBuilder.UseLoggerFactory(MyLoggerFactory).UseMySql("连接字符串");
在数据库连接字符串可设置是否将 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 安装: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 中的会话存储在 Redis 中,可以按照以下步骤进行操作:
安装必要的 NuGet 包:
首先,确保你已经安装了 Microsoft.Extensions.Caching.StackExchangeRedis 包,这是与 Redis 集成的主要包。
在 Visual Studio 中,你可以使用 NuGet 包管理器控制台执行以下命令:
Install-Package Microsoft.Extensions.Caching.StackExchangeRedis
配置 Redis 连接:
在你的 ASP.NET Core 应用程序的 Startup.cs 文件中,使用 AddStackExchangeRedisCache 方法配置 Redis 连接。在 ConfigureServices 方法中添加以下代码:
services.AddStackExchangeRedisCache(options =>
{
options.Configuration = "your_redis_connection_string";
options.InstanceName = "your_instance_name";
});
请将 "your_redis_connection_string" 替换为你的 Redis 连接字符串,并将 "your_instance_name" 替换为你的 Redis 实例名称。
配置会话中间件:
在 Startup.cs 的 ConfigureServices 方法中,使用 AddSession 方法配置会话中间件,并在 Configure 方法中启用它:
services.AddSession(options =>
{
options.IdleTimeout = TimeSpan.FromMinutes(30); // 设置会话超时时间
});
使用会话中间件:
在 Startup.cs 的 Configure 方法中,在调用 app.UseRouting() 之前添加以下代码来启用会话中间件:
app.UseSession();
在控制器或视图中使用会话:
现在,你可以在控制器或视图中使用会话来存储和检索数据。例如:
// 存储数据到会话
HttpContext.Session.SetString("Key", "Value");
// 从会话中检索数据
var value = HttpContext.Session.GetString("Key");
通过这种方式,你可以将会话数据存储在 Redis 中,而不是默认的内存中。
请确保根据你的应用程序的需求进行适当的配置和安全措施,以确保 Redis 连接的安全性和可靠性。
当 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 就选哪个吧。
前言:
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 修饰即可。
无法向会话状态服务器发出会话状态请求。请确保 ASP.NET State Service (ASP.NET 状态服务)已启动,并且客户端端口与服务器端口相同。如果服务器位于远程计算机上,请检查 HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\aspnet_state\Parameters\AllowRemoteConnection 的值,确保服务器接受远程请求。如果服务器位于本地计算机上,并且上面提到的注册表值不存在或者设置为 0,则状态服务器连接字符串必须使用“localhost”或“127.0.0.1”作为服务器名称。
无法连接 StateService 的原因有很多种,我遇到的情况是:
VS 中使用 IISExpress 调试项目,系统没有安装 IIS,
ASP.NET 状态服务是开启状态,
没有对系统作任何修改,无缘无故无法连接。
修改注册表、配置防火墙无效(我没有用到远程连接),
执行 aspnet_regiis -i -enables 无效,
重装 .NET Framework 4.8 Developer Pack 无效,
差点就重装系统了。
最终的解决方法是:
在“启用或关闭 Windows 功能”中勾选安装:Internet Information Services -> 万维网服务 -> 应用程序开发功能 -> ASP.NET 4.8
重启系统。
Visual Studio 2019 已于本月早些时候正式发布,但 MySQL for Visual Studio 却迟迟未更新,目前的 1.2.8 版本只支持到 VS2017。表现在 VS2019 上会发生:
在“服务器资源管理器”中添加连接时,或在新建“ADO.NET 实体数据模型”时,无法看到数据源“MySQL Database (MySQL Data Provider)”
在实体数据模型设计器(Entity Data Model Designer)中执行“从数据库更新数据模型”时出现错误:尝试从数据库进行更新时,遇到类型为“System.ArgumentException”的异常。异常消息为:“无法将运行时连接字符串转换为设计时等效项。没有为提供程序“MySql.Data.MySqlClient”安装为设计目的(DDEX 提供程序)启用 Visual Studio 以便与数据库进行通信所需的库。
幸运的是 VS2019 可以与低版本 VS 共存,使用 ASP.NET 连接 MySQL 的开发者们要再等一等才能用上 VS2019 了。
这几天 MySQL 不断有产品更新,相信 MySQL for Visual Studio 2019 也会在不久的将来面世,本站将第一时间更新下载地址。
2019.8.30 Jose Ramirez(来自 MySQL 的 Windows 团队):MySQL for Visual Studio 的当前版本不支持 Visual Studio 2019。正在努力提供支持,但根据公司政策,我无法披露日期或对何时可用提供任何承诺。查看
2019.9.26 MySQL for Visual Studio 迟迟未更新,但是 VS2019 更新到最新版本(v16.3)已经一切正常了。
MySQL for Visual Studio 的 Repair 功能是鸡肋,有问题的话,Remove 后重新安装才能解决。