次の方法で共有


Cutting Edge

Silverlight で動的コンテンツ配信を管理する (第 1 部)

Dino Esposito

コードは MSDN コード ギャラリーからダウンロードできます。
オンラインでのコードの参照

目次

Silverlight アプリケーションのサイズ
動的に生成された XAML
動的に生成された XAP
オンデマンド コンテンツ
ダウンロードしたコンテンツをキャッシュする
ダウンロード用ツール
XAML 専用データをダウンロードする
XAP パッケージを使用する
XAP コンテンツを処理する
まとめ

Rich Internet Application (RIA) のユーザーが、セキュリティおよびダウンロード サイズに関する懸念を持つのは当然のことです。Silverlight アプリケーションは Microsoft .NET Framework のサブセットを基盤にしているため、ローカル ユーザーのコンピュータに対して有害な動作をする可能性があります。そのため、Silverlight チームは、アプリケーションからコア CLR (.NET Framework の Silverlight 版) のセキュリティ上重要なクラスを呼び出すことができる新しいセキュリティ モデルを開発しました。Silverlight のセキュリティ モデルの詳細については、「CoreCLR を使用して Silverlight のプログラムを記述する」を参照してください。また、Silverlight アプリケーションの構築の詳細については、「Web 上でより豊かな体験を作る」を参照してください。

Silverlight プラグインのダウンロード処理そのものは数秒で完了する 1 回きりの操作なので、特に問題はありませんが、ダウンロードされたアプリケーションのサイズについてはどうでしょうか。

今月のコラムでは、Silverlight アプリケーションのダウンロードの問題に取り組みます。まず、XAML を動的に生成する方法を示します。次に、厳密にユーザーごとの要求に基づいてアプリケーションの機能を動的に有効にする方法について説明します。アプリケーション固有の機能の中には、実装の負荷が大きく、コードのメイン ストリームを別に実装できるものがあります。さらに重要なことは、これらの機能はダウンロードも別に行うことができるうえ、基本ユーザー インターフェイスとの統合も容易です。

Silverlight アプリケーションのサイズ

Silverlight プラグインをインストールすると、Silverlight アプリケーションで使用される可能性があるすべてのシステム アセンブリが配置されます。そのため、ダウンロードはそのアプリケーションを含むアセンブリと参照されているカスタム アセンブリに限定され、結果としてアプリケーションのダウンロード サイズは多くの場合に数十キロバイトになります。この見積もりは Silverlight 2 の RTM バージョンや、リリース モードでコンパイルされたコードにも当てはまります。

アプリケーションのフットプリントは、特に長いアルゴリズム、グラフィック コンテンツやメディア コンテンツ、またはアニメーションを含む場合には、もっと大きくなる場合もあります。ダウンロード時間が問題になるような大きいアプリケーションを処理するには、主に 2 つの方法があります。その 1 つは、Silverlight コンテンツをストリームする方法です (silverlight.live.com を参照)。もう 1 つは、アプリケーションを独立したパーツに分割し、必要に応じて個別にダウンロードできるようにする方法です。

動的に生成された XAML

Silverlight プラグインは、基本的に XAML コンテンツを表示することを目的としています。XAML に分離コードが存在する場合、プラグインはその分離コードを処理してユーザー インターフェイスを生成し、コードに記述された動作や効果をサポートします。XAML のみをダウンロードする場合は URL を使用して直接参照できます。それ以外の場合は、XAP 拡張子を使用して Silverlight パッケージを参照できます。

XAP パッケージにはマニフェストと 1 つまたは複数のアセンブリが含まれます。1 つのアセンブリにアプリケーションのエントリ ポイントが含まれ、他のアセンブリは参照用アセンブリとして使用されます。ユーザー インターフェイスの XAML はエントリ ポイント アセンブリのリソースに保存されます。XAP パッケージは、プロジェクトの作成およびビルド時に Silverlight 2 の Visual Studio 2008 拡張機能によって作成されます。

Silverlight プラグインで処理できるのは XAML および XAP ストリームのみですが、これらのコンテンツをダウンロードするには、必ずしもサーバー上の物理的な XAML または XAP リソースを参照する必要はありません。たとえば、動的に生成された XAML または XAP コンテンツを返すことができる URL を参照する方法もあります。

図 1 は、実行時に作成された XAML を返すサンプル ASP.NET HTTP ハンドラを示しています。ProcessRequest メソッドは、構成データ、パラメータ、または実行時の条件に基づいて、Response オブジェクトにコンテンツの種類を設定し、動的に生成される XAML などの XAML コンテンツを記述します。Response オブジェクトに Expires プロパティを設定することにより、クライアント上にリソースがキャッシュされないようにすることもできます。この機能は、コンテンツが定期的に変更され、情報の更新が必要な場合に有効です。

図 1 XAML を返す HTTP ハンドラ

<%@ 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

動的に生成された XAP パッケージを返す方法は、生の XAML テキストを返す方法と同様ですが、XAP パッケージはプレーンテキスト ファイルではないという点が異なります。XAP パッケージは、XML マニフェストと 1 つまたは複数のアセンブリを含む ZIP ファイルです。パッケージ形式を使用することにより、Silverlight アプリケーションで使用するコンテンツ全体をダウンロードするために必要なラウンドトリップの回数が少なくなりました。図 2 は、XAP ファイルのコンテンツを HTTP 応答ストリームに出力する ASP.NET HTTP ハンドラを示しています。

図 2 XAP パッケージを返す HTTP ハンドラ

<%@ 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;}
    }

}

このサンプル コードでは、既存のファイルから XAP データを読み取ります。言うまでもなく、プロジェクトに ZIP ライブラリを埋め込む場合、複数の異なる DLL を結合し、適切な XML マニフェスト ファイルを作成することにより、パッケージを実行時に簡単に構成できます。

XAP コンテンツを返す場合、アプリケーションまたはオクテット ストリーム (汎用的なバイナリ コンテンツを共通に識別する MIME の種類) に対する応答のコンテンツの種類を設定します。

プラグインを HTTP ハンドラまたは選択した他のエンドポイントに関連付けるには、通常の Silverlight のプログラミング手法を使用します。たとえば、ASP.NET ページで Silverlight サーバー コントロールを使用できます。

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

どちらの場合も Silverlight アプリケーションのファクトリは Web サーバー上に存在します。これは、ダウンロードするコンテンツをホスト ページで動的に確認する必要がある場合に優れた方法です。

とは言え、これは方法の 1 つにすぎません。おそらく、より一般的なケースでは、現在の Silverlight アプリケーションのオプション コンポーネントをダウンロードする必要があります。この場合、外部コンテンツを選択してダウンロードするロジックは、すべてクライアント上の Silverlight プラグインで実行されます。

オンデマンド コンテンツ

Silverlight 2 には、コードや XAML をオンデマンドでダウンロードする高機能 API が豊富に揃っています。これらの API を使用して、コンテンツをダウンロードし、既存の XAML ドキュメント オブジェクト モデルに挿入することができます。

XAML ツリーのすべてのビジュアル要素に Children というプロパティが存在し、このプロパティを使用して任意のサイズの子要素をプログラムで追加または削除できます。たとえば、同じサーバーから、または信頼されたオプトイン リモート サーバーからダウンロードされたユーザー コントロール全体を追加できます。次の行は一例を示しています。

StackPanel1.Children.Add(downloadedContent);

引数で指定されたユーザー コントロールが StackPanel XAML 要素の Children コレクションに追加されます。レンダリングは即座に行われ、ユーザー インターフェイスはリアルタイムに更新されます。

コンテンツをダウンロードするだけでなく、既存のドキュメント オブジェクト モデルにアタッチすることもできます。たとえば、コンテンツをアプリケーションのローカル記憶域にローカルにキャッシュしておき、サーバーに新しい要求を送信する前に、固有のキャッシュからオンデマンド コンテンツを探すことが可能です。

この方法では、ダウンロードしたコンテンツが固定記憶域に格納されます。ただし、場合によっては、ここまでの処理は必要ありません。より単純で、追加作業が不要なオプションとして、ブラウザが持つ XAP リソースの自動キャッシュ機能に処理を任せる方法があります。

ダウンロードしたコンテンツをキャッシュする

Web サーバーから取得した XAP パッケージは、ブラウザにとっては特別な存在ではありません。そのため、ブラウザは XAP パッケージを Web サーバーから取得した他のコンテンツと同様にキャッシュし、Cache-Control によって指定された要求のキャッシュ ポリシーに従って、要求の HTTP ヘッダーまたはホスト HTML ページ内の同様のメタ タグを期限切れにします。

ブラウザにダウンロードする XAP リソースが存在する場合、通常は、メタ タグまたは ASP.NET のディレクティブ属性を使用して挿入するページの設定によって、キャッシュを制御できます。前述の例のように HTTP ハンドラを使用して XAP リソースをダウンロードする場合、要求ごとにキャッシュを制御できます。

キャッシュされるのはアセンブリや XAML を含む元の XAP コンテンツであることも注目すべき点です。したがって、実行中のアプリケーションでプログラムを使用して元の XAML を変更できます。ただし、このような変更は自動的にはキャッシュされず、XAP パッケージ (メディアやイメージなど) から抽出可能なリソースも個別にキャッシュされるわけではありません。ユーザーがページを参照すると、XAP パッケージが再ダウンロードされるのではなく (再ダウンロードは期限切れになった場合にのみ行われます)、リソースが再抽出されます。また、以前のセッションで行ったリソースの変更は失われます。変更を XAML ドキュメント オブジェクト モデルに保存するには、固有のカスタムメイドのキャッシュを用意する必要があります (この優れた方法については、第 2 部で説明します)。

ブラウザのキャッシュに保存された XAP パッケージはユーザーが制御するという点にも注意してください。ユーザーがキャッシュのクリアを選択すれば、XAP パッケージも含めてキャッシュの内容は失われます。Silverlight XAP パッケージを永続的に保存するには、独立したストレージに保存する必要があります (この点についても第 2 部で説明する予定です)。

ダウンロード用ツール

Silverlight アプリケーションを作成する場合、アプリケーションの XAP にパッケージ化されていないすべてのリソースをサーバーから明示的にダウンロードする必要があることを憶えておいてください。WebClient クラスは、追加リソースのダウンロードを準備するために使用する主要な Silverlight ツールです。WebClient クラスには、Web リソースとのデータの送受信を行う複数の非同期メソッドがあります。そのしくみを次に示します。

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

DownloadStringAsync メソッドは、HTTP GET を操作して URL 応答を文字列形式で取得します。次に示すように、この文字列は関連付けられたコールバックによって取得されます。

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

  string response = e.Result;
...
}

すぐにわかると思いますが、このメソッドは分離コードがアタッチされていないプレーンな XAML のダウンロードに最適です。ところが、XAP パッケージなどのバイナリ データをプログラムでダウンロードするには、ストリームが必要なため、方法を少し変える必要があります。この場合は、WebClient クラスの OpenReadAsync のメソッドが適しています。

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

関連付けられたコールバックの構造体は前述の場合と同じです。最後に、DownloadStringAsync メソッドを使用して単純な文字列を取得します。それには、OpenReadAsync メソッドでデータのストリームを取得します。文字列またはストリームのどちらをダウンロードするかはほとんど好みの問題で、基本的には取得するデータの使用方法によって決まります。

WebClient には、リモート URL への書き込みメソッドもいくつか用意されています。文字列を送信する場合は UploadStringAsync を使用し、ストリームを使用して URL にデータをアップロードする場合は OpenWriteAsync を使用します。

クラスでは指定されていないため、Headers プロパティを使用して追加ヘッダーを指定できます。ただし、.NET Framework によってヘッダーの一部が削除され、内部的に管理される場合があることに注意してください。Referer、Connection、User-Agent などのヘッダーがこれに該当します。Content-Type ヘッダーは、設定されていれば保持されます。

リソースをダウンロードする場合、WebClient クラスは接続を試行する前にブラウザのキャッシュを透過的に使用します。XAML または XAP リソースがキャッシュに存在しない場合、ダウンロードを開始します。Silverlight アプリケーションからコンテンツをダウンロードするプロセスは、Silverlight ランタイムと、ホスト ブラウザがプラグインに公開する内部 API の共同作業です。WebClient の背後では、Silverlight ランタイムがブラウザのサンドボックスと通信を行って、要求されたリソースが既にキャッシュに存在するかどうかを確認します。リソースが存在しない場合、Silverlight が固有のセキュリティ ポリシーを開始して要求を承認します。データが最終的にエンドポイントから返されると、Silverlight ランタイムはブラウザのサービスを使用してそのデータをローカルにキャッシュし、現行のキャッシュ ポリシーに完全に準拠します。

複数のセキュリティ制限が WebClient クラスおよび Silverlight の System.Net 名前空間内の他の HTTP クラスに適用されます。具体的に言うと、WebClient クラスは、ストリームを使用してダウンロードする場合は HTTP および HTTPS スキームのみをサポートし、純粋な XAML をダウンロードする場合は FILE スキームのみをサポートします。スキーム間のアクセスは常に禁止されているため、たとえば、ホスト ページを HTTP でダウンロードした場合に WebClient から HTTPS リソースを参照することはできません。また、ホスト ページを HTTPS でダウンロードした場合に WebClient から HTTP リソースを参照することはできません。一般に、WebClient 要求は URL をブラウザの異なるゾーンに送信できますが、インターネット ゾーンから、より制限の多いゾーンに移動しようとした場合は URL を送信できません。現在は、Silverlight を Windows オペレーティング システム上で実行した場合にのみゾーンがサポートされます。

また、ドメイン間のアクセスは、ルート ディレクトリに適切な XML ファイルをホストすることによってリモート サイトをオプトインした場合にのみサポートされます。HTTPS 間ではドメイン間アクセスを利用できないことにも注意してください。

1 つの WebClient オブジェクトで複数の要求を同時に処理することはできません。この場合、IsBusy プロパティ (ブール値) を確認して、同じ WebClient インスタンスに新しい要求を設定してもコードが安全かどうかを判断する必要があります。(おそらくは異なるスレッドで) 複数の WebClient オブジェクトを使用すれば、複数のダウンロードを同時に開始できます。

XAML 専用データをダウンロードする

次に、WebClient を使用して XAML データをダウンロードし、ビジュアル ツリー内で統合する方法を説明します。Silverlight 2 アプリケーションでは、プレーンな XAML データの動的ダウンロードによって必要なプログラミング機能が満たされるとは限りません。XAML 文字列は、バインドや、スタイル、リソース、イベントへの参照のような実行時の参照解決を行わないプレーンな XAML である必要があります。

ダウンロードした XAML 文字列は、XamlReader クラスを使用して、既存のドキュメント オブジェクト モデルに追加可能な UI 要素に変換します。次のコードは、プログラムを使用して URL から XAML 文字列をダウンロードする方法を示しています。URL は Uri オブジェクトとして指定する必要があります。

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

URL は、プレーンな XAML リソースまたは text/xaml 応答を返すエンドポイントを参照できます。ダウンロードした XAML を処理し、ビジュアル ツリーのプレースホルダに追加するコードを次に示します。

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);
}

前述したように、プレースホルダには、現在プラグインでレンダリングが完了しているドキュメント オブジェクト モデル内の任意の要素を指定できます。UI 要素の子によってコレクションが構成され、1 つのシーケンスとしてレンダリングされます。更新の際に望ましくない要素の重複を防ぐには、要素をいったん削除してから追加してください。

XamlReader クラスおよび XamlWriter クラスを使用した XAML のシリアル化は、エクステンションの参照は解除され、実行時の値はデザイン時の設定に優先して保存されるという原則に基づいて実行されます。では、ダウンロードした XAML コンテンツを、動的データ バインドなどを使用してカスタマイズしてから表示する場合はどうでしょうか。バインドを XAML ソースに埋め込むことはできませんが、ダウンロードした XAML にプレースホルダを定義し、解析して取得し、プログラムで任意の値に設定することができます。ただし、Silverlight 2 の場合は、XAP パッケージをダウンロードする方が確実に優れた方法です。

XAP コンテンツを処理する

XAP パッケージには、Silverlight アプリケーション全体が含まれます。このアプリケーションのユーザー インターフェイスは、基本的にユーザー コントロール (単に XAML マークアップおよびコードのコンテナ) で構成されます。

前述したように、XAP パッケージには、マニフェスト ファイルで追跡されている 1 つまたは複数のアセンブリが含まれています。XAP パッケージの処理には、ダウンロード以外に 2 つの追加手順があります。つまり、メイン アセンブリを抽出し、ダウンロードしたアプリケーションを起動するエントリ ポイント クラスをインスタンス化する必要があります。言うまでもなく、この場合にはバインド、スタイル、イベントなど、XAML に必要なあらゆる要素を使用できます。XAP パッケージを操作する場合、XAML を処理し、参照を解決するのはシリアル化 API ではなく、Silverlight ランタイムです。この方法を利用した場合、プログラミング能力が格段に違います。

XAP パッケージのダウンロードおよび処理には、文字列からオブジェクト モデルを構築する以外に必要な追加作業があります。この追加作業の一部 (一般的にはコンテンツのダウンロードとアセンブリの抽出) は、再利用可能なダウンローダ クラスに移行できます (図 3 を参照)。

図 3 XAP パッケージを動的にダウンロードする

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);
    }
}

図 3 のコードは、ユーザーが初めてチェック ボックスをオンにしたときに新しいタブを読み込むタブベースのサンプル アプリケーションを示しています。この場合、新しい XAP パッケージがダウンロードされ、そのユーザー インターフェイスに含まれているユーザー コントロールが新しく作成された TabItem に挿入されます。新しくダウンロードされたパッケージからコンテンツを非表示にするのは、単純なクライアント操作です。コンテンツを再度表示する場合に新たなラウンドトリップは不要です。その理由は、コンテンツはメモリにキャッシュされ、そのコンテンツの構築に使用されたパッケージはブラウザのキャッシュに格納されているからです。

図 4 は、このサンプル アプリケーションのユーザー インターフェイスを示しています。追加されたタブに挿入するコンテンツは、新しい Silverlight アプリケーション プロジェクトから取得します。展開に必要な作業は、XAP パッケージをホスト Web サイトの ClientBin フォルダ (WebClient が相対コンテンツを検索する既定の場所) または WebClient が安全にアクセスできる任意の場所に保存することだけです。

fig04.gif

図 4 UI の一部を動的にダウンロードする機能を持つアプリケーション

次に、ヘルパ Downloader クラスのコードについて説明します。この例で使用されている Downloader クラスは内部クラスであり、JavaScript の呼び出し元で使用可能な Silverlight Downloader オブジェクトとは無関係です。JavaScript のダウンローダ オブジェクトは、基本的にスクリプトで呼び出し可能な WebClient のラッパーです。

XAP コンテンツを処理する

Downloader クラスは、WebClient を使用して XAP パッケージをダウンロードし、アプリケーションを含むアセンブリを圧縮されたストリームから抽出して、指定されたルート クラスをインスタンス化します。このロジックはすべて、LoadPackage メソッドと XapDownloaded イベントで作成された比較的単純なプログラミング ファサードの背後に隠蔽されています。このメソッドのシグネチャは次のとおりです。

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

このメソッドで、パッケージの URL、抽出するパッケージのアセンブリ名、およびインスタンス化するクラスの名前を取得します。前述したように、このクラスは XAML インターフェイス ファイルの分離コードです。

XapDownloaded イベントは、ダウンローダ クラスが XAP の処理を完了し、クライアント アプリケーションのユーザー コントロールの準備ができたときに発生します。イベントの定義を次に示します。

public event 
    EventHandler<XapEventArgs> XapDownloaded;

イベントの引数クラスは次の情報を返します。

public class XapEventArgs : EventArgs
{
  public UserControl DownloadedContent;
}

次に、LoadPackage メソッドのロジックを示します。このメソッドでは、WebClient のバイナリ インターフェイスを使用して 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);
}

ダウンロードは非同期に行われ、ダウンロードが完了すると OpenReadCompleted イベントが発生します。イベント ハンドラの実装を図 5 に示します。

図 5 ダウンロード完了

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);
}

エラーが発生しなければ、ヘルパ関数が指定されたアセンブリをパッケージから抽出し、追加する XAML ブロックの分離コードでユーザー コントロール クラスのインスタンスを取得します。次に、XAML サブツリーを処理するためのカスタム イベントを呼び出し元に生成します。説明が必要な処理がもう 1 つ残っています。それは、URL からダウンロードした圧縮ストリームからアセンブリを抽出する具体的な方法です。図 6 に、そのコードを示します。これは、XAP パッケージからアセンブリを抽出するために使用する定型コードです。StreamResourceInfo および Application.GetResourceStream も、現在の XAP からイメージやメディア コンテンツなどのリソース パッケージを抽出するための一般的なツールです。

図 6 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; 
}

まとめ

Silverlight アプリケーションは、ユーザーのコンピュータ上で迅速にダウンロードおよびインストールされ、高速に実行されることが期待されます。マネージ コードはインタープリタ言語の JavaScript より処理が高速なため、パフォーマンスに優れています。ただし、大規模な Silverlight アプリケーションや、大きい外部グラフィックやメディア コンテンツを必要とするアプリケーションをダウンロードすることが原因で、好ましくない遅延が生じる可能性があります。このような場合には、ダウンロードを複数のステップに分割すると便利です。これらのステップがプログラムによってオンデマンドで実行されるようにすると、さらに便利です。

WebClient クラスには、インターネット上でアクセス可能なあらゆる種類のリソースをダウンロードするための有効な非同期のプログラミング API が用意されています。Silverlight オブジェクト モデルの拡張可能なモデルでは、既存の構造の変更をすぐに組み込むことができます。また、StreamResourceInfo クラスを中心としたストリームベースの API を使用すれば、アセンブリや XAP パッケージのリソースからコンテンツを簡単に抽出できます。

ダウンロードされたリソースはすべてブラウザによってキャッシュされますが、この場合のキャッシュは一時的です。このコラムの第 2 部では、独立したストレージを使用して、ダウンロードしたコンテンツをユーザーのコンピュータにより長期間保存し、発生する可能性がある多くのラウンドトリップを保存します。Silverlight の優れた機能の詳細については、Jeff Prosise の「Wicked Code」コラム、「Silverlight のヒント、テクニック、およびベスト プラクティス」を参照してください。

ご意見やご感想は、cutting@microsoft.com まで英語でお送りください。

Dino Esposito は、IDesign 社のアーキテクトであり、『Microsoft .NET: Architecting Applications for the Enterprise』(Microsoft Press、2008 年) の共著者です。Dino はイタリアに在住し、世界各国で開催される業界のイベントで頻繁に講演しています。ブログは weblogs.asp.net/despos で読むことができます。