DataLoadOptions

A utilização da classe DataLoadOptions pelo LINQ To SQL pode ter um grande benefício em termos de performance. Quando estamos efetuando queries que possuem dados relacionados com outras tabelas, esses dados serão carregados sob demanda, ou seja, eles somente serão carregados quando precisarmos efetivamente deles.

Imagine que temos uma tabela Cliente e uma tabela Telefone, onde a segunda possui uma coluna chamada ClienteId que liga o telefone ao cliente. Para percorrer os clientes e exibir a quantidade de telefones que cada um deles tem cadastrado, podemos executar o seguinte código:

foreach (Cliente c in ctx.Clientes)
{
    Console.WriteLine(c.Nome);
    Console.WriteLine(c.Telefones.Count);
}

Ao visualizar os logs no SQL Profiler, veremos que duas queries chegam até ele, sendo uma para capturar o cliente e a outra para extrair os telefones que ele possui:

SELECT [t0].[ClienteId], [t0].[Nome]
FROM [dbo].[Cliente] AS [t0]

exec sp_executesql N‘SELECT [t0].[ClienteId], [t0].[Tipo], [t0].[Numero]
FROM [dbo].[Telefone] AS [t0]
WHERE [t0].[ClienteId] = @p0′
,N’@p0 int’,@p0=1

Com a utilização da classe DataLoadOptions, podemos especificar quais dados queremos carregar juntamente com o registro principal que, no nosso caso, são os telefones do cliente. Basicamente, precisamos instanciar esta classe e, via método LoadWith, especificamos a entidade alvo (Cliente) e, como parametro, definimos qual campo ou propriedade queremos carregar. Isso é mostrado a partir do código abaixo:

DataLoadOptions opts = new DataLoadOptions();
opts.LoadWith<Cliente>(c => c.Telefones);
ctx.LoadOptions = opts;

foreach (Cliente c in ctx.Clientes)
{
    Console.WriteLine(c.Nome);
    Console.WriteLine(c.Telefones.Count);
}

Depois da instancia da classe DataLoadOptions criada e configurada, basta definirmos a mesma na propriedade LoadOptions do contexto do LINQ To SQL. Ao executar este código, apenas uma única query será encaminhada para o SQL Server, mas preparada para extrair todos os dados:

SELECT [t0].[ClienteId], [t0].[Nome], [t1].[ClienteId] AS [ClienteId2], [t1].[Tipo], [t1].[Numero], (
    SELECT COUNT(*)
    FROM [dbo].[Telefone] AS [t2]
    WHERE [t2].[ClienteId] = [t0].[ClienteId]
    ) AS [value]
FROM [dbo].[Cliente] AS [t0]
LEFT OUTER JOIN [dbo].[Telefone] AS [t1] ON [t1].[ClienteId] = [t0].[ClienteId]
ORDER BY [t0].[ClienteId], [t1].[Tipo], [t1].[Numero]

LINQ To SQL e Processamento Assíncrono do ASP.NET

escrevi e palestrei sobre a vantagem que temos ao fazer uso das páginas assíncronas, recurso que é fornecido a partir do ASP.NET 2.0.

Infelizmente o LINQ To SQL não possui intrinsicamente métodos para executar as queries de forma assíncrona e, sendo assim, não podemos incorporá-lo na execução assíncrona da página ASP.NET. Vale lembrar que voce pode criar um delegate apontando para um método, e dentro deste invocar as queries a partir do contexto do LINQ To SQL.

Utilizando esta técnica não trará os benefícios propostos pelas páginas assíncronas, pois quando voce invocar o delegate, ele extrairá uma thread do ThreadPool para executar a tarefa. A idéia das páginas assíncronas é fazer com que o processo custoso, como acesso a banco de dados, web services, etc., seja disparado através de uma thread de IO, liberando as threads do ThreadPool apenas para executar as páginas ASP.NET.

Para fazer o LINQ To SQL (ou qualquer outra tarefa) executar em uma thread de IO, podemos recorrer ao Power Threading, criada pela Wintellect. Essa library possui várias classes que nos auxiliam em tarefas assíncronas e, entre elas, temos a classe chamada CallbackThreadPool, que encapsula e gerencia a execução de tarefas a partir de threads de IO. Um único detalhe que precisamos nos atentar é a criação de um IAsyncResult customizado, que será utilizado pelo ASP.NET para determinar quando a query executada pelo LINQ To SQL for finalizada.

A parte mais complexa é a criação do IAsyncResult. Além da sua principal finalidade, ele também trará o resultado do processo assíncrono e possíveis exceções que essa tarefa possa disparar. Para facilitar e também conseguir reutilizar essa classe por várias entidades, eu a criei de forma genérica, trabalhando fortemente tipada. Para poupar espaço, abaixo consta apenas a sua definição.

public class DataAsyncResult<TResult> : IAsyncResult
{
    //implementação
}

A classe CallbackThreadPool fornece um método QueueUserWorkItem, e recebe como parametro uma instancia do delegate WaitCallback e da classe DataAsyncResult. O delegate deverá apontar para o método que irá executar a query via LINQ To SQL. O código abaixo ilustra como proceder para alistar um novo processo assíncrono através da Power Threading da Wintellect:

DataAsyncResult<IEnumerable<Cliente>> ar = new DataAsyncResult<IEnumerable<Cliente>>(callback, state);
_threadPool.QueueUserWorkItem(new WaitCallback(RecuperarDados), ar);

A partir deste ponto tudo é como já acontece normalmente com uma página assíncrona, ou seja, definindo o atributo Async da diretiva @Page como True e registrar a execução do processo assíncrono através do método AddOnPreRenderCompleteAsync da classe Page. Código de exemplo:

AsyncLINQToSQL.zip (39.97 kb)

WWS – Windows Web Services

Sendo publicado como um componente a partir do Windows 7, o Windows Web Services – WWS, é uma API que permite ao código nativo, não gerenciado, criar e consuimir Web Services sem a necessidade do .NET Framework. O propósito de ser um componente é que a Microsoft pretende disponibilizá-lo para as outras versões do Windows, como XP, Vista, 2003 e 2008.

Com um modelo bem similar ao WCF, com bindings, channels, endpoints, etc., o WWS facilita a criação e hosting de serviços, bem como o consumo deles, fornecendo interoperabilidade com o WCF, ASMX e outras tecnologias não-Microsoft, que seguem os padrões WS-*.

Os tres grandes pilares do WWS são: Service Model, Channel LayerXML Layer. O primeiro deles gerencia a comunicação, ou seja, se estivermos consumindo o serviço, ele fornece tipos para facilitar o seu consumo (service proxy); já se estivermos expondo o serviço, então ele fornece tipos para efetuar o hosting (service host). A Channel Layer controla a comunicação, como os dados que são enviados e recebidos, abstração do protocolo, segurança, etc. E, finalmente, temos a XML Layer, fornece acesso completo ao conteúdo das mensagens.

Algumas funcionalidades ainda não foram implementadas, como é o caso do suporte a segurança em nível de mensagem e, pelo que parece, isso irá acontecer de acordo com a necessidade dos clientes. Assim como o WCF, o WWS também é orientado a contratos, permitindo trabalhar de forma bem semelhante no código não gerenciado. O WWS também fornece um utilitário chamado WsUtil.exe, que mapeia o WSDL/XSD para tipos C.

Para aqueles que possuem o Windows 7, verá que no diretório %windir%System32 existe uma DLL chamada Webservices.dll que gerencia grande parte desta comunicação. Além disso, o Windows SDK disponibilizado no PDC 2008, traz esta API (WWSAPI), contendo o Wsutil.exe, Webservices.h e webservice.lib.

É importante dizer que, segundo Nikola Dudar, o WWS não substitui o WCF. A finalidade do WWS é possibilitar aplicações nativas consumirem ou disponibilizarem um serviço, enquanto o WCF é a alternativa para o código gerenciado, e depende do .NET Framework.