O elemento system.transactions

Quando utilizamos a API System.Transactions para a criação e gerenciamento de transações, algumas configurações estão acessíveis de forma declarativa. O elemento em questão, tema deste post, é o system.transactions, que permite através de dois sub-elmentos, efetuar tais configurações.

O primeiro destes sub-elementos é representando pela classe DefaultSettingsSection. A propriedade Timeout possibilita especificarmos um TimeSpan, com o valor padrão de timeout que será utilizado por todas as transações daquela aplicação. Caso voce especifique o timeout diferente para o TransactionScope, este sobrescreverá a valor do arquivo de configuração. Outra propriedade exposta por essa classe é a DistributedTransactionManagerName. Essa propriedade recebe uma string contendo o nome do gerenciador de transações (MSDTC). Quando não especificado (que é o padrão), ele assumirá o nome do gerenciador da máquina local, onde a aplicação está sendo executada. Voce pode definir nesta propriedade o nome de uma máquina remota e, com isso, o gerenciador de transações desta será utilizada.

Já o outro sub-elemento, MachineSettingsSection, como o próprio nome diz, encapsula as configurações em nível de máquina e, justamente por isso, pode somente ser definida no arquivo machine.config. Basicamente ele define uma propriedade chamada MaxTimeout, também do tipo TimeSpan, definindo o valor máxima de timeout para uma transação.

<?xml version=”1.0″ encoding=”utf-8″ ?>
<configuration>
  <system.transactions>
    <defaultSettings timeout=”00:00:02″/>
    <machineSettings maxTimeout=”00:01:00″/> <!– somente no machine.config –>
  </system.transactions>
</configuration>

System.IO.Log

A versão 3.0 do .NET Framework temos um namespace chamado System.IO.Log, qual traz uma série de classes para manipular um log de informações, utilizando o sistema de arquivos como repositório, mas baseando-se em registros. Isso permite catalogar e ler as informações como se elas fossem itens, ao invés de ficar manipulando linhas, strings, etc., como acontece atualmente.

Para fazer o uso dessas classes, é necessário adicionar a referencia para o assembly System.IO.Log.dll que está dentro do seguinte diretório: %ProgramFiles%Reference AssembliesMicrosoftFrameworkv3.0.

Como já era de se esperar, tudo o que for catalogado deverá ser convertido em um array de bytes. Já durante a leitura, o retorno será através de um Stream, que também deverá ser carregado para um array de bytes. O exemplo abaixo exibe como proceder para armazenar dados e também para ler o conteúdo do mesmo arquivo:

using (FileRecordSequence f = new FileRecordSequence(“Teste.log”))
{
    f.Append(
        new ArraySegment<byte>(Encoding.Default.GetBytes(“Mensagem para logar”)),
        SequenceNumber.Invalid,
        SequenceNumber.Invalid,
        RecordAppendOptions.ForceFlush);
}

using (FileRecordSequence f = new FileRecordSequence(“Teste.log”))
{
    foreach (LogRecord r in f.ReadLogRecords(f.BaseSequenceNumber, LogRecordEnumeratorType.Next))
    {
        int length = (int)r.Data.Length;

        byte[] data = new byte[length];
        r.Data.Read(data, 0, length);
        Console.WriteLine(Encoding.Default.GetString(data));
    }
}

Note que para cada append, haverá uma instancia da classe LogRecord que representará o registro corrente. A classe FileRecordSequence, responsável por grande parte do processo, também traz suporte ao processamento assíncrono, com os métodos BeginAppend e EndAppend.

Neutral Culture

Neutral Culture ou Cultura Neutra é o termo utilizado para referenciar uma cultura específica, sem ter uma região ou um país vinculado. Quando estamos trabalhando com globalização de aplicações .NET (seja ela Windows ou Web), é comum criarmos arquivos de recursos (*.resx) que representam uma cultura juntamente com uma região, como por exemplo: “pt-BR”, “pt-PT”, “en-US”, “en-NZ”, etc.

Uma cultura é considerada neutra quando referenciamos apenas a cultura, como por exemplo: “pt” ou “en”. Com isso, independentemente da região (Brasil ou Portugal), podemos ter uma única versão, ou seja, portugues é portugues no Brasil e em Portugal; já noutro exemplo (Estados Unidos ou Nova Zelandia), ingles é ingles nos Estados Unidos e na Nova Zelandia.

É importante dizer que a cultura neutra pode ser utilizada apenas para a globalização da interface (arquivos *.resx). Isso se deve ao fato de que para formatar números, datas, efetuar parsing, etc., é necessário determinar os separadores, convenções, moedas, etc. Essas informações são características de uma região específica e que, a cultura neutra não disponibiliza. Para saber se a instancia da classe CultureInfo está ou não armazenando uma cultura neutra, basta recorrer a propriedade IsNeutralCulture, que retorna uma valor booleano indicando isso.

CultureInfo ci = CultureInfo.GetCultureInfo(“en”);
Console.WriteLine(ci.IsNeutralCulture); //Retornará True

Exceções dentro de Threads no ASP.NET

Quando uma exceção não tratada ocorre dentro na mesma thread que está executando a requisição, provavelmente teremos aquela famosa página amarela com os detalhes do erro, incluindo a mensagem, o tipo da exceção e a stack trace (vamos levar em conta que a seção customErrors está desabilitada), continuando a aplicação funcionando perfeitamente, ou seja, outras requisições não serão comprometidas.

Exceções não tratadas que ocorrem dentro da thread que executa a requisição não traz maiores problemas para a aplicação como um todo, ao contrário quando a exceção não tratada acontece dentro de uma thread a parte que, podem ser criadas através de uma instancia da classe Thread, através do método estático QueueUserWorkItem da classe ThreadPool ou, uma forma mais esotérica, quando alguma exceção acontece dentro de um destrutor de uma classe. Dependendo do que se precisa fazer, a utilização das páginas assíncronas garantem que um processo de IO seja efetuado em uma thread a parte mas, qualquer problema que eventualmente possa acontecer, a exceção sempre será disparada sob a thread que está executando a requisição.

Neste cenário, exceções não tratadas levam o AppDomain da respectiva aplicação ASP.NET ser reciclado e, com isso, todas as informações que estão armazenadas dentro do mesmo, como Session, Application e Cache serão perdidas, fazendo com que outros usuários sejam prejudicados. Aplicações que dependem destes dados, confiantes que uma vez definido nunca será nulo, podem começar a falhar e, um dos motivos podem ser justamente a questão de exceções não tratadas em threads.

Como essas exceções não tratadas não são catalogadas mesmo quando o Health Monitoring está habilitado, temos que recorrer ao conhecido evento UnhandledException da classe AppDomain. Podemos aqui utilizar um módulo para que todas as requisições façam o uso dele e, como exemplo, este artigo mostra detalhes da implementação deste importante módulo. Apesar de existir um contador de performance que contabiliza as reinicializações da aplicação, é sempre importante efetuar o log de possíveis exceções que podem estar sendo disparadas.

Testando o envio de e-mails

Eu ministro treinamentos oficiais Microsoft e, em um dos capítulos, falamos sobre o envio de e-mails, mais precisamente, sobre as classes MailMessage e SmtpClient.

Pois bem, para não ter que criar/utilizar uma conta de e-mail dummy ou ter uma infraestrutura com um servidor de e-mail, podemos ao invés de efetivamente enviar o e-mail ao seu destinatário, salvá-lo localmente e assim visualizar como ficou o mesmo, sem a necessidade de abrí-lo em alguma ferramenta específica ou webmail. Para essa mudança, voce deve configurar o modo de entrega como sendo specifiedPickupDirectory ao invés de network. O exemplo abaixo exibe como efetuar essa mudança através do arquivo de configuração da aplicação:

<?xml version=”1.0″ encoding=”utf-8″ ?>
<configuration>
  <system.net>
    <mailSettings>
      <smtp deliveryMethod=”SpecifiedPickupDirectory” from=”automatico@site.com.br“>
        <specifiedPickupDirectory pickupDirectoryLocation=”C:Temp”/>
      </smtp>
    </mailSettings>
  </system.net>
</configuration>

Já no ambiente de produção, voce deverá trocar o valor do atributo deliveryMethod para Network e, ao invés de utilizar o sub-elemento specifiedPickupDirectory, configurar o sub-elemento network, como mostrado abaixo:

<network host=”mail.site.com.br” port=”25″ />

MemoryMappedFile

Por curiosidade, abri o .NET Reflector com o .NET Framework 4.0 e comecei a explorar as classes que ele disponibiliza e, logo percebi um novo namespace: System.IO.MemoryMappedFiles. Dentro deste namespace, entre as poucas classes, temos a classe MemoryMappedFile. É importante dizer que é uma versão CTP, sem a garantia de que isso prevalecerá na versão final.

A finalidade dela é mapear um espaço da memória para o conteúdo de um determinado arquivo (ou algum outro recurso), criando um objeto que servirá de “ponte” entre a aplicação e o arquivo físico, criandos “views” deste arquivo e, como principal benefício, permitirá acessar “seções” do arquivo, sem carregá-lo completamente para a memória. Abaixo um exemplo simples que faz a utilização desta classe:

using System.IO;
using System.IO.MemoryMappedFiles

using (MemoryMappedFile mmf = 
    MemoryMappedFile.CreateFromFile(new FileStream(“C:Teste.txt”, FileMode.Open)))
{
    byte[] buffer = new byte[5];
    mmf.CreateViewStream(120, 5).Read(buffer, 0, 5);
    Console.WriteLine(Encoding.Default.GetString(buffer));
}

O primeiro parametro do método CreateViewStream é a posição inicial dentro do arquivo e, o segundo, a quantidade de caracteres que voce quer extrair.

Algumas novidades interessantes

A versão CTP do Visual Studio 2010 com o .NET Framework 4.0 já está disponível, assim como o Soma Somasegar disse neste post. Olhando superficialmente, há algumas funcionalidades que achei bastante interessantes, que quero listar aqui:

  • Code-snippet para HTML.
  • ClientIDMode: Nova propriedade que está contida nos controles ASP.NET, dando-nos a possibilidade de definir como renderizar o ID dos controles.
  • Gráficos: Agora temos nativamente um controle chamado Chart, mas que pode também ser utilizado em conjunto com o Visual Studio 2008 e .NET Framework 3.5. Download: Controles, VS.NET Toolbox (Add-On) e Documentação.
  • Web.config: Possibilidade de criar/transformar o seu arquivo Web.config para diversos estágios do desenvolvimento (Debug/Staging/Release) que, na maioria das vezes, as configurações mudam em cada uma dessas fases.
  • PIA – Primary Interop Assemblies: Quando adicionamos uma referencia a algum componento COM, era criado um Assembly de interoperabilidade em nosso projeto. Esse Assembly apenas contém a estrutura do componente não gerenciado e, que devemos distribuir juntamente com a aplicação onde ele foi referenciado. Com o Visual Studio 2010, podemos embutir esses tipos no Assembly da aplicação/DLL onde o componente está sendo referenciado, sem a necessidade da criação de um Assembly a parte.
  • Computação Paralela: Até então chamada de Parallel Extensions, agora ela foi incorporada dentro do .NET Framework.
  • VB.NET – Propriedades Automáticas: Assim como no C#, o VB.NET agora suporta propriedades automáticas. A sintaxe para isso é: Public Property Nome As String.
  • C# – Parametros Opcionais: Assim como nas versões do Visual Basic, o C# agora passa a suportar parametros opcionais e, assim como no VB, é necessário especificar um valor padrão. A sintaxe para isso é: public void Teste(string nome, int valor = -1){ }.
  • C# – Tipos Dinamicos: Assim como Charlie Calvert comentou aqui há algum tempo, o C# agora terá uma keyword chamada dynamic. Ao declarar uma variável como dinamica, ela suportará a chamada de membros e, a verificação se eles existem ou não, somente acontecerá em tempo de execução. Isso também é conhecido como late-binding.

O tipo de dado também é um validador

Analisando o projeto de um terceiro me deparei com um código que, ao meu ver, está errado (o exemplo não segue o padrão de nomenclatura encontrado lá):

public void DefinirDataDePagamento(DateTime? data)
{
    if(data == null)
        throw new ArgumentNullException(“data”);

    //executa a tarefa
}

Pelo que aparenta, a data que é passada como parametro para o método DefinirDataDePagamento é obrigatória e, caso seja nula, uma exceção do tipo ArgumentNullException é disparada. Se essa regra existe (não permitir que data seja nula), então porque o parametro data é criado como sendo um Nullable<DateTime> ao invés de apenas DateTime? Se fosse definido como DateTime, obrigatoriamente, o desenvolvedor deverá passar uma data (seja DateTime.MinValue, DateTime.MaxValue, DateTime.Now ou um DateTime customizado) ao utilizar o método.

#error

Em alguns momentos durante a escrita de um projeto para servir de exemplo para um artigo, eu gostaria de colocar uma diretiva no código para forçar o leitor a antes de executar o mesmo, configurar algo que seja necessário para que o projeto funcione como o esperado. Depois de algumas pesquisa no MSDN Library, a solução que encontrei foi a diretiva #error, que podemos aplicar ao código da seguinte forma:

#error Altere o valor da connectionstring apontando para uma base válida
private const string SQL_CONN_STRING = “Data Source=.;Initial Catalog=DBTest;Integrated Security=SSPI;”;

Isso impedirá o usuário de compilar a aplicação sem antes alterar essa informação ou, ao menos, comentá-la. 😛