Wednesday, September 26, 2012

Microsoft Office Excel Corrupted File

Hoje tivemos mais um problema de configuração de ambientes que foi um pouco chato de resolver por quê o erro não acontecia na nossa aplicação.

Descrição do Cenário:

Implementamos nosso sistema em uma empresa que possui um ERP e deseja que este sistema ERP atualize os dados de cadastro dos funcionários automaticamente no nosso sistema, todos os dias.

Para isso, disponibilizamos um serviço web através do qual a atualização dos dados seriam publicadas no nosso sistema a partir de arquivos .XLS exportados do sistema ERP.

O problema da abordagem adotada era que o ERP não gerava arquivos .XLSX e sim arquivos do tipo BIFF ou Excel 2. Descritos abaixo




Quando os arquivos eram publicados acontecia um erro de leitura dos dados através do provider Microsoft.ACE.OLEDB que não suporta esses tipos de arquivo.

A solução mais rápida foi adotar um novo processo antes de fazer a leitura das planilhas:

1 - Salvar arquivo BIFF/Excel 2 recebido pelo webservice.
2 - Abrir arquivo com o Microsoft Excel
3 - Salvar arquivo como .XLS (2007)
4 - Continuar leitura dos dados a partir do novo arquivo.

Quando publicamos em um dos nossos ambientes de homologação, a solução não funcionava, embora nos testes em desenvolvimento não tenha aparecido nenhum tipo de problema.

Investigando um pouco mais, no passo 2 descobrimos que o seguinte erro acontecia:


Neste momento, identificamos que o erro não estava na nossa aplicação, mas sim no Excel que não conseguia abrir o arquivo BIFF/Excel2.  Investigando o Log da aplicação no windows, conseguimos identificar a fonte do problema através da mensagem abaixo:


Para resolver a situação, foi necessário alterar algumas configurações de segurança no Microsoft Excel.

Abrindo o programa, clicamos em File > Options > Trust Center > Trust Center Settings > File Blocking Settings

Devemos desmarcar as opções Open e Save para os arquivos que precisamos, conforme mostrado na figura abaixo:




Wednesday, September 19, 2012

Operation is not valid due to the current state of the object

Hoje tratamos de um problema em uma aplicação asp.net que pode ser um pouco complicado já que não temos como detectar o erro através do debug comum Visual Studio.

Acontece que em uma das páginas um relatório é gerado dinâmicamente, com base na pesquisa feita pelo usuário (Mês / Ano).

Cada registro deste relatório gera novos controles ASP.NET na página, (caixas de texto para que o usuário possa adicionar ou modificar informações).



O relatório gerava mais de 300 linhas, logo o número de controles .NET adicionados ultrapassava o limite suportado pela aplicação. Quando qualquer postback era feito na página, o erro abaixo acontecia:



Com um pouco de pesquisa podemos encontrar referências da microsoft a respeito desta restrição:
http://support.microsoft.com/kb/2661403

"Microsoft security update MS11-100 limits the maximum number of form keys, files, and JSON members to 1000 in an HTTP request. Because of this change, ASP.NET applications reject requests that have more than 1000 of these elements. HTTP clients that make these kinds of requests will be denied, and an error message will appear in the web browser. The error message will usually have an HTTP 500 status code. This new limit can be configured on a per-application basis. Please see the "Resolution" section for configuration instructions."

* O limite de chaves em um form, arquivos e membros JSON é, por default, 1000 em um request HTTP .
 Para modificar este limite podemos adicionar ao web.config da aplicação:


<configuration>
  <appSettings>
    <add key="aspnet:MaxHttpCollectionKeys" value="1000" />
  </appSettings>
</configuration>

Caso você esteja usando ASP.NET 1.1, ajuste o limite adicionando um valor DWORD para a seguinte chave de registro:


HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\ASP.NET\1.1.4322.0\MaxHttpCollectionKeys

Para modificar o limite de membros JSON adicione o seguinte código:

<configuration>
  <appSettings>
    <add key="aspnet:MaxJsonDeserializerMembers" value="1000" />
  </appSettings>
</configuration>

O problema dessa abordagem é que um limite muito alto pode aumentar a possibilidade do seu servidor ter vulnerabilidade a ataques do tipo Denial of Service.




Tuesday, September 18, 2012

Desenvolvimento para Android com C#



Quando pensamos em como construir aplicativos para IOS e Android , a maioria das pessoas pensam que as linguagens nativas (Objectice-C e Java repectivamente) são as únicas opções. Entretanto, nos últimos anos um novo ecosistema de plataformas para construção de aplicaçãoes surgiu. Entre as novas soluções podemos citar: PhoneGap, Appcelerator, CellSDK e Xamarin. 
Neste artigo iremos fazer uma introdução ao desenvolvimento de aplicativos para Android a partir do Framework da Xamarin: Mono.Android

Xamarin é uma empresa de softwares baseada em Boston, Massachussetts que foi fundada em Maio de 2011. A Xamarin oferece como seus produtos principais os frameworks Xamarin.IOS e Xamarin.Android, também conecidos como MonoTouch e Mono for Android respectivamente.
Ambas as plataformas são construídas em cima da biblioteca Mono, uma versão open-souce do .Net Framework que foi baseada nos padrões ECMA(http://www.ecma-international.org). Esta biblioteca é quase tão antiga quanto o própio .Net Framework e  está presente em várias plataformas como Linux, Unix, FreeBSD e Mac OSX.

Um dos diferenciais da Xamarin é que ela oferece uma única linguagem (C#) e uma biblioteca de classes que funcionam em todas as 3 plataformas: Android, IOS e Windows Phone.

No Android, o código é compilado para uma linguagem intermediária que por sua vez será compilada para o assembly nativo da plataforma quando a aplicação for lançada (Just in time compilation of an Intermediate Language). As aplicações utilizam um runtime que manipula automaticamente problemas clássicos como alocação de memória, garbage collector, interoperabilidade de plataformas subjacentes, etc..

Outras features do framework apontados pelo site da Xamarin são (em inglês):

Complete Binding for the Indigenous SDKs – Xamarin contains bindings for nearly the entire underlying platform SDKs in both iOS and Android. Additionally, these bindings are strongly typed, which means that they’re easy to navigate and use, and provide robust compile-time type checking and auto completion during development. This leads to fewer runtime errors and higher quality applications.

    Objective-C, Java, C, and C++ Interop – Xamarin provides facilities for directly invoking Objective-C, Java, C, and C++ libraries, giving you the power to use a wide array of 3rd party code that has already been created. This lets you take advantage of existing iOS and Android libraries written in Objective-C, Java, or C/C++. Additionally, Xamarin offers binding projects that allow you to easily bind native Objective-C and Java libraries by using a declarative syntax.

    Modern Language Constructs – Xamarin applications are written in C#, a modern language that includes significant improvements over Objective-C and Java such as Dynamic Language Features, Functional Constructs such as Lambdas, LINQ, Parallel Programming features, sophisticated Generics, and more.

    Amazing Base Class Library (BCL) – Xamarin applications use the .NET BCL, a massive collection of classes that have comprehensive and streamlined features such as powerful XML, Database, Serialization, IO, String, and Networking support, just to name a few. Additionally, existing C# code can be compiled for use in your applications, which provides access to thousands upon thousands of libraries that will let you do things that aren’t already covered in the BCL.
    Modern Integrated Development Environment (IDE) – Xamarin uses MonoDevelop on Mac OSX, and also MonoDevelop or Visual Studio 2010 on Windows. These are both modern IDE’s that include features such as code auto completion, a sophisticated Project and Solution management system, a comprehensive project template library, integrated source control, and many other options.

Considerações Importantes para Desenvolvimento de Aplicativos Mobile:

Multitasking
Em aparelhos mobile apenas uma aplicação pode estar em foreground. Isso se deve principalmente às limitações de memória e espaço em tela. Outro ponto importante é que ter muitas aplicações abertas ao mesmo tempo consomem rapidamente a bateria do aparelho.

No android Multitasking pode ser dividido em 2 componentes:  

Activity Lifecycle: Cada tela em uma aplicação android é representada por uma atividade. Existe um grupo de eventos específicos que são acionados sempre que uma atividade vai para background ou foreground.

Services: Serviços são processos geralmente longos que existem independentemente da aplicação e são executados quando a aplicação está em background.

Padrões e tamanhos de tela

Telefones e tables são os dois tipos principais de aparelhos mobile. Alguns aparelhos acabam caindo em categorias intermediárias à essas duas. Desenvolver aplicações para esses tipos de aparelhos pode ser muito diferentes devido ao pouco espaço em tela quando comparados a um laptop ou PC. Por esse motivo, os controles responsáveis pela interface com o usuário devem ser desenhados especificamente para serem efetivos nesses aparelhos e menores em tamanho.

No IOS temos um pequeno número de dispositivos para atender, no Windows Phone 7 existem restrições impostas pela Microsoft que padronizam os aparelhos e estabelecem requisitos mínimos a serem atendidos para que o dispositivo seja aprovado. Já no Android , a Google não estabelece limites e restrições para os dispositivos o que aumenta muito o universo de ambientes ao quais a nossa aplicação precisa se adaptar, isso inclui diferentes tamanhos de tela, especificações de hardware, features, capabilities, etc... Devido a esse universo abrangente, a maioria das equipes de desenvolvimento escolhem 5 ou 6 aparelhos top e focam os testes para esses aparelhos.

Dispositivo e fragmentação do SO

Conceitualização e planejamento – Devido às grandes diferenças entre o hardware e features de cada aparelho, você precisa ter em mente que uma aplicação que depende de algumas features podem não funcionar corretamente em todos os dispositivos. Por exemplo, nem todos os smartphones tem câmeras. Esse tipo de fator é muito importante para o seu planejamento inicial.

Design – Ao desenhar o controle de experiência e usabilidade do usuário (UX), tamanos diferente de tela, teclados e interfaces de controle (touch-screen ou voz) devem ser levados em consideração.  Ao desenharmos a interface do usuário (UI) devemos ter em mente diferentes tipos de resolução.
Desenvolvimento – Quando usamos alguma feature do dispositivo (a câmera, por exemplo) esta feature deve ser sempre testada primeiro. (Pergunte ao SO se a feature está disponível)
Testes – É muito importante testar a sua aplicação nos dispositivos atuais (e na maioria deles). No android, mesmo aparelhos com especificações de hardware idênticas podem apresentar diferenças de comportamento.

Recursos Limitados

Os dispositivos móveis estão cada vez mais poderosos, porém ainda possuem capacidades limitadas se comparados a notebooks e computadores. Por isso devemos sempre adotar boas práticas de programação que limitem o consumo de memória e tempo de CPU das nossas aplicações. 

Considerações de Segurança

Cada aplicação no Android OS roda sob uma identidade isolada e distinta cujas permissões são limitadas. Por default, aplicações tem permissão para quase nada. Por exemplo, sem permissões especiais uma aplicação não consegue enviar mensagens de texto ou até mesmo acessar a internet. Para poder acessar essas features, as aplicações precisam especificar no arquivo de manifesto quais permissões serão utilizadas. Quando a aplicação é instalada essas permissões são listadas para o usuário que deverá aprovar, ou cancelar a intalação.

Distribuição

Todas as aplicações android devem ser assinadas digitalmente antes de serem distribuídas. Os desenvolvedores devem assinar suas aplicações usando seus própios certificados protegidos por uma chave privada. Ao contrário das outras plataformas, Android tem uma abordagem muito aberta quanto a distribuição de aplicativos. Os dispositivos não são restritos a uma única app store, desse modo um canal muito mais largo e complexo existe para distribuição dos aplicativos.

Starting Guide:

Para começar a desenvolver para android, entre no site da Xamarin e faça o download do framework e dos plugins para IDE (Visual Studio). A empresa fornece todo o material necessário para iniciantes, como exemplos de código e documentação da biblioteca de classes além de tutorias relacionados a permissões, distribuição, etc..

Site da Xamarin: http://www. xamarin.com
Download das ferramentas:   http://xamarin.com/androidbeta/setup.exe

Após instalar a solução, observe os novos tipos de projeto que você pode criar:

Tuesday, September 11, 2012

GoDaddy Failure

Ontem tivemos problema de acesso nos nossos servidores devido ao outage do serviço de dns do Godaddy. Hoje, dia 11/09 o serviço já foi restaurado e podemos ver a repercussão através das notícias da web:

http://bits.blogs.nytimes.com/2012/09/10/member-of-anonymous-takes-credit-for-godaddy-attack/
http://mashable.com/2012/09/10/godaddy-gone-recover-its-reputation/
http://techcrunch.com/2012/09/10/godaddy-outage-takes-down-millions-of-sites/

Monday, September 3, 2012

Criando User Controls em ASP.NET


User Controls são componentes nos quais você inclui Tags html e um ou mais controles web, podendo tratá-los como uma unidade com suas propriedades e métodos particulares. Ou seja, pode ser visto como uma página ASP.NET que assume o comportamento de um Controle encapsulado.

Tem como grande vantagem a praticidade da utilização e reutilização de código e ganho de agilidade na manutenção.

Os User Controls são baseados na classe System.Web.UI.UserControl e difere de uma página ASP.NET nos seguintes itens:

  • A extensão do arquivo de um  User Control  é ".ascx";
  • Ao invés de usar a diretiva @page, um  User Control  contém uma diretiva @Control que define suas propriedades;
  • Um  User Control não pode ser executado diretamente, somente quando incluído em uma página ASP.NET;
  • Um  User Control  não possui os elementos html, body ou form no seu interior.


O exemplo mais comum que temos de User Controls são páginas web que permitem a navegação do usuário não autenticado. Enquanto este usuário não estiver autenticado, um controle de Login estará disponível em todas as páginas do site e algumas ações (geralmente ações de compra e download) estão desabilitadas.

Não seria esperto neste caso escrevermos o código do controle de login em todas as páginas.  Nós poderíamos usar um User Control. Cada vez que você procurar a sua Toolbox por um controle específico e ele não existir, você mesmo pode criar o seu controle e adicionar a toolbox.

Abaixo, temos um exemplo prático de como criar um User Control para input de datas no sistema. Este controle associa ao textbox aonde a data será digitada: Um controle de calendário, um controle de validação do formato de acordo com a cultura do usuário e um controle de range de datas de acordo com a base de dados com a qual estamos trabalhando.

01- Clique com o botão direito no seu projeto web e vá em Adicionar Novo item. Adicione uma nova pasta com o nome UserControls
02- Clique com o botão direito nesta pasta e vá em Adicionar novo Item. Adicione um novo  User Control com o nome "DateControl" conforme mostra a figura abaixo.


03- Abra o arquivo DateControl.ascx que foi adicionado e escreva o seguinte código para adicionar os controles necessários:

04 - Agora vamos adicionar o codebehind deste controle no arquivo "DateControl.ascx.cs":



========================================================================

using System;
using AjaxControlToolkit;

namespace WebApplication1.UserControls
{
    public partial class DateControl : System.Web.UI.UserControl
    {
        public enum DateRange
        {
            Aspnet,
            SqlServer,
            Oracle
        }
        private bool _isValid = false;
        public bool IsValid()
        {
            if (_isValid)
                return true;
            return false;
        }
        private DateTime _minimumRange;
        private DateTime _maximumRange;
        public string Text
        {
            get { return tbDate.Text; }
            set { tbDate.Text = value; }
        }

        public DateRange Range { get; set; }
        protected void Page_Load(object sender, EventArgs e)
        {
            SetRange();
            SetMask();
        }
        protected void Validate(object sender, EventArgs e)
        {
            ValidarData();
        }
        /// <summary>
        /// Retorna o valor da data já convertido
        /// </summary>
        /// <returns></returns>
        public DateTime? GetDate()
        {

            if (_isValid)
                return Convert.ToDateTime(tbDate.Text);
            return null;

        }
        /// <summary>
        /// Tenta converter a data e compara com o range. Em caso de falha limpa o texto.
        /// </summary>
        private void ValidarData()
        {
            try
            {
                var date = Convert.ToDateTime(tbDate.Text);
                //Check  range
                if (date < _minimumRange)
                {
                    Clear();
                }
                else if (date > _maximumRange)
                {
                    Clear();
                }
                _isValid = true;
            }
            catch
            {
                Clear();
            }

        }
        /// <summary>
        /// Determina o mínimo e máximo suportado pela estrutura de dados
        /// </summary>
        private void SetRange()
        {
            switch (Range)
            {
                default:
                    _minimumRange = System.DateTime.MinValue;
                    _maximumRange = System.DateTime.MaxValue;
                    break;
                case DateRange.Aspnet:
                    _minimumRange = System.DateTime.MinValue;
                    _maximumRange = System.DateTime.MaxValue;
                    break;
                case DateRange.Oracle:
                    //4712 antes de cristo não é suportado pelo .Net?
                    _minimumRange = System.DateTime.MinValue;
                    _maximumRange = new DateTime(9999, 12, 31, 23, 59, 59);
                    break;
                case DateRange.SqlServer:
                    _minimumRange = new DateTime(1753, 01, 01);
                    _maximumRange = new DateTime(9999, 12, 31, 23, 59, 59);
                    break;
            }
        }
        private void SetMask()
        {
            var mke = new CalendarExtender();
            mke.TargetControlID = tbDate.UniqueID;
            mke.Format = System.Threading.Thread.CurrentThread.CurrentUICulture.DateTimeFormat.ShortDatePattern;

            upDateControl.ContentTemplateContainer.Controls.Add(mke);

        }
        /// <summary>
        /// Limpa o conteúdo da caixa e marca o estado como inváido
        /// </summary>
        private void Clear()
        {
            tbDate.Text = "";
            _isValid = false;
        }
    }
}

========================================================================
05 - Para usar o controle, crie uma nova página Default.aspx e adicione o seguinte código:


Vamos agora fazer um outro controle que desta vez valide uma data de início e uma data de fim de um determinado período. Este controle aplica todas as regras do DateUsercontrol porém valida se a data de fim é maior que a data de início.

01 - Clique com o botão direito na pasta UserControls e vá em Adicionar novo Item. Adicione um novo User Control com o nome "DateFromToControl" e adicione o código ao arquivo .ascx, conforme mostra a figura abaixo.


02 - Adicione o CodeBehind:

using System;
using System.Web.UI.WebControls;
using AjaxControlToolkit;

namespace WebApplication1.UserControls
{
    public partial class DateFromToControl : System.Web.UI.UserControl
    {
        public enum DateRange
        {
            Aspnet,
            SqlServer,
            Oracle
        }
        private bool _isValidTo = false;
        private bool _isValidFrom = false;
        private bool _isValid = false;
        private DateTime _minimumRange;
        private DateTime _maximumRange;
        public DateRange Range { get; set; }

        public string FromText{ get; set; }
        public string ToText { get; set; }
       
        protected void Page_Load(object sender, EventArgs e)
        {
            lblFrom.Text = FromText;
            lblTo.Text = ToText;
            SetRange();
            SetMask();
        }
        protected void Validate(object sender, EventArgs e)
        {
            _isValid = false;
            ValidarTo();
            ValidarFrom();
            if (_isValidTo & _isValidFrom)
            {
                if (Convert.ToDateTime(tbDate.Text) > Convert.ToDateTime(tbDate2.Text))
                {
                    var tb = (TextBox)sender;
                    if(tb.UniqueID == tbDate.UniqueID)
                        ClearTo();
                    else
                    {
                        ClearFrom();
                    }
                }
                else
                    _isValid = true;
            }
        }
        public DateTime? GetToDate()
        {

            if (_isValidTo)
                return Convert.ToDateTime(tbDate.Text);
            return null;

        }
        public DateTime? GetFromDate()
        {

            if (_isValidFrom)
                return Convert.ToDateTime(tbDate.Text);
            return null;

        }
        private void ValidarTo()
        {
            try
            {
                var date = Convert.ToDateTime(tbDate.Text);
                //Check  range
                if ((date < _minimumRange) || (date > _maximumRange))
                {
                    ClearTo();
                }
                else
                _isValidTo = true;
            }
            catch
            {
                ClearTo();
            }

        }
        private void ValidarFrom()
        {

            try
            {
                var date = Convert.ToDateTime(tbDate2.Text);
                //Check  range
                if ((date < _minimumRange) || (date > _maximumRange))
                {
                    ClearFrom();
                }
                
                else
                _isValidFrom = true;
                
            }
            catch
            {
                ClearFrom();
            }
        }
        private void SetRange()
        {
            switch (Range)
            {
                default:
                    _minimumRange = System.DateTime.MinValue;
                    _maximumRange = System.DateTime.MaxValue;
                    break;
                case DateRange.Aspnet:
                    _minimumRange = System.DateTime.MinValue;
                    _maximumRange = System.DateTime.MaxValue;
                    break;
                case DateRange.Oracle:
                    //4712 antes de cristo não é suportado pelo .Net?
                    _minimumRange = System.DateTime.MinValue;
                    _maximumRange = new DateTime(9999, 12, 31, 23, 59, 59);
                    break;
                case DateRange.SqlServer:
                    _minimumRange = new DateTime(1753, 01, 01);
                    _maximumRange = new DateTime(9999, 12, 31, 23, 59, 59);
                    break;
            }
        }
        private void SetMask()
        {
            var mke = new CalendarExtender
                          {
                              TargetControlID = tbDate.UniqueID,
                              Format = System.Threading.Thread.CurrentThread.CurrentUICulture.DateTimeFormat.ShortDatePattern
                          };
            var mke2 = new CalendarExtender
                           {
                               TargetControlID = tbDate2.UniqueID,
                               Format = System.Threading.Thread.CurrentThread.CurrentUICulture.DateTimeFormat.ShortDatePattern
                           };

            upDateControl.ContentTemplateContainer.Controls.Add(mke);
            upDateControl.ContentTemplateContainer.Controls.Add(mke2);

        }
        private void ClearTo()
        {
            tbDate.Text = "";
            _isValidTo = false;
            _isValid = false;
        }
        private void ClearFrom()
        {
            tbDate2.Text = "";
            _isValidFrom = false;
            _isValid = false;
        }
    }
}

03 - Para finalizar, abra a página Default.aspx e adicione o seguinte código: