Mostrar mensagens com a etiqueta Programação. Mostrar todas as mensagens
Mostrar mensagens com a etiqueta Programação. Mostrar todas as mensagens

quinta-feira, março 11, 2010

Ler dados do Excel através de C#

Há cerca de de 2 anos tive de trabalhar pela primeira vez com a leitura de dados a partir de um ficheiro excel, e nessa altura até cheguei a escrever um post sobre o assunto. Mas o solução que tinha estava longe de optima, apesar de funcionar num ambiente controlado que tinha.

Num projecto que estou actualmente foi-me pedido que um utilizador pudesse fazer upload de um ficheiro excel, e que os dados fossem lidos para dentro da base de dados. Como já se tinha passado algum tempo desde do meu código original, fui investigar e tentar resolver alguns problemas que tinha, nomeadamente o problema da cultura, o que provocava que não conseguisse utilizar a minha aplicação anterior em todos os ambientes. Outra preocupação que tive desta vez foi o desempenho, a leitura é algo 'pesado' logo devemos ler o máximo de células de uma só chamada (à semelhança do que acontece quando se lêem dados de uma BD), e ter cuidado com a memória que estou a utilizar e liberta-la sempre que já não for necessário.

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Text;
using System.Threading;
using System.Reflection;
using System.IO;
using System.Xml;
using System.Web.Security;
using Excel = Microsoft.Office.Interop.Excel; 
  
    public class ExcelUtils
    {
        public static void GetDataFromExcel()
        {

            Application xlApp;
            Workbook xlWorkBook;
            Worksheet objWorkSheet;
            object misValue = Missing.Value;

            using (new LanguageHelper())
            {
                string file = "example.xls";
                xlApp = new Excel.ApplicationClass();
                xlWorkBook =
                    xlApp.Workbooks.Open(file, 0, true, 5, "", "", true, 
                  XlPlatform.xlWindows, "\t", false, false, 0, true, 1, 0);
                objWorkSheet = (Worksheet) xlWorkBook.Worksheets.get_Item(1);


                Array cod = (Array)objWorkSheet.get_Range("A4", "Z4").Value2;
                string name = cod.GetValue(1, 1).ToString();

                Console.WriteLine("cod={0}", name);
                
                xlWorkBook.Close(true, misValue, misValue);
                xlApp.Quit();

                releaseObject(objWorkSheet);
                releaseObject(xlWorkBook);
                releaseObject(xlApp);
            }

            Console.WriteLine("Ended!");
        }

        private static void releaseObject(object obj)
        {
            try
            {
                System.Runtime.InteropServices.Marshal.ReleaseComObject(obj);
                obj = null;
            }
            catch (Exception ex)
            {
                obj = null;
                Console.WriteLine("Unable to release the Object " + ex.ToString());
            }
            finally
            {
                GC.Collect();
            }
        } 
    }
    
    public class LanguageHelper : IDisposable
    {
        private CultureInfo mCurrentCulture;

        public LanguageHelper()
        {
            // save current culture and set culture to en-US
            mCurrentCulture = Thread.CurrentThread.CurrentCulture;
            Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US");
        }

        #region IDisposable Members

        public void Dispose()
        {
            // return to normal culture
            Thread.CurrentThread.CurrentCulture = mCurrentCulture;
        }

        #endregion
    } 

sexta-feira, janeiro 22, 2010

Oracle adquire Sun

A Comissão Europeia aprovou a aquisição e provavelmente o gigante vai tornar-se ainda maior. Mais informação pode ser obtida aqui.

quarta-feira, janeiro 20, 2010

Appender para TextBox em log4net

Este problema apareceu-me porque tenho uma aplicação já construída que usa o log4net para fazer log, neste caso para ficheiro, mas eu queria por essa aplicação a ser invocada por um windows form estando o log a ser escrito para uma RichTextBox.

Esta técnica não só serve para esta situação, mas para qualquer objecto que tenha uma propriedade/campo que seja do tipo string. É necessário fazer 3 coisas:
1) Criar uma classe que derive de AppenderSkeleton;
2) Registar o appender no xml de configuração;
3) Dar ao appender o objecto para o qual vai escrever.

1)
using log4net.Appender;
using System.Windows.Forms;
using log4net.Core;

namespace LogExample
{
    public class TextAppender : AppenderSkeleton
    {
        private RichTextBox logPlace;

        public RichTextBox LogPlace
        {
            get { return logPlace; }
            set { logPlace = value; }
        }

        /// 
        /// Writes the logging event to a TextBox
        /// 
        override protected void Append(LoggingEvent loggingEvent)
        {
            LogPlace.Text += string.Format("\n{0}", loggingEvent.RenderedMessage);
        }
    }
}

2)
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <configSections>
    <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler,log4net"/>
  </configSections>
  <log4net>
    <appender name="Writer" type="LogExample.TextAppender, LogExample">
      <layout type="log4net.Layout.PatternLayout">
        <conversionPattern value="%d{HH:mm:ss} %-5p - %m%n"/>
      </layout>
    </appender>

    <root>
      <priority value="info"/>
      <appender-ref ref="Writer"/>
    </root>
  </log4net>
</configuration>

3)
using System;
using System.Windows.Forms;
using log4net;
using log4net.Config;
using log4net.Appender;

namespace LogExample
{
    public partial class Log : Form
    {
        private readonly ILog log;

        public Log()
        {
            InitializeComponent();
            log = LogManager.GetLogger(typeof(Log));
            XmlConfigurator.Configure();
            IAppender[] appenders = log.Logger.Repository.GetAppenders();

            foreach (IAppender append in appenders)
            {
                if (append is TextAppender)
                {
                    ((TextAppender)append).LogPlace = logBox;
                }
            }
        }
    }
}

segunda-feira, novembro 23, 2009

Obter as últimas queries

Necessitava de obter as últimas queries que estavam a ser executadas no SQL Server, mas devido a ter concorrência no código seguindo o seu fio de execução não conseguia perceber o que estava a acontecer.

Até que descobri este artigo que explica como obter as últimas queries executadas pelo SQL Server.
SELECT deqs.last_execution_time AS [Time], dest.TEXT AS [Query]FROM sys.dm_exec_query_stats AS deqsCROSS APPLY sys.dm_exec_sql_text(deqs.sql_handle) AS destORDER BY deqs.last_execution_time DESC

quarta-feira, novembro 18, 2009

Estou a ficar esclerosado

Hoje tive o seguinte diálogo:

Sujeito A: "Sabes dizer-me o que é OOP?"
Eu: "Não faço ideia!"
Sujeito A: "E se for POO?"
Eu: "Por acaso tive uma cadeira na faculdade que tinha esse nome mas não me recordo o que significava..."

É nesta altura que chamo URSO a mim próprio, POO - Programação Orientada por Objectos

terça-feira, novembro 03, 2009

Criar um indexer num objecto

Não conhecia esta técnica, mas em C# podemos simular que um objecto é uma colecção. O truque é simples o objecto tem uma colecção interna que a expõe indirectamente através de uma propriedade.

class Tester
{
 private Dictionary<string, int> dictionary = new Dictionary<string, int>();

 // Indexer
 public int this[string key]
 {
    get { return dictionary [key]; }
    set { dictionary [key] = value; }
 }
}
//[...]

static void Main(string[] args)
{
 Tester tester = new Tester();
 //[...]
 int data = tester["xpto"];
 //[...]
}

segunda-feira, outubro 26, 2009

Mount do disco com rdesktop

Devo dizer que não tenho nada contra Linux, mas tanto o meu trajecto académico como profissional sempre me levou mais para o lado do Windows, mas não pertenço a nenhum dos "clãs".

Estava aqui no Linux e precisava de fazer um remote desktop, mas precisava de ver o disco do computador que estava a estabelecer a conexão, no computador que estava a ser acedido remotamente. E o comando é algo do género:

rdesktop [maquinaDestino] -r disk:LPT1=/opt

E não é que a pasta /opt do Linux fica acessível do lado do Windows...

quinta-feira, outubro 15, 2009

Parar um pedido em client side

O meu objectivo é ter algo que evite que o pedido chegue ao lado do servidor, ou seja, que haja algum género de confirmação do utilizador para o pedido prosseguir. O único truque está em ter um evento/função javascript que retorne false. Utilizando o seguinte exemplo de código aspx:

<asp:button id="identifier" runat="server" onclick="ClickMe" text="Send Request" onclientclick="return Confirmation()"/>

Tipicamente a função Confirmation() faz algo como mostrar um pedido de confirmação ao utilizador e mediante a resposta o pedido segue para o servidor sendo já 'atendido' em client side pela função ClickMe.
<script type="'text/javascript'">
  function GetLineConfirmation (){
      return confirm('Quer prosseguir?');
  }
</script>

quinta-feira, outubro 08, 2009

Verificar se uma função javascript existe

Por acaso até é algo extremamente fácil, apesar de nunca o ter tido a necessidade de utilizar até hoje, é tão simples como:
function VerifyExistance{
  if(window.FunctionToVerify){
     FunctionToVerify();
  }
}

terça-feira, julho 07, 2009

Questionário técnico

Foi-me pedido para elaborar meia dúzia de perguntas de cariz técnico a nível de informática. Aqui está o resultado desse pedido.
  • Quais as propriedades que tem uma transacção a nível de uma SGBD?
  • Qual a diferença entre uma linguagem compilada e interpretada?
  • O que é uma máquina virtual?
  • Para que serve o garbage collector?
  • O que entende por arquitectura em 3 camadas?
  • Qual a diferença a nível de classes entre uma derivação e uma implementação?

quarta-feira, junho 17, 2009

Concatenar datas no Excel

Normalmente uso o excel como forma de exportar ou importar dados para uma base de dados. No caso da importação é habitual concatenar as strings de diversas colunas para obter um statement. Mas concatenar campos do tipo data pode ser um pouco tricky, veja-se o seguinte exemplo:

Na célula A1 temos "Dia de Portugal" e na célula B1 temos "10-06-2009", o que acontece se noutra célula escrever: =A1& ": " & B1? O resultado será: Dia de Portugal: 39974, pois não era nada disto que se pretendia!

A forma correcta será: =A1 & ": " &TEXT(B1;"dd-mm-aaaa")

sexta-feira, março 13, 2009

Deficiencia da API do Paysafe

Ao fim de 3 dias de volta do Paysafe consegui aprender relativamente bem como utilizar a API do Paysafe e implementei uma solução que possibilita aos clientes da minha aplicação fazerem pagamentos através do Paysafe card.

Até fiquei espantado 3 dias foi mesmo muito pouco para quem não sabia nada do Paysafe, e isto porque, porque até existem exemplos muito simples de seguir e boa documentação disponibilizada pela Paysafe.

Até aqui tudo bem, o problema foi quando tentei passar para ambiente de produção. O meu ambiente de desenvolvimento é Windows/.NET, ambiente de produção é Linux/Mono, toda a aplicação funcionava em ambos os mundos e é uma aplicação com alguns milhares de ficheiros escritos em C#.

Conforme tento chamar os serviços do Paysafe levo com uma excepção:
Paysafecard.Client.PaysafecardException: Incorrect value in
configuration file. ---> System.DllNotFoundException: crypt32.dll
Vou ver que ficheiro é o crypt32.dll e descubro que é uma dll criptográfica nativa de Windows. Ok 3 dias para implementar era muito rápido, tinha de surgir algum problema. Contactamos a Paysafe para resolver o problema, visto que em .NET existem bibliotecas criptográficas que têm compatibilidade total com o Mono e seria com essas bibliotecas que o código ficaria bem feito e portável para qualquer ambiente.

Resposta foi: "Ah e tal nunca testamos essa siuação, se quiserem ter um ambiente em Linux usem a implementação de Java para comunicar com a Paysafe."

A minha vontade foi responder: "Estão a brincar comigo, não? Vocês são uma empresa com centenas de clientes, somos os primeiros a ter este problema? Eu tenho todo o meu código feito em C# e tudo funciona bem, NÃO vou fazer um módulo em Java só porque meteram um trolha a programar a vossa API."

Pelo menos podiam ter disponibilizado o código fonte da API deles que em último caso fazia eu o código, mas não, além de um código coxo querem que eu implemente também uma solução coxa.

quinta-feira, fevereiro 19, 2009

Parâmetros por referência em C#

Se não tivermos atenção ao que fazemos às vezes podemos apanhar algumas partidas. Vamos analisar um caso simples de modo a poder demonstrar que pequenas alterações podem conduzir a resultados finais totalmente diferentes.

      static void Main(string[] args)
      {
          IList list = new List();
          IList list2 = new List();
          IList list3 = new List();

          list.Add("a");
          list2.Add("a");
          list3.Add("a");

          AddToList(list);
          AddToList2(list2);
          AddToList3(ref list3);

          Console.WriteLine("List 1 count = {0}", list.Count);
          Console.WriteLine("List 2 count = {0}", list2.Count);
          Console.WriteLine("List 3 count = {0}", list3.Count);
      }

      private static void AddToList(ICollection list)
      {
          list.Add("b");
          list.Add("c");
      }

      private static void AddToList2(ICollection list)
      {
          list = new List();
          list.Add("b");
          list.Add("c");
      }

      private static void AddToList3(ref IList list)
      {
          list = new List();
          list.Add("b");
          list.Add("c");
      }
O objectivo é acertar nos valores que vão aparecer no output da consola. Sabendo que em C# as listas são passadas por referência vamos analisar o código de modo a perceber qual vai ser o output.

No primeiro caso à lista que é passada no parâmetro são adicionados mais dois elementos, logo é espectável que o seu count seja 3, pois a referência da lista está a ser partilhada dentro e fora do método.

No segundo caso antes de se adicionarem os elementos à lista está a haver uma nova referenciação, logo a referência à qual estão a ser adicionados os 2 elementos é à referência interna do método, logo fora do método a referência manterá só 1 elemento.

No último caso ao ser colocada a palavra reservada ref, estamos a dizer que a referência é partilhada tanto dentro como fora do método, ou seja, tudo o que fizermos ao objecto inicial dentro do método irá ser repercutido para fora do mesmo. Com isto podemos concluír que no output a list3 terá 2 elementos.

segunda-feira, janeiro 19, 2009

Programador corajoso

Quando eu digo que a vida de um programador não é fácil não estou a exagerar...



P.S.: Obrigado ao Daniel Grilo pela imagem

sexta-feira, janeiro 09, 2009

Documentação PayPal

Tenho andado de volta da implementação da comunicação com o serviço PayPal, e depois de "bater contra algumas paredes" até começo a ver a beleza do PayPal.
Deixo aqui dois links que me ajudaram bastante na implementação que comunica com o IPN do PayPal:

http://www.domainavenue.com/paypal_ipn.pdf
https://www.paypal.com/us/cgi-bin/webscr?cmd=p/pdn/ipn-codesamples-pop-outside

quarta-feira, novembro 12, 2008

Paginação em queries MySql

Infelizmente esta é uma funcionalidade que não existe em todos os tipos base de dados, porque realmente dá imenso jeito e tem uma sintaxe bastante fácil de utilizar. Então a sintaxe é a seguinte:

SELECT * FROM TableXPTO
WHERE columnName = 'XPTO'
LIMIT 20, 10

O que vai acontecer é que serão retornados 10 tuplos começando no tuplo 20. Se por exemplo quisermos uma paginação de 10 tuplos/página, estaríamos a receber os 10 tuplos da 3ª página com esta query.

quinta-feira, outubro 23, 2008

Ciclo de eventos no ASPX

Uma das coisas mais chatas no ciclo de eventos, é o evento de click de um botão acontecer só depois do OnLoad. Então que fazer quando no OnLoad precisamos de saber se um botão foi pressionado? O truque para conseguir esta informação no OnLoad é ter um hidden field que é afectado pelo evento de client click do botão.

Código no ASPX:
<asp:button id="filter" onclientclick="JSFilter()" onclick="Filter" runat="server">

Código no code behind:
protected override void OnInit(EventArgs e)
{
   Page.ClientScript.RegisterHiddenField("filterChange", "");
}
protected override void OnLoad(EventArgs e)
{
   base.OnLoad(e);

   string arg = HttpContext.Current.Request.Form["filterChange"];
   if (arg == "1")
   {
       //A lógica que se quiser se o botão foi pressionado
   }
}

Código de Javascript:
function JSFilter(){
  var hidden = $('filterChange');
  hidden.value = 1;
}
Quando o botão é pressionado o javascript é executado colocando o valor do hidden field igual a 1 (estou a usar mootools no javascript, mas poderia usar um get por id, sem usar o mootools). Quando entro no evento OnLoad verifico se o valor do hidden field é 1, se for é porque o botão foi pressionado.

terça-feira, outubro 07, 2008

Google Chart

Ontem pela primeira vez andei de volta disto e ao contrário do que me disse o Pedro Santos, o Google Chart não me pareceu assim tão intuitivo. Ok que fazer um gráfico simples são 3 parâmetros e voilá já temos um graficozito, o problema é quando se quer personalizar alguma coisa tudo se torna confuso. Ao fim de 3 horas lá surgiu algo idêntico à imagem seguinte.

Google Chart Example

Alguns dos problemas que senti:
  • Os exemplos de transparências não funcionam e não consegui fazer transparências de todo, se alguém já conseguiu e tem um exemplo funcional agradecia a ajuda;
  • A posição de alguns parâmetros da query string tem influência no resultado final;
  • A documentação não é explicita em todos os casos e às vezes não se sabem todas as opções dentro de cada parâmetro e que faz cada uma delas;
  • Existe redundância na passagem de parâmetros, por exemplo, no gráfico que mostrei tive de colocar três vezes o número 2000.

quarta-feira, outubro 01, 2008

Dropdown com submenus

No outro dia necessitei de fazer o seguinte controlo, e apesar de nunca o ter feito antes com submenus até é algo bastante simples:



Código:
<select id="groupType">
 <option value=""> </option>
 <optgroup label="Resources">
  <option value="1">Intrinsic</option>
  <option value="2">Rare</option>
 </optgroup>
 <optgroup label="Units">
  <option value="4">Ships</option>
  <option value="8">Light</option>
  <option value="16">Medium</option>
  <option value="32">Heavy</option>
  <option value="64">Ultimate weapon</option>
 </optgroup>
</select>

sexta-feira, setembro 19, 2008

O conceito de Scrum

O Scrum é basicamente uma metodologia ágil de desenvolvimento de software. Hoje fui a um evento no qual se debateu a importância e aplicabilidade do Scrum, e fiquei com a sensação que é só aplicável num mundo ideal. Apesar de ser um conceito interessante que se pode tentar aplicar na maior parte das situações, em projectos de alguma dimensão dificilmente trará o efeito desejado.

O Scrum tem como principal pilar a equipa e a boa capacidade que os diversos elementos que a compõem têm para desenvolver um produto, ou partes do produto designados por "Potentially shippable product increment". Porque digo que o Scrum é bom no mundo ideal, porque é difícil ter uma equipa que funcione bem, que seja composta sempre pelo mesmos elementos, que os elementos estejam só agregados a um projecto, que não haja necessidade de colocar mais pessoas ao longo do projecto, etc.


A grande vantagem do Scrum é o pouco desperdício de tempo caso as coisas corram para o torto pois as iterações são curtas (2-4 semanas como se vê na imagem). O fluxo é facilmente explicável, começam-se por ter os requisitos (Product backlog), que são divididos em vários pacotes de requisitos em que cada pacote tem que original uma possível release, sendo que os pacotes são ordenados pela prioridade das tarefas. Cada pacote (Sprint backlog) é dividido pelos diversos elementos da equipa e durante o periodo de tempo extipulado (Sprint), o tal tempo a rondar as 2-4 semanas tem de surgir um Potentially shippable product increment.

Na minha opinião o Scrum não é nada de novo, aliás é só um nome pomposo para algo que todas as equipas de desenvolvimento de software tentam implementar a nível da sua organização, a própria equipa que componho tenta seguir este esquema desde à bastante tempo, mesmo sem saber que tal metodologia tinha um nome.