SqlNullValueException when executing a stored procedure with MySqlCommand
我正在编写一个 C# 应用程序来从 MySQL 数据库中检索食谱,使用 Dapper for ORM。到目前为止,我已经用 C# 编写了带有直接查询的 DAL(我知道这是不安全的),而且效果很好。我现在已经开始过渡到带有参数的存储过程,以更好地保护数据库免受 SQL 注入,并且尽可能地使用接近最佳实践。
但是,当我使用 Dapper 的
但是,如果我将查询作为字符串文字 SQL 语句执行,或者使用字符串文字来调用存储过程,则可以正常工作。我知道数据在那里,而不是空,因为当它直接在 MySQL 中使用我知道存在的设置 ID 号运行时它可以工作。我还尝试使用我知道存在的 id 在 C# 中运行下面列出的方法,其中一些工作正常,其中一些返回所述错误。
一旦我进行
非常感谢您在解决该呼叫可能失败的地方的任何帮助。
我已经在底部包含了堆栈跟踪,我还无法理解。我仍然需要学习理解堆栈跟踪。
编辑:
我在 SQL Server 中重新创建了 MySql 数据库,并创建了一个新的 DAL 连接器。所有这些都完全反映了 MySql 结构和 DAL。
中的存储过程交互的方式
我的食谱课:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | public class Recipe { [Description("id")] public int Id { get; set; } [Description("name")] public string Title { get; set; } [Description("description")] public string Description { get; set; } [Description("source_site")] public string SourceSite { get; set; } } |
这是我在 MySQL 中的
1 2 3 4 5 6 | recipes ============= id (pk) | INT | Not Null | Auto-Increment name | VARCHAR(45) | Not Null | description | VARCHAR(250) | Allow Null | source_site | VARCAHR(200) | Allow Null | |
这是我用来设置自定义映射的帮助器类,因此我的列不需要匹配属性名称:
1 2 3 4 5 6 7 8 9 10 11 | public class Helper { public static void SetTypeMaps() { var recipeMap = new CustomPropertyTypeMap(typeof(Recipe), (type, columnName) => type.GetProperties().FirstOrDefault(prop => GetDescriptionFromAttribute(prop) == columnName)); SqlMapper.SetTypeMap(typeof(Recipe), recipeMap); // Other custom mappers omitted } |
我正在使用的存储过程:
1 2 3 4 5 6 | PROCEDURE `sp_recipes_GetByRecipeId`(IN RecipeId INT) BEGIN SELECT r.* FROM recipes r WHERE r.id = RecipeId; END |
现在介绍我在 DAL 中使用的各种版本的方法(为方便起见,我在此处对其进行了编号):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 | /// This does not work public async Task<Recipe> GetRecipeByIdAsync1(int id) { using (IDbConnection db = new MySqlConnection(GlobalConfig.CnnString("CookbookTest1"))) { var p = new DynamicParameters(); p.Add("RecipeId", id, dbType: DbType.Int32, direction: ParameterDirection.Input); // This is the line where the exception occurs var result = await db.QueryAsync<Recipe>("sp_recipes_GetByRecipeId", p, commandType: CommandType.StoredProcedure); return result.FirstOrDefault(); } } // This also does not work public async Task<Recipe> GetRecipeByIdAsync2(int id) { using (IDbConnection db = new MySqlConnection(GlobalConfig.CnnString("CookbookTest1"))) { // This is the line where the exception occurs var result = await db.QueryAsync<Recipe>("sp_recipes_GetByRecipeId", new {RecipeID = id}, commandType: CommandType.StoredProcedure); return result.FirstOrDefault(); } } // Nor this public async Task<Recipe> GetRecipeByIdAsync3(int id) { using (IDbConnection db = new MySqlConnection(GlobalConfig.CnnString("CookbookTest1"))) { // This is the line where the exception occurs var result = await db.QueryAsync<Recipe>("sp_recipes_GetByRecipeId", new {id}, commandType: CommandType.StoredProcedure); return result.FirstOrDefault(); } } // This works perfectly, but I'm not sure how safe it is public async Task<Recipe> GetRecipeByIdAsync4(int id) { using (IDbConnection db = new MySqlConnection(GlobalConfig.CnnString("CookbookTest1"))) { var result = await db.QueryAsync<Recipe>($"call sp_recipes_GetByRecipeId({id})"); return result.FirstOrDefault(); } } // And of course, this works, but is horrible practice public async Task<Recipe> GetRecipeByIdAsync5(int id) { using (IDbConnection db = new MySqlConnection(GlobalConfig.CnnString("CookbookTest1"))) { var result = await db.QueryAsync<Recipe>($"SELECT * FROM recipes WHERE recipes.id = {id}"); return result.FirstOrDefault(); } } |
如果有人想要连接字符串
1 2 3 | <connectionStrings> </connectionStrings> |
堆栈跟踪:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | System.Data.SqlTypes.SqlNullValueException HResult=0x80131931 Message=Data is Null. This method or property cannot be called on Null values. Source=MySql.Data StackTrace: at MySql.Data.MySqlClient.MySqlDataReader.GetFieldValue(Int32 index, Boolean checkNull) at MySql.Data.MySqlClient.MySqlDataReader.GetString(Int32 i) at MySql.Data.MySqlClient.MySqlDataReader.GetString(String column) at MySql.Data.MySqlClient.SchemaProvider.GetProcedures(String[] restrictions) at MySql.Data.MySqlClient.ISSchemaProvider.GetProcedures(String[] restrictions) at MySql.Data.MySqlClient.ISSchemaProvider.GetSchemaInternal(String collection, String[] restrictions) at MySql.Data.MySqlClient.SchemaProvider.GetSchema(String collection, String[] restrictions) at MySql.Data.MySqlClient.MySqlConnection.GetSchemaCollection(String collectionName, String[] restrictionValues) at MySql.Data.MySqlClient.ProcedureCache.GetProcData(MySqlConnection connection, String spName) at MySql.Data.MySqlClient.ProcedureCache.AddNew(MySqlConnection connection, String spName) at MySql.Data.MySqlClient.ProcedureCache.GetProcedure(MySqlConnection conn, String spName, String cacheKey) at MySql.Data.MySqlClient.StoredProcedure.GetParameters(String procName) at MySql.Data.MySqlClient.StoredProcedure.CheckParameters(String spName) at MySql.Data.MySqlClient.StoredProcedure.Resolve(Boolean preparing) at MySql.Data.MySqlClient.MySqlCommand.ExecuteReader(CommandBehavior behavior) at MySql.Data.MySqlClient.MySqlCommand.ExecuteDbDataReader(CommandBehavior behavior) at System.Data.Common.DbCommand.ExecuteDbDataReaderAsync(CommandBehavior behavior, CancellationToken cancellationToken) at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Runtime.CompilerServices.TaskAwaiter.ValidateEnd(Task task) at Dapper.SqlMapper.<QueryAsync>d__33`1.MoveNext() in C:\\projects\\dapper\\Dapper\\SqlMapper.Async.cs:line 468 at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult() at CookbookLibrary.DataAccess.MySqlConnector.<TestStoredProcAsync>d__5.MoveNext() in C:\\Users\\cyclone\\Desktop\\VS Projects\\DigitalCookbook\\CookbookLibrary\\DataAccess\\MySqlConnector.cs:line 119 at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult() at DigitalCookbook.ViewModel.MainWindowModel.<TestProcedure>d__38.MoveNext() in C:\\Users\\cyclone\\Desktop\\VS Projects\\DigitalCookbook\\DigitalCookbook\\ViewModel\\MainWindowModel.cs:line 228 at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Runtime.CompilerServices.TaskAwaiter.GetResult() at DigitalCookbook.ViewModel.MainWindowModel.<<get_TestCommand>b__31_0>d.MoveNext() in C:\\Users\\cyclone\\Desktop\\VS Projects\\DigitalCookbook\\DigitalCookbook\\ViewModel\\MainWindowModel.cs:line 114 |
这看起来像是 Oracle 的 MySQL 连接器/NET(又名
我建议切换到 MySqlConnector;它是 MySQL 的备用 ADO.NET 库,与 Dapper 具有很好的兼容性,并修复了 MySQL Connector/NET 中的许多已知错误。 MySqlConnector 也有真正的异步 I/O 支持,这在 Connector/NET 中没有实现;如果您想在代码中使用
如果您想继续使用 Oracle 的 MySQL 连接器/NET,您可以通过将
更新:查看 Connector/NET 源代码后,您的数据库似乎有一些它不期望的数据。以下两个查询中的任何一个是否产生行?如果是,哪些值是
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | SELECT * FROM information_schema.routines WHERE specific_name IS NULL OR routine_schema IS NULL OR routine_name IS NULL OR routine_type IS NULL OR routine_definition IS NULL OR is_deterministic IS NULL OR sql_data_access IS NULL OR security_type IS NULL OR sql_mode IS NULL OR routine_comment IS NULL OR definer IS NULL; SELECT * FROM mysql.proc WHERE specific_name IS NULL OR db IS NULL OR name IS NULL OR type IS NULL OR body IS NULL OR is_deterministic IS NULL OR sql_data_access IS NULL OR security_type IS NULL OR sql_mode IS NULL OR comment IS NULL OR definer IS NULL; |
您使用的是什么 MySQL 服务器(MySQL、MariaDB、Amazon Aurora)以及哪个版本?