2010年10月24日日曜日

NuGet(以前はNuPack)で簡単インストール

※NuPackはNuGetという名称に変更されました。リンク修正済み


オープンソースライブラリを簡単に開発環境にインストールできるNuGetがリリースされた。いわゆるパッケージ管理システムだ。Visual Studioに統合されていて、どのバージョンでも使用できるようだ(ちなみに今回はVisual Studio 2010 Ultimateを使用している)。NuGetのインストール方法は簡単だ。下記のリンクからNuGet.Tools.vsixをダウンロードしてダブルクリックするだけだ。

NuGet
サイト右側のDownloadリンクから取得しよう。
※NuGetはいくつか問題が報告されていて私もはじめは使用できなかったので対処法を後述する。
Known Issues With NuGet CTP 1

NuGetを使うと何が便利かというと、ライブラリのインストール(設定を含む)をすべて自動で行ってくれることだ。以前当ブログでも解説したElmahの設定なども自動で設定してくれて、すぐに使用できるようになる。また、複雑な依存関係があるようなNHibernate.Linqなどもすべて自動で設定してくれる。さらにNuGetの素晴らしいところはシステムレベルでは何もインストールしないので環境が汚染されることを心配しなくてよい。

では実際にNuGetを使用してみよう。NuGetの説明ではElmahが引っ張りだこなので同じ内容をやるのは気が引けるが、以前マニュアルでの導入方法を解説したこともあるのでここでもElmahの設定方法を解説する。

インストール後にVisual Studioを起動して適当にASP.NET MVCのプロジェクトを作成し、View → Other Windowを開くとPackage Manager Consoleがあるので選択しよう。ちなみに下図でPackage Manager Consoleの項目が二つあるのも既知のバグだ。ここではASP.NET MVCのプロジェクトを使用しているが、もちろんASP.NETのプロジェクトでも問題ないし、WPF、Silverlightなど、どのようなプロジェクトでもNuGetは使用可能だ。


Package Manager ConsoleでList-Packageと入力すると下図のリストが一覧できる。これはNuGetを使用して自動でインストールできるライブラリの一覧だ。


ついでAdd-Package elmahと入力する。これでインストール完了だ。いたって簡単。ReferencesにElmahが追加されているのが分かると思う。


またWeb.configにも必要な設定が追加されている。以前解説したのとは違い、ここではIn-Memoryにエラーをログするようになっている。


http://localhost:2430/elmah.axdにアクセスすると下図のElmahのエラーログページが表示されるはずだ。


以上コマンドラインでのインストール方法を解説したが、GUIで行う方法もある。Referencesを右クリックしてAdd Package Referenceを選択しよう。


Visual Studio 2010で馴染み深い下図のダイアログがポップアップするので、目当てのライブラリを検索する。


ライブラリが見つかったらInstallボタンをクリックしよう。



と、簡単にライブラリがインストールできるのが分かったと思うので、気になるライブラリがあったらガンガン導入して評価してみると良いだろう。

ここから既知のバグの対処法をいくつか紹介する。

・NuGetがアンインストールできない
管理者権限でVisual Studioを実行しないとTools → Extension Managerで表示されるダイアログのNuGetの項目にあるUninstallボタンが無効化されているので注意しよう。

・View → Other WindowsにPackage Manager Consoleがない
・Package Manager Consoleを実行するとSystem.Security.AccessControl.ObjectSecurityExceptionが表示される
どうやらReflectorのAddinをインストールしてあるとこれらの現象が発生するようだ。下記リンクを参考にReflectorのアドインを削除しよう。
方法: アドインを非アクティブにして削除する

・View → Other WindowsにPackage Manager Consoleが2つある
NuGetをインストールする以前にPower Shellをインストールしてあるとこうなってしまうようだ。ちなみにこれの解決方法はないそうだ。

2010年10月3日日曜日

MVVMパターンでSilverlightアプリを開発する その2

前回MVVMパターンと、それを実装するためのライブラリのMVVM Light Toolkitを紹介した。今回はソースコードを交えつつ解説したいと思う。

今回使用するソースコードは次の場所に公開している。



実行後の画面は下図の通りだ。



リストがあって、詳細ボタンをクリックすると詳細が表示されるといういたって単純な構成にしてある。

プロジェクトの構成は下図の通り。


MainView.xaml
MainView.xamlにFrameが定義してあり、状況によってViewsフォルダ配下のDetailsPage.xamlとListPage.xamlを表示する。MainPage.xamlで注目してほしい箇所は2箇所ある。まずUserControlタグのDataContext属性にキー名LocatorというオブジェクトのMainというプロパティがBindingされている。LocatorはViewModelのインスタンスを管理するViewModelLocatorクラスで、App.xamlでインスタンス化されている。

ViewModelLocator.csは下記の通り。

public class ViewModelLocator{
 public static MainViewModel MainStatic
 {
    get
    {
        if (_main == null)
        {
            CreateMain();
        }

        return _main;
    }
 }

 public MainViewModel Main
 {
    get
    {
        return MainStatic;
    }
 }

 public static void ClearMain()
 {
    _main.Cleanup();
    _main = null;
 }

 public static void CreateMain()
 {
    if (_main == null)
    {
        _main = new MainViewModel();
    }
 }  

 // 以下、他のViewModel用のPropertyが続く
}

ViewModelごとに上記の記述が連なっていく。

ついで、MainPage.xamlで定義したFrameのSourceにCurrentPageがBindingされている。CurrentPageプロパティが定義されているのはMainViewModelクラスになる。MainViewModel.csのコードは下記になる。

public class MainViewModel : ViewModelBase
{
 public MainViewModel()
 {
    CurrentPage = new Uri("/Views/ListPage.xaml", UriKind.Relative);
    Messenger.Default.Register(this, x =>
    {
        switch(x.Notification)
        {
            case Notifications.ToList:
                CurrentPage = new Uri("/Views/ListPage.xaml", UriKind.Relative);
                break;
            case Notifications.ToDetails:
                CurrentPage = new Uri("/Views/DetailsPage.xaml", UriKind.Relative);
                break;
        }
    }); 
 }

 Uri _currentPage;
 public Uri CurrentPage {
    get { return _currentPage; }
    set
    {
        _currentPage = value;
        base.RaisePropertyChanged("CurrentPage");
    }
 }
}

MainViewModelはCurrentPageを変更することによりページの遷移の制御を行っている。MessengerはMVVM Light Toolkitが提供してくれる通信用のクラスだ。Messengerはデフォルトで1つのインスタンスを提供してくれるので、ViewModelのBroadCastのテスト用途などで指定のMessengerを使いたい場合など以外はMessenger.Defaultプロパティを使えば良いだろう。Register関数でライブラリ標準のNotificationMessageを受け取るように指定している。今回はNotificationMessageに遷移先を渡すようにしているので、Registerを呼び出した際のコールバック内でNotificationを参照してCurrentPageを変更する。

NotificationMessage.Notificationは文字列だが、NotificationMessageにはSenderとTargetも渡せるようになっているので、Registerハンドラーの中でNotificationの文字列比較だけでなく、Sender, Targetも使用してどこに対してのメッセージなのかを識別するようにしたほうが良いだろう。というのも、MessengerのSendとRegisterはGenericを使用してNotificationMessage以外にも任意の型を指定して簡単にメッセージを発信、受信することも可能だが、実際、データ型を指定してしまうとコードからどこの誰に対するメッセージなのかまったく識別できなくなってしまうし、また、メッセージ毎にメッセージ用のクラスを用意すると、それはそれで作りすぎるとかなりカオスな状態を引き起こしてしまう。そのため、Messengerの利用はプロジェクトの規模が大きくなればなるほど計画的に使用するべきだろう。

標準のメッセージクラスは以下の通り。
MessageBase
GenericMessage<T>
NotificationMessage
NotificationMessage<T>
NotificationMessageAction
NotificationMessageAction<T>
DialogMessage
PropertyChangedMessage<T>


ListPage.xaml
ListPage.xamlもMainPage.xamlと同様にDataContextへViewModelLocatorを介してListPageViewModelクラスのBindingを行っている。リストの表示に選択機能は必要ないのでItemsControlでListPageViewModelのBushiListプロパティを単純に表示している。

ListPage.xamlは下記の通り。

<ItemsControl ItemsSource="{Binding BushiList}" >
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <StackPanel Orientation="Horizontal"
                            Margin="10">
                    <Button Content="詳細"
                            Margin="0 0 10 0"
                            CommandParameter="{Binding}">
                        <local:BindingHelper.Binding>
                            <local:RelativeSourceBinding Path="ShowDetailsCommand" TargetProperty="Command" />
                        </local:BindingHelper.Binding>
                    </Button>
                    <TextBlock Text="{Binding Name}" 
                               VerticalAlignment="Center"/>
                </StackPanel>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
</ItemsControl>

ListPage.xaml.csは下記の通り。

public class ListPageViewModel : ViewModelBase
{
 public ListPageViewModel()
 {
    if (IsInDesignMode)
    {
        BushiList = new ObservableCollection<Bushi> { 
            new Bushi{ Name = "Sample1", Age = 10, Address = "Tokyo" },
            new Bushi{ Name = "Sample2", Age = 20, Address = "Kyoto" },
            new Bushi{ Name = "Sample3", Age = 30, Address = "Osaka" }
        };
    }
    else
    {
        BushiList = new ObservableCollection<Bushi> { 
            new Bushi{ Name = "織田 信長", Age = 49, Address = "Kyo-" },
            new Bushi{ Name = "羽柴 秀吉", Age = 46, Address = "Himeji" },
            new Bushi{ Name = "徳川 家康", Age = 40, Address = "Mikawa" },
            new Bushi{ Name = "明智 光秀", Age = 55, Address = "Tanpa" }
        };
    }

    ShowDetailsCommand = new RelayCommand<Bushi>(x =>
    {
        Messenger.Default.Send<Bushi>(x);
        Messenger.Default.Send<NotificationMessage>(new NotificationMessage(Notifications.ToDetails));
    });
 }

 public ObservableCollection<Bushi> BushiList { get; set; }
 public RelayCommand<Bushi> ShowDetailsCommand { get; private set; }
}

BushiListは名前の通りBushiクラスのリストだ。それをItemsControlのItemsSourceとしている。注目して欲しいのはDataTemplate内のButtonの定義だ。実はSilverlight4からButtonBaseクラスはCommandを実装するようになったので、ButtonBaseから派生しているButtonは本来ならばCommand="{Binding ShowDetailsCommand}"といったような記述が可能なのだが、今回はItensControlのDataTemplate内ということで、個々のアイテムのDataContextはBushiになってしまう。そのため単純に前述の定義を行ってもListPageViewModel内で定義されたShowDetailsCommandプロパティは見つからない。SL4ではRelativeSourceがWPFほど強力ではないので、それを補うため今回は下記のブログポストで見つけたBindingHelperクラスを使用している。

RelativeSource Binding with FindAncestor mode in Silverlight

CommandParameterにBindingのみを指定することで個々のアイテムのDataContextであるBushiを渡している。これを受けるのはPateListViewModelのShowDetailsCommandだ。ShowDetailsCommandはRelayCommandのGenericを使用してBushiクラスを受け取るようになっている。ShowDetailsCommand内で、BushiをMessengerでDetailsPageViewModelクラスへと発信している(実際にはTargetを設定していないのでBushiをRegisterしている対象へとばらまいている)。ついでNotificationMessageも発信して詳細ページへの遷移を促している。

一点注目して欲しいのがIsInDesignModeだ。このフラグを使用してデザインモード時に仮のデータを渡してやることにより、アプリケーションを実際に動作させてWCF経由などでデータを取得、画面に表示させなくともExpression BlendやVisual StudioのXamlビューで実際に動作させた場合と同等の画面確認が行えるようになっている。これを容易に可能にするのがMVVMパターンを使うことによってもたらされる恩恵の一つでもあり、Expression Blendとの親和性が高いことからBlendabilityが高いとも表現される。


DetailsPage.xaml
DetailsPage.xamlはGridタグでListPage.xamlで選択されたBushiの詳細を受け取り表示するだけである。また戻るボタン押下時にListPage.xamlへの遷移をNotificationMessageを発信することによりMainViewModelへと通知している。DetailsPage.xaml, DetailsPageViewModel.csの詳細はここまで説明してきたこととほぼ同じなので省略する。


まとめ
前回と今回で、MVVMパターン開発手法のMVVM Light Toolkit版を解説してきた。MVVM Light Toolkitは大きなライブラリではないけれど、MVVMパターンに必要な要素はすべて盛り込まれているので、MVVM Light Toolkitを使えばすぐにでもMVVMパターンで開発を行えるのが理解できたと思うし、容易に使える実感を持てたのではないだろうか。

この解説を通して、MVVMパターンを使用してUIとビジネスロジックの切り離しを明確にすることにより、ビジネスロジックの単体テストが容易になり、それにあわせてアプリケーション全体の保守性もあがり、またデザイナとの共同作業を行う上で重要なExpression Blendとの親和性も高まる、ということが伝わったら幸いだ。