Estou escrevendo um aplicativo C # para recuperar receitas de um banco de dados MySQL, usando Dapper para ORM. Até agora, escrevi meu DAL em C # com consultas diretas (que eu sei que não são seguras) e isso funciona muito bem. Agora comecei a fazer a transição para procedimentos armazenados com parâmetros para proteger melhor o banco de dados contra injeção de SQL, bem como usar o mais próximo possível das melhores práticas.
No entanto, quando estou usando Dapper's QueryAsync<T>(isso também se aplica a Query<T>) junto com DynamicParameters, recebo uma exceção, com a mensagem "Os dados são nulos. Este método ou propriedade não pode ser chamado em valores nulos."
No entanto, se eu executar a consulta como uma instrução SQL de literal de string ou usar um literal de string para chamar o procedimento armazenado, funcionará bem. Eu sei que os dados estão lá, e não nulos, porque funciona ao ser executado diretamente no MySQL com um número de identificação definido que sei que existe. Também tentei executar os métodos listados abaixo em C # com uma id que sei que existe, alguns deles funcionam bem, alguns deles retornam o erro declarado.
Não tenho ideia de onde isso está falhando depois de fazer a QueryAsync<Recipe>("...")ligação. Não sei se os parâmetros que forneço para o método não estão sendo passados para o procedimento armazenado, ou se o procedimento está retornando nulo, ou alguma outra coisa está dando errado.
Qualquer ajuda para descobrir onde isso pode estar falhando com aquela chamada seria muito apreciada. Incluí o rastreamento de pilha na parte inferior, que ainda não consigo entender. Ainda preciso aprender a entender os rastreamentos de pilha.
Edit: Eu recriei o banco de dados MySql no SQL Server e criei um novo conector DAL. Tudo refletindo exatamente a estrutura do MySql e o DAL. GetRecipeByIdAsync1(int id)funciona exatamente como esperado com o SQL Server. Portanto, deve haver algo sobre a forma como Dapper / DynamicParameters / MySql.Data está interagindo com o procedimento armazenado no MySQL
Minha aula de receita:
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; }
}
Esta é minha recipestabela no MySQL:
recipes
=============
id (pk) | INT | Not Null | Auto-Increment
name | VARCHAR(45) | Not Null |
description | VARCHAR(250) | Allow Null |
source_site | VARCAHR(200) | Allow Null |
Esta é a classe auxiliar que estou usando para definir o mapeamento personalizado para que minhas colunas não precisem corresponder aos nomes das propriedades:
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
}
O procedimento armazenado que estou usando:
PROCEDURE `sp_recipes_GetByRecipeId`(IN RecipeId INT)
BEGIN
SELECT r.*
FROM recipes r
WHERE r.id = RecipeId;
END
Agora, para as várias versões do método que estou usando em meu DAL (eu os numerei aqui para facilitar):
/// 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();
}
}
String de conexão se alguém quiser
<connectionStrings>
<add name="CookbookTest1" connectionString="Server=localhost;Database=cookbook_test1;Uid=vs_dev;Pwd=developer;" providerName="MySql.Data"/>
</connectionStrings>
Rastreamento de pilha:
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
Isso parece um bug no MySQL Connector / NET (aka MySql.Data) da Oracle . Não se parece com nenhum bug com o qual estou familiarizado nesse banco de dados de bug; pode ser necessário arquivar como um novo problema. ( Bug 75301 é semelhante, mas não é imediatamente óbvio que seja o mesmo problema.)
Eu recomendaria mudar para MySqlConnector ; é uma biblioteca ADO.NET alternativa para MySQL que tem grande compatibilidade com Dapper e corrige muitos bugs conhecidos no MySQL Connector / NET. MySqlConnector também tem suporte verdadeiro de E / S assíncrona, que não é implementado no Connector / NET; isso será importante se você quiser usar QueryAsyncem seu código.
Se você quiser continuar usando o MySQL Connector / NET da Oracle, poderá contornar o problema adicionando CheckParameters=falseà string de conexão. Observe que esta pode ser uma alteração significativa em seu código; se definir a configuração como false, você terá que garantir manualmente que os parâmetros adicionados a cada um CommandType.StoredProcedure MySqlCommandestão na mesma ordem exata do banco de dados (porque MySql.Data não os corrigirá mais para você).
Atualização: Depois de examinar o código-fonte do Connector / NET, parece que seu banco de dados contém alguns dados que não são esperados. Alguma das duas consultas a seguir produz linhas? Se sim, quais são os valores NULL?
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;
Qual servidor MySQL você está usando (MySQL, MariaDB, Amazon Aurora) e qual versão?
Jana Duggar foi aberta sobre sua busca pelo amor. Aqui está tudo o que ela disse sobre o assunto e sua janela de cinco anos para o casamento.
O astro de 'Outlander', Sam Heughan, revelou recentemente o que vai levar do set para relembrar Jamie Fraser, o papel que o tornou uma estrela.
'She Loves You' foi escrita em uma hora, gravada em um dia, e foi a música dos Beatles com uma de suas melhores apresentações de sua carreira.
Dolly Parton e sua avó Bessie tiveram um relacionamento especial. Vovó Parton estava muito doente, mas isso não impediu Dolly de pregar uma peça nela.
Você pode achar que o carvalho ou a nogueira são madeiras resistentes, mas quando se trata da madeira mais dura do mundo, elas nem chegam perto.
O oceano é repleto de beleza, mas também esconde algumas das criaturas marinhas mais assustadoras do planeta. Muitos desses animais espreitam nas profundezas do oceano, no mundo escuro e de alta pressão do fundo do mar.
Se você está enfrentando criaturas hostis ou se preparando para cenários PvP, conhecer os melhores encantamentos de espada no Minecraft pode te dar uma grande vantagem. Encantar espadas permite causar mais dano, aumentar a quantidade de itens obtidos de criaturas e prolongar a durabilidade da sua espada.
Quando as pessoas falam sobre países socialistas, geralmente imaginam o controle total do governo e a ausência de propriedade privada. Mas, na prática, as economias socialistas variam muito.
“Children” traz uma participação especial de grande nome e algumas notícias devastadoras
Este RAV4 está em excelentes condições e está preparado para um jogo de cadeiras musicais.
Imagens de drone capturaram bombeiros parecendo lutar para apagar o incêndio.
Eyes of Wakanda está diretamente relacionado ao MCU, além de atualizações sobre X-Men '97, What If..., Demolidor e muito mais.
O anel de noivado de Kate Middleton pertenceu à Princesa Diana antes do Príncipe William pedi-la em casamento. Descubra tudo o que há para saber sobre a peça histórica aqui.
John Cleese se casou com sua esposa, Jennifer Wade, em 2012. Aqui está tudo o que você precisa saber sobre a esposa de John Cleese, Jennifer Wade.
Patton Oswalt se casou com sua esposa, a também atriz Meredith Salenger, em 2017. Aqui está tudo o que você precisa saber sobre a esposa de Patton Oswalt, Meredith Salenger.
Mena Suvari é casada com o marido Michael Hope desde 2018. Aqui está tudo o que você precisa saber sobre o marido de Mena Suvari.
Isso me atinge De repente, como tantas vezes acontece É o meio do caminho tudo de novo <Não, não aquele no Pacífico, seu marrom, aquele no carnaval> Todas as manchetes em voz alta…..
Em 2022, com o lançamento do GPT-3, a habilidade de escrita parece se tornar menos importante. Com a IA, pessoas com redação ruim também podem gerar funcionários de qualidade.
No mundo acelerado e competitivo de hoje, a carreira desempenha um papel significativo no crescimento pessoal, na estabilidade financeira e na satisfação geral com a vida. No entanto, muitos indivíduos encontram-se presos em um ciclo implacável de estagnação e insatisfação, definhando gradualmente em suas vidas profissionais.
Na semana passada, notei um comunicado de imprensa, enviado via PressGazette (um site de notícias da mídia britânica). O artigo anunciava que a Acast, a empresa de publicidade e hospedagem de podcast Scandi, lideraria um consórcio de "provavelmente os editores mais influentes" em podcasting.