none
Problema com casas decimais OLEDB vs Access 2003. RRS feed

  • Pergunta

  • Olá,

    Estou passando por uma situação que já esta se tornando bem incomoda:

    Temos um código C# que acessa um banco Access atraves de OleDb, quando vamos inserir um valor MOEDA(Currency) que em .Net equivale a Decimal o valor tem as casas decimais ignoradas:

    Ex: 15,50   fica salvo na tabela como 1550

    Apos pesquisar me deparei com esse post. A questão é:

    •Mudar o CultureInfo da aplicação não esta resolvendo

    •Alterar o operador decimal no SISTEMA OPERACIONAL de vírgula para ponto faz com que o problema seja sanado mas esta fora de cogitação ter de alterar essas configurações toda vez que instalar o aplicativo.

    •Não posso converter os valores para string.

    Alguém tem uma solução ou ja passou por algo parecido?

    De antemão, obrigado.


    terça-feira, 10 de setembro de 2013 20:12

Respostas

  • Por acaso é feito assim:

    public void Gravar(String Nome, DateTime Data, Decimal Salario, Boolean Status)
    {
    	using (OleDbConnection Conexao = new OleDbConnection(StringConexao))
    	{
    		Conexao.Open();
    		using (OleDbCommand Commando = Conexao.CreateCommand())
    		{
    			Commando.CommandType = CommandType.Text;
    			Commando.CommandText = "INSERT INTO Valores(Nome, Data, Salario, Status) VALUES(@Nome, @Data, @Salario, @Status);";
    			Commando.Parameters.Add("@Nome", OleDbType.VarChar, 50).Value = Nome;
    			Commando.Parameters.Add("@Data", OleDbType.Date).Value = Data;
    			Commando.Parameters.Add("@Salario", OleDbType.Currency).Value = Salario;
    			Commando.Parameters.Add("@Status", OleDbType.Boolean).Value = Status;
    			Commando.ExecuteNonQuery();
    		}
    		Conexao.Close();
    	}
    }

    No Banco de dados Acess coloque: Moeda com 2 casas decimais

    Na Programação do C# (Visual Studio) coloque o dado do tipo Decimal e no Paramenters o OleDbType como Currency

    Código testado e 100% rodando todos esses tipos de campos ???

    É esse o problema ?????????????????

    Só mais uma dica use sempre Paramenters ele tem um grande função de converte para o tipo relacionado do banco ...


    Fúlvio Cezar Canducci Dias

    quinta-feira, 12 de setembro de 2013 00:00
  • Olá, após um pouco de pesquisa mudamos parte da arquitetura de uma das classes, a classe que estava com esse probleminha nos valores do tipo Currency. O método de atualização dessa classe esta usando o OleDbDataAdapter e OleDbCommandBuilder. As outras classes que acessam banco  continuam no método anterior que é suficiente.

    //Inicia o adapter com o comando

    OleDbDataAdapter adapter2 = new OleDbDataAdapter(Cmd); adapter2.SelectCommand = UpdateCmd; OleDbCommandBuilder builder = new OleDbCommandBuilder(adapter2); DataTable temp = new DataTable(); Conn.Open(); adapter2.Fill(temp); //registros é uma datatable que contem os dados atuais (uma versão mais atual de temp) temp.Merge(registros, false); builder.GetUpdateCommand(); //os valores são inseridos de forma correta no banco. adapter2.Update(temp)

    Obs: desempenho dessa rotina parece um pouco menor, do que o desempenho da rotina que monta o comando com os parametros 1 a 1.

    Obrigado a todos pela ajuda.

    • Editado Isaias S. Silva terça-feira, 17 de setembro de 2013 12:22
    • Marcado como Resposta Isaias S. Silva terça-feira, 17 de setembro de 2013 12:22
    • Não Marcado como Resposta Isaias S. Silva terça-feira, 17 de setembro de 2013 12:23
    • Marcado como Resposta Isaias S. Silva terça-feira, 17 de setembro de 2013 12:23
    terça-feira, 17 de setembro de 2013 12:21

Todas as Respostas

  • Boa noite Isaias,

    Durante esta semana na Empresa passei pela mesma dificuldade que você. E a solução que encontrei foi simples.

    Na Empresa onde trabalho trabalhamos em camadas, ou seja, para chegar a solução tive que alterar em duas camadas, Front-End e DAO.

    Ex.:

    Na camada de apresentação eu fiz da seguinte maneira:

    clienteDTO.Salario = Convert.ToDouble(txtSalarioCliente.Text.Replace("R$", "")); //Aqui para trocar o Currency;

    Já na camada de acesso a dados fiz da seguinte maneira, detalhe o valor é Double mesmo:

    comando.Parameters.Add(new OleDbParameters("@SalarioCliente", clienteDTO.Salario.ToString().Replace(",", ".")));

    Portanto Isaias, se você realizar este procedimento, utilizando o método Replace apenas na camada de acesso a dados vai salvar 15.50.

    Espero ter ajudado e, qualquer coisa me responda.

    Se a resposta te ajudou, marcar como útil por favor.

    Obrigado,

    Victor Oliveira - Programador .NET

    quarta-feira, 11 de setembro de 2013 02:30
  • Olá Victor e obrigado pela resposta,

    Mas ainda não funcionou, o problema persiste.

    updateCmd.Parameters[item.ColumnName].Value = dr[item.ColumnName].ToString().Replace(",", ".");

    Esta linha esta dentro de um loop foreach que preenche os parâmetros de acordo com os valores de um DataRow. mesmo convertendo os valores para double antes de preencher as DataRows e mesmo usando o Replace(), o problema persiste. Ja tentamos varias formas, converter para double, decimal, string (convertendo novamente depois), mas aparentemente, o resultado apenas sai como esperado quando trocamos o separador diretamente no SO, o que não seria viável. Ou será que ainda estou fazendo algo errado?

    Aqui esta o valor como esta sendo inserido na DataRow da DataTable, agora esta double, já tentei com decimal (9.49M), sem sucesso:


    Essa linha é onde os parâmetros são criados, dentro de um outro loop foreach:

    updateCmd.Parameters.Add(item.ColumnName, Util.ToOleDbType(item.DataType));

    A classe util acima cria o parâmetro como tipo Decimal (System.Data.DbType.Decimal).

    Alguma ideia?

    quarta-feira, 11 de setembro de 2013 13:58
  • Mais um detalhe, ja fiz com que ele retornasse o tipo System.Data.DbType.Currency quando o campo for System.Decimal no .NET e a aplicação funcionou como desejado. Mas o Analista disse que essa é uma forma incorreta de tratar os dados visto que no banco Access temos o tipo Currency e tipo numérico Decimal como tipos distintos, logo poderia causar problemas na precisão dos valores, segundo o mesmo.


    quarta-feira, 11 de setembro de 2013 14:03
  • Isaias,

    Deixa eu ver se compreendi corretamente.

    Em um valor monetário (ex.: R$ 9,49) você quer pretende gravar no Banco de Dados na seguinte forma 9.49. Correto? Porém, eu sei que um valor Double ficará 949.00 ou em Banco de Dados como o SQL Server ou MySQL se você não der um Replace na vírgula apresentará erros.

    Porque na Empresa onde trabalho utilizamos o AJAX para máscaras em caixa de textos com valores monetários. Portanto automaticamente quando o usuário informa R$ 9,49 a vírgula e o cifrão aparecem.

    No primeiro estágio, no Codebehind utilizo o Replace por causa do cifrão para enviar os valores para o Objeto.

    Quando o Objeto atribui os valores na instrução T-SQL aí consigo com que o valor seja gravado como 9.49, eliminando assim os zeros após o ponto. Utilizando o método acima clienteDTO.Salario.ToString().Replace(",", ".")));.

    quarta-feira, 11 de setembro de 2013 23:02
  • Eu poderia ver o seu código todo?

    Outra coisa andei lendo todo o texto acima, desculpa, mas, calma no que dizem!!!

    Manda para mim o código por favor?


    Fúlvio Cezar Canducci Dias

    quarta-feira, 11 de setembro de 2013 23:25
  • Por acaso é feito assim:

    public void Gravar(String Nome, DateTime Data, Decimal Salario, Boolean Status)
    {
    	using (OleDbConnection Conexao = new OleDbConnection(StringConexao))
    	{
    		Conexao.Open();
    		using (OleDbCommand Commando = Conexao.CreateCommand())
    		{
    			Commando.CommandType = CommandType.Text;
    			Commando.CommandText = "INSERT INTO Valores(Nome, Data, Salario, Status) VALUES(@Nome, @Data, @Salario, @Status);";
    			Commando.Parameters.Add("@Nome", OleDbType.VarChar, 50).Value = Nome;
    			Commando.Parameters.Add("@Data", OleDbType.Date).Value = Data;
    			Commando.Parameters.Add("@Salario", OleDbType.Currency).Value = Salario;
    			Commando.Parameters.Add("@Status", OleDbType.Boolean).Value = Status;
    			Commando.ExecuteNonQuery();
    		}
    		Conexao.Close();
    	}
    }

    No Banco de dados Acess coloque: Moeda com 2 casas decimais

    Na Programação do C# (Visual Studio) coloque o dado do tipo Decimal e no Paramenters o OleDbType como Currency

    Código testado e 100% rodando todos esses tipos de campos ???

    É esse o problema ?????????????????

    Só mais uma dica use sempre Paramenters ele tem um grande função de converte para o tipo relacionado do banco ...


    Fúlvio Cezar Canducci Dias

    quinta-feira, 12 de setembro de 2013 00:00
  • Isaias,

    Deixa eu ver se compreendi corretamente.

    Em um valor monetário (ex.: R$ 9,49) você quer pretende gravar no Banco de Dados na seguinte forma 9.49. Correto? Porém, eu sei que um valor Double ficará 949.00 ou em Banco de Dados como o SQL Server ou MySQL se você não der um Replace na vírgula apresentará erros.

    Porque na Empresa onde trabalho utilizamos o AJAX para máscaras em caixa de textos com valores monetários. Portanto automaticamente quando o usuário informa R$ 9,49 a vírgula e o cifrão aparecem.

    No primeiro estágio, no Codebehind utilizo o Replace por causa do cifrão para enviar os valores para o Objeto.

    Quando o Objeto atribui os valores na instrução T-SQL aí consigo com que o valor seja gravado como 9.49, eliminando assim os zeros após o ponto. Utilizando o método acima clienteDTO.Salario.ToString().Replace(",", ".")));.

    Olá,

    Infelizmente não posso postar o código mas vou tentar ser claro e sucinto:

    tenho aqui duas camadas, uma é a biblioteca de procedimentos e a outra é a camada de acesso a dados (usando um banco MDB ). Favor, deixe o front-end de lado por enquanto.

    A camada de procedimentos passa um dataTable para a camada de acesso ao banco, com os dados que devem ser inseridos na tabela X. Nessa Datatable temos um campo(preço) de valor correspondente a 9,49.

    representação do campo na DataTable.

    A tabela X  possui um campo (preço) do tipo Unidade monetária que tem 4 casas decimais, isso é uma regra do sistema, não posso alterar.

    Agora vou explicar como estou montando os parâmetros:

    Tenho uma variável cmd (OleDbCommand)  e o cmd.CommandText fica assim:

    "UPDATE [tabela X] SET  Preco = ? WHERE (Chave = ?)"

    para adicionar os parametros em cmd é usado um loop que verifica verifica as colunas da DataTable. o tipo do parametro é dado usando uma classe utilitaria, como eu havia dito.

    ClasseUtilitaria utilitaria = new ClasseUtilitaria(); foreach (DataColumn coluna in Dtabela.Columns){ //codigo faz algumas verificações //por fim adiciona o parametro no cmd cmd.Parameters.Add(coluna.ColumnName, utilitaria.RetornarTipoDeParametro(coluna.DataType); }

    depois em outro loop inserimos os valores de cada parametro:

    //aqui existe um loop para DataRow
    //logo depois esse para cada coluna
    
    foreach (DataColumn coluna in Dtabela.Columns){
     //faz algumas verificações
     //Coloca os valores dos parametros
     cmd.Parameters[coluna.ColumnName].Value = DataRow[coluna.ColumnName];
    
     }

    No cmd, o parametro Preço do tipo OleDbType.Decimal: 9.49M

    Por fim:

    cmd.ExecuteNonQuery();

    E os valores na tabela ficam: R$ 949,0000

    As considerações:

    A Classe Utilitaria retorna um OleDbType.Decimal (esse é o problema, deveriamos estar voltando OleDbType.Currency)

    Usei uma expressão quando o DataType da DataTable é Decimal e criei o parametro com o tipo OleDbType.Currency:

    ClasseUtilitaria utilitaria = new ClasseUtilitaria();
    foreach (DataColumn coluna in Dtabela.Columns){
     
      cmd.Parameters.Add(coluna.ColumnName, coluna.DataType == typeof(System.Decimal) ? OleDbType.Currency : utilitaria.RetornarTipoDeParametro(coluna.DataType));
    
     

    O código acima cria os parametros com o tipo OleDbType.Currency e insere os valores de forma correta no banco, mas não posso usa-lo pois como disse anteriormente o Analista afirmou que isso vai gerar problemas quando o campo for numérico decimal na tabela (OleDbType.Decimal).

    Então a pergunta agora é: para inserir os dados de maneira correta, seria possivel diferenciar o tipo na tabela antes de dar o comando update (diferenciar OleDbType.Decimal e OleDbType.Currency) ?  há outra solução?





    quinta-feira, 12 de setembro de 2013 13:04
  • Eu poderia ver o seu código todo?

    Outra coisa andei lendo todo o texto acima, desculpa, mas, calma no que dizem!!!

    Manda para mim o código por favor?


    Não posso postar o código, mas expliquei detalhadamente o que acontece no reply ao Victor, peço que de uma olhada, obrigado.

    quinta-feira, 12 de setembro de 2013 13:05

  • Na Programação do C# (Visual Studio) coloque o dado do tipo Decimal e no Paramenters o OleDbType como Currency

    Olá, esse é o X da questão, como eu disse agora a pouco, a criação dos campos é feita em um loop e o tipo é dito de forma automática quando criamos o parâmetro e quando os campos forem do tipo Decimal (OleDbType.Decimal) no banco. Como vou saber quem é OleDbType.Decimal e quem é OleDbType.Currency (ambos System.Decimal no .Net ) antes do Update???
    quinta-feira, 12 de setembro de 2013 13:15
  • Eu poderia ver o seu código todo?

    Outra coisa andei lendo todo o texto acima, desculpa, mas, calma no que dizem!!!

    Manda para mim o código por favor?


    Não posso postar o código, mas expliquei detalhadamente o que acontece no reply ao Victor, peço que de uma olhada, obrigado.

    Fica complicado ajudar então!

    Fúlvio Cezar Canducci Dias

    quinta-feira, 12 de setembro de 2013 13:40

  • Na Programação do C# (Visual Studio) coloque o dado do tipo Decimal e no Paramenters o OleDbType como Currency

    Olá, esse é o X da questão, como eu disse agora a pouco, a criação dos campos é feita em um loop e o tipo é dito de forma automática quando criamos o parâmetro e quando os campos forem do tipo Decimal (OleDbType.Decimal) no banco. Como vou saber quem é OleDbType.Decimal e quem é OleDbType.Currency (ambos System.Decimal no .Net ) antes do Update???

    Sem o seu código fica complica dizer!!! é você falando de algo que não consigo ver nem imaginar! por isso até eu fiz um exemplo de cadastro!

    Agora, que no banco é Moeda com 2 casas decimais e no seu Projeto é OleDbType.Currency e o campo variável no seu projeto tem que ser Decimal !!! isso é um fato!



    Fúlvio Cezar Canducci Dias


    quinta-feira, 12 de setembro de 2013 13:42

  • Na Programação do C# (Visual Studio) coloque o dado do tipo Decimal e no Paramenters o OleDbType como Currency

    Olá, esse é o X da questão, como eu disse agora a pouco, a criação dos campos é feita em um loop e o tipo é dito de forma automática quando criamos o parâmetro e quando os campos forem do tipo Decimal (OleDbType.Decimal) no banco. Como vou saber quem é OleDbType.Decimal e quem é OleDbType.Currency (ambos System.Decimal no .Net ) antes do Update???

    Sem o seu código fica complica dizer!!! é você falando de algo que não consigo ver nem imaginar! por isso até eu fiz um exemplo de cadastro!

    Agora, que no banco é Moeda com 2 casas decimais e no seu Projeto é OleDbType.Currency e o campo variável no seu projeto tem que ser Decimal !!! isso é um fato!



    Fúlvio Cezar Canducci Dias


    Olá, eu não posso postar o código, não porque eu não quero, mas porque não posso.

    Mais uma vez, no Banco esse campo tem 4 casas decimais, voce pode confirmar isso na imagem logo acima, no reply. Isso é uma regra da aplicação, ele NÃO pode ter apenas 2 casas.

    Quer dizer que eu posso usar OleDbType.Currency mesmo quando o campo deve ser OleDbType.Decimal? isso não ira me gerar problemas no banco?

    Obrigado.



    quinta-feira, 12 de setembro de 2013 14:27

  • Na Programação do C# (Visual Studio) coloque o dado do tipo Decimal e no Paramenters o OleDbType como Currency

    Olá, esse é o X da questão, como eu disse agora a pouco, a criação dos campos é feita em um loop e o tipo é dito de forma automática quando criamos o parâmetro e quando os campos forem do tipo Decimal (OleDbType.Decimal) no banco. Como vou saber quem é OleDbType.Decimal e quem é OleDbType.Currency (ambos System.Decimal no .Net ) antes do Update???

    Sem o seu código fica complica dizer!!! é você falando de algo que não consigo ver nem imaginar! por isso até eu fiz um exemplo de cadastro!

    Agora, que no banco é Moeda com 2 casas decimais e no seu Projeto é OleDbType.Currency e o campo variável no seu projeto tem que ser Decimal !!! isso é um fato!



    Fúlvio Cezar Canducci Dias


    Olá, eu não posso postar o código, não porque eu não quero, mas porque não posso.

    Mais uma vez, no Banco esse campo tem 4 casas decimais, voce pode confirmar isso na imagem logo acima, no reply. Isso é uma regra da aplicação, ele NÃO pode ter apenas 2 casas.

    Obrigado.

    Então coloque 4 !!! VAI FUNCIONAR DO MESMO JEITO NO CÓDIGO ABAIXO!

    using (OleDbConnection Conexao = new OleDbConnection(StringConexao))
    {
    	Conexao.Open();
    	using (OleDbCommand Commando = Conexao.CreateCommand())
    	{
    		Commando.CommandType = CommandType.Text;
    		Commando.CommandText = "INSERT INTO Valores(Nome, Data, Salario, Status) VALUES(@Nome, @Data, @Salario, @Status);";
    		Commando.Parameters.Add("@Nome", OleDbType.VarChar, 50).Value = Nome;
    		Commando.Parameters.Add("@Data", OleDbType.Date).Value = Data;
    		Commando.Parameters.Add("@Salario", OleDbType.Currency).Value = Salario;
    		Commando.Parameters.Add("@Status", OleDbType.Boolean).Value = Status;
    		Commando.ExecuteNonQuery();
    	}
    	Conexao.Close();
    }


    Fúlvio Cezar Canducci Dias

    quinta-feira, 12 de setembro de 2013 14:32
  • Caro Fúlvio,

    O processo de criação dos parâmetros é feito dentro de um loop, o tipo do parâmetro é determinado por uma classe com um método que verifica o tipo .NET e converte para o OleDbType equivalente. Por gentileza, leia o post onde eu explico praticamente linha por linha como acontece o processo.

    O código que vc passou limita o comando a uma determinada Tabela o que não é viavel,visto que o BD tem quase mil tabelas. Peço por favor que vc entenda o contexto e a minha situação.

    Obrigado.

    quinta-feira, 12 de setembro de 2013 15:10
  • Caro Fúlvio,

    O processo de criação dos parâmetros é feito dentro de um loop, o tipo do parâmetro é determinado por uma classe com um método que verifica o tipo .NET e converte para o OleDbType equivalente. Por gentileza, leia o post onde eu explico praticamente linha por linha como acontece o processo.

    O código que vc passou limita o comando a uma determinada Tabela o que não é viavel,visto que o BD tem quase mil tabelas. Peço por favor que vc entenda o contexto e a minha situação.

    Obrigado.

    Caro Isaias!

    O que importa se o seu código é um Loop e faz a tipagem nele!!! #Fato

    Então, o que me importa e passar a você um exemplo daquilo que é o ideal!!! Se o seu código não ta dando certo, eu fiz questão de pedi-lo, não todo mas, a parte que não está dando certo! Oras, como ajudar uma pessoa no qual o código dela difere do trivial ... se ela não me mostra ???????????????????????

    Só para que você se posicione, eu entendi que é um LOOP e entendi que é feito em tempo de Execução, mas, preciso dizer que ninguém ta entendendo seu código e por isso foi pedido! e por isso que eu disse que infelizmente não tem como ajudar...

    Se passar o código um parte consigo resolver se não fica complicado !!!!!!!!!!!!!!!!

    OBS: Eu até imagino o que se ta fazendo pelo código do LOOP, e já sei porque, não funciona! 

     



    Fúlvio Cezar Canducci Dias

    quinta-feira, 12 de setembro de 2013 15:56
  • Fúlvio, peço perdão,

    Pode não parecer, mas eu estou te entendendo, por enquanto vou ficar no aguardo, e estou argumentando aqui com o Analista para que me deixe usar o tipo Currency quando estou criando os parâmetros. Se eu não conseguir vou pedir aqui para me deixar postar a parte do código que esta dando problemas. Caso eu tenha sucesso, irei postar a solução. Novamente peço desculpas.


    p.s. eu tambem sei pq não funciona.
    quinta-feira, 12 de setembro de 2013 16:58
  • Olá, após um pouco de pesquisa mudamos parte da arquitetura de uma das classes, a classe que estava com esse probleminha nos valores do tipo Currency. O método de atualização dessa classe esta usando o OleDbDataAdapter e OleDbCommandBuilder. As outras classes que acessam banco  continuam no método anterior que é suficiente.

    //Inicia o adapter com o comando

    OleDbDataAdapter adapter2 = new OleDbDataAdapter(Cmd); adapter2.SelectCommand = UpdateCmd; OleDbCommandBuilder builder = new OleDbCommandBuilder(adapter2); DataTable temp = new DataTable(); Conn.Open(); adapter2.Fill(temp); //registros é uma datatable que contem os dados atuais (uma versão mais atual de temp) temp.Merge(registros, false); builder.GetUpdateCommand(); //os valores são inseridos de forma correta no banco. adapter2.Update(temp)

    Obs: desempenho dessa rotina parece um pouco menor, do que o desempenho da rotina que monta o comando com os parametros 1 a 1.

    Obrigado a todos pela ajuda.

    • Editado Isaias S. Silva terça-feira, 17 de setembro de 2013 12:22
    • Marcado como Resposta Isaias S. Silva terça-feira, 17 de setembro de 2013 12:22
    • Não Marcado como Resposta Isaias S. Silva terça-feira, 17 de setembro de 2013 12:23
    • Marcado como Resposta Isaias S. Silva terça-feira, 17 de setembro de 2013 12:23
    terça-feira, 17 de setembro de 2013 12:21