Cutting Edge

Gerenciando a entrega de conteúdo dinâmico no Silverlight, parte 1

Dino Esposito

Código disponível para download na MSDN Code Gallery
Navegue pelo código online

Sumário

O tamanho dos aplicativos do Silverlight
XAML gerado dinamicamente
XAP gerado dinamicamente
Conteúdo sob demanda
Cache do conteúdo baixado
Uma ferramenta para downloads
Baixando dados somente XAML
Trabalhando com pacotes XAP
Processando conteúdo XAP
Resumo

Os usuários de qualquer aplicativo sofisticado de Internet têm preocupações válidas sobre segurança e tamanho dos downloads. Os aplicativos do Silverlight são apoiados por um subconjunto do Microsoft .NET Framework completo e, como tal, têm potencial para executar ações prejudiciais à máquina do usuário local. Por isso, a equipe do Silverlight desenvolveu um novo modelo de segurança que impossibilita os aplicativos de chamarem qualquer classe crítica quanto à segurança no Core CLR (a edição Silverlight do .NET Framework). Leia mais sobre o modelo de segurança do Silverlight no artigo "Programe o Silverlight com o CoreCLR" e leia mais sobre a criação de aplicativos do Silverlight em "Comece a criar uma experiência mais profunda na Web".

Baixar o plug-in Silverlight não é realmente um problema, pois leva apenas alguns segundos e é uma operação que só precisa ser executada uma vez. Mas, e o tamanho dos aplicativos baixados?

Na coluna deste mês, vou analisar a questão dos downloads dos aplicativos do Silverlight. Primeiro, vou ilustrar como você pode gerar XAML dinamicamente. Depois, analisarei como habilitar dinamicamente recursos dos aplicativos exclusivamente por solicitação do usuário. Alguns recursos específicos a aplicativos, que são pesados para implementar com o fluxo principal do código, podem então ser implementados separadamente e, mais importante, baixados separadamente e ainda assim serem facilmente integrados à interface principal do usuário.

O tamanho dos aplicativos do Silverlight

Depois que o usuário instala o plug-in Silverlight, todos os assemblies do sistema que um aplicativo do Silverlight pode requerer estão em funcionamento. Isso significa que o download é limitado ao assembly que contém o aplicativo, além de quaisquer outros assemblies personalizados especificados. No final, o tamanho do download dos aplicativos é, frequentemente, de dezenas de quilobytes. Observe que essa estimativa aplica-se à versão RTM do Silverlight 2 e ao código compilado no modo de versão.

Naturalmente, o aplicativo deve exigir muito mais memória, especialmente se contiver grandes algoritmos, gráficos e conteúdo de mídia ou animações. Para lidar com aplicativos grandes em que o tempo de download se torne um problema, você tem duas opções principais. Uma é transmitir o conteúdo do Silverlight, como abordado em silverlight.live.com. A outra opção é dividir o aplicativo em partes independentes que possam ser baixadas separadamente e sob demanda.

XAML gerado dinamicamente

O plug-in Silverlight foi projetado essencialmente para exibir conteúdo XAML. Caso o XAML venha com algum code-behind, o plug-in processa o código para produzir a interface do usuário e aceita qualquer efeito ou comportamento codificado. É possível apontar diretamente para o XAML por meio de uma URL, se isso for tudo o que se quer baixar; caso contrário, pode-se fazer referência ao pacote Silverlight pela extensão XAP.

Um pacote XAP contém um manifesto e um ou mais assemblies. Um dos assemblies contém o ponto de entrada do aplicativo; os outros assemblies são apenas de referência. O XAML da interface do usuário é armazenado nos recursos do assembly do ponto de entrada. Quando você cria e compila o projeto, a extensão do Visual Studio 2008 para Silverlight 2 cria um pacote XAP.

O plug-in Silverlight é excelente para lidar com fluxos de XAML e XAP. No entanto, para baixar esse tipo de conteúdo, você não precisa necessariamente apontar o plug-in para um recurso XAP ou XAML físico no servidor. Você pode, por exemplo, apontar o plug-in para uma URL que pode retornar conteúdo XAML ou XAP gerado dinamicamente.

A Figura 1 mostra um exemplo de manipulador HTTP do ASP.NET que retorna qualquer XAML criado dinamicamente. O método ProcessRequest define o tipo de conteúdo do objeto Response e então escreve algum conteúdo XAML, como um XAML composto dinamicamente, baseado em dados e parâmetros de configuração ou em condições de tempo de execução. Configurando a propriedade Expires do objeto Response, você também pode impedir o cache do recurso no cliente. Isso pode ser útil caso o conteúdo oferecido seja alterado periodicamente e precise de atualização.

Figura 1 Um manipulador HTTP que retorna XAML

<%@ WebHandler Language="C#" Class="XamlGenHandler" %>

using System;
using System.Web;

public class XamlGenHandler : IHttpHandler 
{
    public void ProcessRequest (HttpContext context) 
    {
        // Prevent caching of the response
        context.Response.Expires = -1;

        // Set the type of data we're returning
        context.Response.ContentType = "text/xaml";

        // Create some XAML and return it down the wire
        context.Response.Write("<Canvas xmlns=
            'https://schemas.microsoft.com/client/2007' " +
            "xmlns:x='https://schemas.microsoft.com/winfx/2006/xaml'>" +
            "<TextBlock Foreground='black' Padding='10' FontSize='20'>
             <Run>XAML content</Run><LineBreak/>" + 
            "<Run>[generated " + DateTime.Now.ToLongTimeString() + "]</Run>" +
            "</TextBlock></Canvas>");
    }

    public bool IsReusable 
    {
        get {return true;}
    }

}

XAP gerado dinamicamente

O retorno de um pacote XAP gerado dinamicamente não é muito diferente do retorno de texto XAML bruto, exceto pelo fato de que um pacote XAP não é um arquivo de texto sem formatação. Um pacote XAP é um arquivo ZIP que contém um manifesto XML e um ou mais assemblies. Usando o formato de pacote, a equipe minimizou o número de viagens de ida e volta necessárias para baixar todo o conteúdo exigido por um aplicativo do Silverlight. A Figura 2 mostra um manipulador HTTP do ASP.NET que grava o conteúdo de um arquivo XAP no fluxo de resposta HTTP.

Figura 2 Um manipulador HTTP que retorna um pacote XAP

<%@ WebHandler Language="C#" Class="XapGenHandler" %>

using System;
using System.Web;

public class XapGenHandler : IHttpHandler 
{
    public void ProcessRequest (HttpContext context) 
    {
        // XAP file to return 
        string xapFile = "...";

        // Set the type of data we're returning
        context.Response.ContentType = "application/octet-stream";

        // Create some XAML and return it down the wire
        content.Response.WriteFile(xapFile);
    }


    public bool IsReusable 
    {
        get {return true;}
    }

}

O código de exemplo lê os dados XAP de um arquivo existente. Nem é preciso dizer que, se você incorporar uma biblioteca ZIP em seu projeto, poderá facilmente montar o pacote dinamicamente combinando DLLs diferentes e então criando um arquivo de manifesto XML apropriado.

Se estiver retornando conteúdo XAP, você deverá definir o tipo de conteúdo da resposta como application/octet-stream — o tipo de MIME que geralmente identifica conteúdo binário genérico.

Para associar o plug-in a um manipulador HTTP, ou a qualquer outro ponto de extremidade de sua escolha, use as técnicas usuais de programação do Silverlight. Por exemplo, você pode usar o controle de servidor do Silverlight em uma página ASP.NET:

<asp:Silverlight ID="Xaml1" runat="server" 
    Source="~/xap.ashx" 
    MinimumVersion="2.0.30523" 
    Width="100%" 
    Height="100%" />

Nos dois exemplos, a fábrica do aplicativo do Silverlight reside no servidor Web. Essa é uma abordagem excelente se a página host precisa descobrir dinamicamente o conteúdo que deve baixar.

No entanto, esse é apenas um dos possíveis cenários. Há outro cenário, provavelmente mais comum — a necessidade de baixar componentes opcionais para o aplicativo atual do Silverlight. Nesse caso, toda a lógica para selecionar e baixar conteúdo externo está em execução no cliente, no plug-in Silverlight.

Conteúdo sob demanda

O Silverlight 2 oferece uma API sofisticada e poderosa para baixar código e/ou XAML sob demanda e que você pode usar para baixar conteúdo e inseri-lo no modelo de objeto de documento XAML existente.

Todos os elementos visuais de uma árvore XAML apresentam uma propriedade chamada Children, que pode ser usada para adicionar ou remover programaticamente elementos filho de qualquer tamanho. Por exemplo, você pode acrescentar todo um controle de usuário baixado do mesmo servidor ou de um servidor remoto opcional confiável. A linha a seguir mostra um exemplo:

StackPanel1.Children.Add(downloadedContent);

O controle de usuário representado pelo argumento é adicionado à coleção Children de um elemento StackPanel XAML. O processamento é imediato, e a interface do usuário é atualizada em tempo real.

Você pode fazer muito mais do que simplesmente baixar conteúdo e anexá-lo ao modelo de objeto de documento existente. Você pode, por exemplo, armazená-lo em cache localmente, no armazenamento local do aplicativo, e verificar o conteúdo sob demanda em seu próprio cache antes de fazer uma nova solicitação ao servidor.

Esse método proporciona o armazenamento permanente do conteúdo baixado. No entanto, em alguns casos isso pode ser um exagero. Existe outra opção, mais simples, que não requer nenhum trabalho extra: deixar o navegador armazenar em cache o recurso XAP.

Cache do conteúdo baixado

O pacote XAP obtido no servidor Web não tem significado especial para o navegador. Portanto, o navegador o armazenará em cache como faz com qualquer outra coisa que obtém do servidor Web, respeitando as diretrizes de cache de solicitações determinadas pelo controle de cache e pelo cabeçalho HTTP "expires" da solicitação ou marcas META semelhantes na página HTML host.

Observe que, quando há um recurso XAP para ser baixado no navegador, você pode controlar o cache pelas configurações da página que você geralmente insere usando marcas META ou atributos de diretiva do ASP.NET. Caso o recurso XAP deva ser baixado via manipulador HTTP, como no exemplo anterior, você pode controlar o cache da solicitação específica.

Outro ponto digno de nota: o que é armazenado em cache é o conteúdo XAP original, inclusive os assemblies e o XAML. Portanto, o aplicativo em execução pode modificar programaticamente o XAML original. No entanto, essas alterações não serão automaticamente armazenadas em cache, da mesma forma que qualquer recurso extraído de um pacote XAP (mídia, imagens etc) não é armazenado em cache separadamente. Assim, quando o usuário visitar a página, o pacote XAP não será baixado novamente (a menos que tenha expirado), mas os recursos serão novamente extraídos. Além disso, todas as alterações feitas nesses recursos em sessões anteriores serão perdidas. Para manter as alterações no modelo de objeto de documento XAML, você precisa providenciar seu próprio cache personalizado. (Essa é uma técnica bem interessante que abordarei na parte 2 deste tópico.)

Por fim, observe que o pacote XAP salvo no cache do navegador está à mercê do usuário. Se em algum momento o usuário decidir limpar o cache, tudo o que estiver lá será perdido — inclusive os pacotes XAP. Para armazenar permanentemente os pacotes XAP do Silverlight, você precisa recorrer a armazenamento isolado (esse tópico também está programado para a parte 2).

Uma ferramenta para downloads

Ao escrever aplicativos do Silverlight, lembre-se de que todos os recursos de que você precisar e que não estiverem já incluídos no XAP do aplicativo deverão ser explicitamente baixados do servidor. A classe WebClient é a principal ferramenta do Silverlight a ser usada para organizar downloads de recursos adicionais. A classe oferece alguns métodos assíncronos de enviar dados a um recurso da Web e receber dados de um recurso da Web. Veja como funciona:

WebClient wc = new WebClient();
wc.DownloadStringCompleted += 
     new DownloadStringCompletedEventHandler(callback);
wc.DownloadStringAsync(address);

O método DownloadStringAsync opera um HTTP GET e captura a resposta da URL como cadeia de caracteres. A cadeia de caracteres é recebida pelo retorno de chamada associado, como mostrado aqui:

void callback(object sender, DownloadStringCompletedEventArgs e)
{
  if (e.Error != null)
     return;

  string response = e.Result;
...
}

Como você verá daqui a pouco, esse método é perfeito para baixar XAML sem formatação e sem code-behind anexado. Para baixar programaticamente dados binários, como um pacote XAP, você precisa de um fluxo e de uma abordagem um pouco diferente. A classe WebClient ainda é útil, pois fornece um método adequado em OpenReadAsync:

WebClient wc = new WebClient();
wc.OpenReadCompleted += 
     new OpenReadCompletedEventHandler(callback);
wc.OpenReadAsync(address);

A estrutura do retorno de chamada associado é a mesma do caso anterior. Em resumo, use o método DownloadStringAsync para obter uma cadeia de caracteres simples; use o método OpenReadAsync para obter um fluxo de quaisquer dados. A decisão de baixar uma cadeia de caracteres ou um fluxo é principalmente uma questão de preferência e depende essencialmente de como você pretende usar os dados recebidos.

Observe também que WebClient oferece um par de métodos para gravar em uma URL remota: UploadStringAsync para postar uma cadeia e OpenWriteAsync para carregar quaisquer dados em uma URL, usando um fluxo.

Você pode usar a propriedade Headers para especificar cabeçalhos adicionais porque a classe, por padrão, não especifica nenhum. Observe, porém, que alguns dos cabeçalhos que você definir serão suprimidos pelo Framework e gerenciados internamente. Entre eles estão Referer, Connection e User-Agent. O cabeçalho Content-Type, se definido, será preservado.

Quando se trata de baixar um recurso, a classe WebClient usa de modo transparente o cache do navegador antes de tentar uma conexão. Se o recurso XAML ou XAP não estiver no cache, a classe continuará o download. O processo de baixar conteúdo de um aplicativo do Silverlight é resultado dos esforços combinados do runtime do Silverlight e da API interna que o navegador host expõe a um plug-in. Isso significa que, nos bastidores de WebClient, o runtime do Silverlight diz para a área de segurança do navegador verificar se o recurso solicitado já está no cache. Se não estiver, o Silverlight continuará com suas próprias diretivas de segurança para autorizar a solicitação. Quando os dados finalmente retornam do ponto de extremidade, o runtime do Silverlight armazena-os localmente por meio dos serviços do navegador, respeitando integralmente as diretivas de cache vigentes.

No namespace System.Net do Silverlight, há várias restrições de segurança à classe WebClient e a outras classes de HTTP. Em particular, a classe WebClient só oferece suporte a esquemas HTTP e HTTPS quando se baixa via fluxo e ao esquema FILE quando se baixa XAML simples. O acesso entre esquemas é sempre proibido, de modo que você pode apontar WebClient para, digamos, um recurso HTTPS se o download da página host tiver ocorrido via HTTP (e vice-versa). Uma solicitação WebClient pode, geralmente, alcançar uma URL em outra zona do navegador, mas não quando tenta passar de uma zona da Internet para outra mais restritiva. Atualmente, o Silverlight só dá suporte a zonas em um sistema operacional Windows.

Por fim, o acesso entre domínios só tem suporte se o site remoto tiver decidido hospedar um arquivo XML adequado no diretório raiz. Observe também que o acesso entre domínios não funciona em um cenário HTTPS-para-HTTPS.

O mesmo objeto WebClient não pode lidar simultaneamente com várias solicitações. Você deve verificar a propriedade IsBusy — um valor booleano — para determinar se é seguro seu código fazer uma nova solicitação na mesma instância de WebClient. Se você usar vários objetos WebClient — talvez em threads diferentes — poderá iniciar múltiplos downloads ao mesmo tempo.

Baixando dados somente XAML

Vejamos como usar WebClient para baixar dados XAML e integrá-los à árvore visual: Nos aplicativos do Silverlight 2, o download dinâmico de dados XAML sem formatação não oferece necessariamente a eficiência de programação de que você precisa. A cadeia de caracteres XAML deve ser XAML sem formatação e sem referências a serem resolvidas em tempo de execução, como associações ou referências a estilos, recursos e eventos.

Assim que a cadeia de caracteres XAML for baixada, use a classe XamlReader para transformá-la em um elemento da interface do usuário que possa ser adicionado ao modelo de objeto de documento existente. O código a seguir mostra como baixar programaticamente uma cadeia de caracteres XAML a partir de uma URL. Você precisa fornecer a URL como um objeto Uri:

WebClient client = new WebClient();
client.DownloadStringCompleted += 
   new DownloadStringCompletedEventHandler(OnDownloadCompleted);
Uri uri = new Uri("xaml.ashx", UriKind.Relative);
client.DownloadStringAsync(uri);

A URL pode apontar para um recurso XAML sem formatação ou para um ponto de extremidade que retorne uma resposta em texto/xaml. Este é o código para processar o XAML baixado e acrescentá-lo a um espaço reservado na árvore visual:

void OnDownloadCompleted(object sender, DownloadStringCompletedEventArgs e)
{
    // Parse XAML to a UI element 
    string xaml = e.Result;
    UIElement dom = XamlReader.Load(xaml) as UIElement;

    // Append to the DOM
    Placeholder.Children.Clear();
    Placeholder.Children.Add(dom);
}

Como já mencionado, um espaço reservado pode ser qualquer elemento do modelo de objeto de documento que esteja, no momento, sendo processado no plug-in. Observe que os filhos de um elemento da interface do usuário formam uma coleção e são processados como uma sequência. Isso significa, no caso de atualizações, que você deve primeiro remover elementos e então adicioná-los, para evitar a sobreposição indesejada de elementos.

A serialização de XAML, que é executada pelas classes XamlReader e XamlWriter, baseia-se no princípio de que referências a extensões são retiradas e os valores de tempo de execução são salvos sobre as configurações de tempo de design. E se você quiser baixar conteúdo XAML e personalizá-lo antes de exibir, como, por exemplo, por associação dinâmica de dados? Você não pode incorporar associações no XAML de origem, mas pode definir espaços reservados no XAML baixado, recuperá-las com análise e defini-las com qualquer valor, programaticamente. No Silverlight 2, entretanto, baixar um pacote XAP é, sem dúvida, uma solução melhor.

Trabalhando com pacotes XAP

Um pacote XAP contém um aplicativo Silverlight completo cuja interface do usuário consiste, essencialmente, em um controle de usuário que, na verdade, é apenas um contêiner do código e da marcação XAML.

Como já foi dito, um pacote XAP contém um ou mais assemblies controlado(s) por um arquivo de manifesto. O processamento de um pacote XAP exige duas etapas extras além do download. É preciso extrair o assembly principal e então criar uma instância da classe do ponto de entrada que inicializa o aplicativo baixado. Nem é preciso dizer que, nesse caso, você pode usar associação, estilos, eventos e qualquer outra coisa que deseje no XAML. Quando você trabalha com um pacote XAP, é o runtime do Silverlight — não a API de serialização — que processa o XAML e então resolve as referências. Isso faz uma enorme diferença em termos de eficiência de programação.

Baixar e processar um pacote XAP exige um pouco mais de trabalho do que simplesmente criar um modelo de objeto fora de uma cadeia de caracteres. Parte desse trabalho — geralmente o download do conteúdo e a extração do assembly — pode ser movida para uma classe downloader reutilizável (veja a Figura 3).

Figura 3 Baixando um pacote XAP dinamicamente

public partial class Page : UserControl
{
    private UIElement content = null;
    private TabItem item = null;

    public Page()
    {
        InitializeComponent();
    }

    private void chkNewContent_Click(object sender, RoutedEventArgs e)
    {
        bool shouldDisplay = (sender as CheckBox).IsChecked.Value;   

        if (shouldDisplay)
        {
            if (!IsContentAvailable())
                DownloadContent();
            else
                ShowContent();
        }
        else
        {
            HideContent();
        }
    }

    private bool IsContentAvailable()
    {
        return (content != null);
    }

    private void DownloadContent()
    {
        Downloader dl = new Downloader();
        dl.XapDownloaded += 
            new EventHandler<XapEventArgs>(OnPackageDownload);
        dl.LoadPackage("more.xap", "more.dll", "More.ExtraTab");
    }

    void OnPackageDownload(object sender, XapEventArgs e)
    {
        content = e.DownloadedContent;
        ShowContent();
    }

    private void HideContent()
    {
        this.TabList.Items.Remove(item);
    }

    private void ShowContent()
    {
        item = new TabItem();
        item.Header = "Extra tab";
        item.Content = content;
        this.TabList.Items.Add(item);
    }
}

O código da Figura 3 mostra um exemplo de aplicativo baseado em guias que carrega uma nova guia na primeira vez em que o usuário clica em uma caixa de seleção. Nesse caso, é baixado um novo pacote XAP, e o controle de usuário contido na interface do usuário é inserido em um TabItem recém-criado. Ocultar o conteúdo do pacote recém-baixado é uma operação simples de cliente. E mostrá-lo novamente não exige uma segunda viagem de ida e volta, por dois bons motivos: o conteúdo está no cache da memória, e o pacote a partir do qual ele foi criado está armazenado no cache do navegador.

A Figura 4 mostra a interface do usuário desse aplicativo de exemplo. O conteúdo inserido na guia adicional vem de um novo projeto de aplicativo do Silverlight. Em termos de implantação, tudo o que você precisa fazer é armazenar o pacote XAP na pasta do site host na Web (o local padrão em que o WebClient procura conteúdo relativo) ou em qualquer outro local que seja seguro para o WebClient acessar.

fig04.gif

Figura 4 Um aplicativo capaz de baixar dinamicamente partes da interface do usuário

Agora, vamos explorar o código da classe auxiliar Downloader. Observe que a classe Downloader usada nesse exemplo é uma classe interna e não tem nenhuma ligação com o objeto Downloader do Silverlight disponível para chamadores JavaScript. O objeto downloader de JavaScript é essencialmente um wrapper do WebClient que pode ser chamado via script.

Processando conteúdo XAP

A classe Downloader usa o WebClient para baixar um pacote XAP e então extrai do fluxo compactado o assembly que contém o aplicativo e cria a instância da classe raiz especificada. Toda essa lógica está oculta em uma camada de programação relativamente simples, composta de um método LoadPackage e um evento XapDownloaded. O método tem a seguinte assinatura:

public void LoadPackage(
    string xapURL, string assemblyName, string className);

Ele recebe a URL para o pacote, o nome do assembly do pacote a ser extraído e o nome da classe cuja instância deve ser criada. Como já mencionado, essa classe é o code-behind do arquivo XAML da interface.

O evento XapDownloaded é disparado quando a classe downloader termina o processamento do XAP e o controle de usuário está pronto para o aplicativo cliente. Veja a definição do evento:

public event 
    EventHandler<XapEventArgs> XapDownloaded;

A classe de argumento do evento retorna as seguintes informações:

public class XapEventArgs : EventArgs
{
  public UserControl DownloadedContent;
}

Vamos analisar a lógica do método LoadPackage. O método utiliza a interface binária do WebClient para apontar para o pacote XAP:

void LoadPackage(string package, string asm, string cls)
{
    assemblyName = asm;
    className = cls;

    Uri address = new Uri(package, UriKind.RelativeOrAbsolute); 
    WebClient client = new WebClient();
    client.OpenReadCompleted += 
       new OpenReadCompletedEventHandler(OnCompleted);
    client.OpenReadAsync(address);
}

O download continua de modo assíncrono e, ao terminar, dispara o evento OpenReadCompleted. A Figura 5 mostra a implementação do manipulador do evento.

Figura 5 Download concluído

void OnCompleted(object sender, OpenReadCompletedEventArgs e)
{
    if (PackageDownloaded == null)
        return;

    if (e.Error != null)
        return;

    // Load a particular assembly from the XAP package
    Assembly a = GetAssemblyFromPackage(assemblyName, e.Result);

    // Get an instance of the XAML object
    object page = a.CreateInstance(className);  

    // Fire the event
    XapEventArgs args = new XapEventArgs();
    args.DownloadedContent = page as UserControl;
    XapDownloaded(this, args);
}

Se não ocorrer nenhum erro, uma função manipuladora extrairá do pacote o assembly especificado e obterá uma instância da classe de controle de usuário no code-behind do bloco XAML a ser adicionado. A seguir, um evento personalizado será disparado para o chamador de modo que a subárvore possa ser processada. Nesse ponto, resta um aspecto a ser esclarecido: os detalhes de como um assembly é extraído do fluxo compactado que foi baixado da URL. Esse código é mostrado na Figura 6. Esse é o código clichê que você usa para extrair um assembly de um pacote XAP. StreamResourceInfo e Application.GetResourceStream também são ferramentas comuns para extrair pacotes de recursos como imagens ou conteúdo de mídia do XAP atual.

Figura 6 Extraindo um assembly de um pacote XAP

Assembly GetAssemblyFromPackage(string assemblyName, Stream xap)
{
    // Local variables
    Uri assemblyUri = null;
    StreamResourceInfo resPackage = null;
    StreamResourceInfo resAssembly = null;
    AssemblyPart part = null;

    // Initialize
    assemblyUri = new Uri(assemblyName, UriKind.Relative);
    resPackage = new StreamResourceInfo(xap, null);
    resAssembly = Application.GetResourceStream(resPackage, assemblyUri);

    // Extract an assembly 
    part = new AssemblyPart();
    Assembly a = part.Load(assemblySri.Stream);
    return a; 
}

Resumo

O esperado é que os aplicativos do Silverlight sejam baixados e instalados rapidamente e que a execução seja veloz na máquina do usuário. O código gerenciado supera o JavaScript interpretado, de modo que o desempenho será realmente rápido. Mas — como baixar aplicativos grandes do Silverlight ou aplicativos que requeiram grandes gráficos externos e conteúdo de mídia pode criar uma latência indesejada — é útil dividir o download em etapas. Melhor ainda se você programar essas etapas para ocorrerem sob demanda.

A classe WebClient oferece uma API de programação assíncrona e eficiente para baixar qualquer tipo de recurso que possa ser acessado na Internet. O modelo extensível do modelo de objeto do Silverlight permite incorporar rapidamente qualquer alteração na estrutura existente. Por fim, a API baseada em fluxo e centralizada na classe StreamResourceInfo facilita a extração de conteúdo dos recursos de assemblies e pacotes XAP.

Todos os recursos baixados são armazenados em cache pelo navegador. No entanto, esse tipo de cache não é permanente. Na parte 2 desta coluna, usarei o armazenamento isolado para fazer com que qualquer conteúdo baixado permaneça por mais tempo na máquina do usuário, poupando assim muitas viagens de ida e volta potenciais. Para conhecer outros pontos positivos do Silverlight, consulte "Dicas, truques e práticas recomendadas do Silverlight" na coluna Wicked Code, de Jeff Prosise.

Envie dúvidas e comentários para Dino pelo email cutting@microsoft.com.

Dino Esposito é arquiteto da IDesign e co-autor de Microsoft .NET: Architecting Applications for the Enterprise (Microsoft Press, 2008). Residente na Itália, Dino é um palestrante sempre presente em eventos do setor no mundo inteiro. Entre em contato com ele por meio de seu blog em weblogs.asp.net/despos.