手始めに Silverlight+WCFの組み合わせで実験

手始めに Silverlight+WCFの組み合わせで実験

Silverlight+PHP(NuSOAP)の組み合わせを試す前に、Silverlight+WCFを試さないといけない。

ので、試してみた結果をメモ書き。

■最初はWindowsフォーム+WCFで

この手のお試しは確実なところからやっていくのが良いので、手堅くWindowsフォームからWCFを使えるかどうかの実験からスタートします。

1.WCFサービスライブラリのプロジェクト作成

2.初期状態で「Service1」と「IService1」が作成されるので名前を変える。
 ここでは「SampleService」、「ISampleService」と変更。
 名前の置換は、Visual Studio 2008のリファクタリングの機能を使うと楽。

3.インターフェース(ISampleService.cs)はこんな感じ

 
    [ServiceContract]
    public interface ISampleService
    {
        [OperationContract]
        string GetData(int value);
        [OperationContract]
        CompositeType GetDataUsingDataContract(CompositeType composite);

        // タスク: ここにサービス操作を追加します。
        [OperationContract]
        Product GetProduct(int id);
        [OperationContract]
        IList<Product> GetAllProducts();
    }

・サービスを追加するときは「OperationContract」属性を付ければOK。
・取得する場合は、戻り値にする(refが使えるかどうかは不明)
・リストで取りたいときはコレクションを戻り値にする。

ここで Product というクラス構造を受け渡しているので、定義する。

 
    [DataContract]
    public class Product
    {
        private int _id;
        private string _name;
        private decimal _price;

        [DataMember]
        public int ID
        {
            get { return _id; }
            set { _id = value; }
        }
        [DataMember]
        public string Name
        {
            get { return _name; }
            set { _name = value; }
        }
        [DataMember]
        public decimal Price
        {
            get { return _price; }
            set { _price = value; }
        }
    }

・クラスは DataContract 属性を付ける。
・プロパティは DataMember 属性を付ける。

# 実は、Silverlight の DataGrid で扱いやすいように ObservableCollection を使う予定なのだが、
# 今回は試しなのでこのまま。

これでインターフェースはできたので、サービスメソッドの実装に入る。

4.サービスの実装(SampleService.svc.cs)を行う。
 先のインターフェースに従ってメソッドの内容を書く。

 
        IList<Product> m_products;

        public SampleService()
        {
            m_products = new List<Product>();
            m_products.Add( new Product() { ID=1, Name="Visual Studio 20X0", Price=40000 });
            m_products.Add( new Product() { ID=2, Name="SQL Server 20X0", Price=50000 });
            m_products.Add( new Product() { ID=3, Name="Expression Blend X", Price=60000 });
        }
        public Product GetProduct(int id)
        {
            var q = from p in m_products where p.ID == id select p;
            if ( q.Count<Product>() == 0 )
            {
                return null;
            }
            else
            {
                return q.First<Product>();
            }
        }

        public IList<Product> GetAllProducts()
        {
            return m_products;
        }

・本来はデータベースから拾ってくるのだが、今回は内部にデータを持ったままテスト。
・GetProduct メソッドは id を渡すと Product オブジェクトを返す。
・GetAllProducts メソッドはコレクションを返す。

大抵のパターンはこれだけで OK。
さて、普通はこれで十分なのだろうが、環境が悪いのか、そのままだと Windows フォームで参照するときに「ASP.NET互換ではない」というエラーが出る。よくわからないが、↓のように AspNetCompatibilityRequirements を付けると通るようになる。

 
namespace SampleSL2WCF
{
    [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
    public class SampleService : ISampleService
    {
        ...
    }
}

■Windowsフォームの場合

WindowsフォームからWCFを呼び出すときは、

1.サービス参照で、先のWCFサービスを参照する。
 名前空間を「SampleServiceReference」と指定した。

2.この状態でバインドができているので、サービスの呼び出しは簡単。

 
 // サービスのクライアントを作成
    SampleServiceClient client = new SampleServiceClient();
    // オープン
    client.Open();
    // メソッド呼び出し
    Product pro = client.GetProduct(id);
    // クローズ
    client.Close();

3.先ほど、使ったインターフェース(ISampleService)のメソッドを使う。

 
using ClientWindowForm.SampleServiceReference;

namespace ClientWindowForm
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        // IDを指定して検索
        private void button1_Click(object sender, EventArgs e)
        {
            int id = int.Parse(textBox1.Text);
            // サービス呼び出し
            SampleServiceClient client = new SampleServiceClient();
            client.Open();
            Product pro = client.GetProduct(id);
            client.Close();
            // データグリッドに表示1
            List<Product> products = new List<Product>();
            products.Add(pro);
            this.dataGridView1.DataSource = products;
        }
        // すべてのデータを取得
        private void button2_Click(object sender, EventArgs e)
        {
            // サービス呼び出し
            SampleServiceClient client = new SampleServiceClient();
            client.Open();
            IList<Product> products = client.GetAllProducts();
            client.Close();
            // データグリッドに表示1
            this.dataGridView1.DataSource = products;
        }
    }
}

これをビルドして実行すると、Windowsフォーム+WCFのできあがり。

■次はWPF+WCFの組み合わせ

SilverlightはWPFのサブセットなので、WPFの文法を覚えておくとSilverlightでも応用が利く。
が、サブセットゆえに、違い/落とし穴があるのが難点。
違い/落とし穴を示すために WPF+WCF でも動作させてみる。

1.Windowsフォームと同様にサービス参照でインタフェースを作る。
2.WPFにデータグリッドを付けたウィンドウを作る。

<Window x:Class="ClientWPF.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1" Height="310" Width="517">
    <Grid>
        <Button Height="23" HorizontalAlignment="Left" Margin="12,12,0,0" Name="button1" VerticalAlignment="Top" Width="75" Click="button1_Click">ID指定</Button>
        <Button HorizontalAlignment="Left" Margin="12,0,0,178" Name="button2" Width="75" Height="23" VerticalAlignment="Bottom" Click="button2_Click">すべて</Button>
        <TextBox Margin="12,41,0,0" Name="textBox1" Height="24" HorizontalAlignment="Left" VerticalAlignment="Top" Width="75">2</TextBox>
        <my:DataGrid AutoGenerateColumns="True" Margin="93,12,12,12" Name="dataGrid1" xmlns:my="clr-namespace:Microsoft.Windows.Controls;assembly=WPFToolkit" />
    </Grid>
</Window>

※DataGridのAutoGenerateColumns属性が True になっているのは、初期値の False だとグリッドが正常に表示されないため。
 おそらくデータのコレクションが間違っている(IListじゃだめ?)からだと思うのだが。

 

3.ボタンイベントを記述する。

記述の仕方は、Windows フォームとほぼ同じ。
データグリッドにバインドするときは「ItemsSourceプロパティ」を使う。

 
using ClientWPF.SampleServiceReference;

namespace ClientWPF
{
    /// <summary>
    /// Window1.xaml の相互作用ロジック
    /// </summary>
    public partial class Window1 : Window
    {
        public Window1()
        {
            InitializeComponent();
        }

  // IDを指定して検索
        private void button1_Click(object sender, RoutedEventArgs e)
        {
            int id = int.Parse(textBox1.Text);

            SampleServiceClient client = new SampleServiceClient();
            client.Open();
            Product pro = client.GetProduct(id);
            client.Close();

            List<Product> products = new List<Product>();
            products.Add(pro);
            this.dataGrid1.ItemsSource = products;
        }

  // すべてのデータを取得
        private void button2_Click(object sender, RoutedEventArgs e)
        {
            SampleServiceClient client = new SampleServiceClient();
            client.Open();
            IList<Product> products = client.GetAllProducts();
            client.Close();
            this.dataGrid1.ItemsSource = products;

        }
    }
}

これをビルドして動かすと問題なく動作する。OK。

 

■いよいよ、Silverlight+WCFの組み合わせを試す

1.Windowsフォームと同様にサービス参照でインタフェースを作る。

 ※このときに、WCFのデフォルトのバインディング(binding)が「wsHttpBinding」のままだと通らない。
 ※ので、basicHttpBinding に変更する。
 ※Silverlight 2 の場合は「basicHttpBinding」のみ有効、という記述があるのだが、Silverlight 3 の記述はない。
 ※どれが正しいのか不明。

WCFのweb.configを以下のように書きかえる。

  <system.serviceModel>
    <services>
      <service behaviorConfiguration="SampleSL2WCF.Service1Behavior" name="SampleSL2WCF.SampleService">
<!--        <endpoint address="" binding="wsHttpBinding" contract="SampleSL2WCF.ISampleService"> -->
          <endpoint address="" binding="basicHttpBinding" contract="SampleSL2WCF.ISampleService">
            <identity>
            <dns value="localhost" />
          </identity>
        </endpoint>
        <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior name="SampleSL2WCF.Service1Behavior">
          <!-- メタデータ情報の開示を避けるには、展開する前に、下の値を false に設定し、上のメタデータのエンドポイントを削除します -->
          <serviceMetadata httpGetEnabled="true" />
          <!-- デバッグ目的で障害発生時の例外の詳細を受け取るには、下の値を true に設定します。例外情報の開示を避けるには、展開する前に false に設定します -->
          <serviceDebug includeExceptionDetailInFaults="false" />
        </behavior>
      </serviceBehaviors>
    </behaviors>
  <serviceHostingEnvironment aspNetCompatibilityEnabled="true" /></system.serviceModel>
</configuration>

# まぁ、考えてみれば、インターネット越しで Windows 認証を操ることはほぼ無いので、
# ベーシック認証にしておく(認証なし)にするのが良かろう。

2.Silverlightにデータグリッドを付けたウィンドウを作る。

このあたりは、WPF とほぼ同じ。

<UserControl
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:data="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data" x:Class="ClientSilverlight.Page"
    Width="400" Height="300">
    <Grid x:Name="LayoutRoot" Background="White">
     <Button x:Name="button1" HorizontalAlignment="Left" Margin="8,8,0,0" VerticalAlignment="Top" Width="75" Content="ID指定" Click="button1_Click"/>
     <TextBox x:Name="textBox1" HorizontalAlignment="Left" Margin="9,34,0,0" VerticalAlignment="Top" Width="74" Text="2" TextWrapping="Wrap" />
     <Button x:Name="button2" HorizontalAlignment="Left" Margin="9,62,0,0" VerticalAlignment="Top" Width="75" Content="すべて" Click="button2_Click"/>
     <data:DataGrid Margin="88,8,8,8" Name="dataGrid1"/>
    </Grid>
</UserControl>

3.ボタンのイベントを記述する。

ここが、WindowsフォームやWPFと大きく異なる。

using ClientSilverlight.SampleServiceReference;

namespace ClientSilverlight
{
    public partial class Page : UserControl
    {
        public Page()
        {
            InitializeComponent();
        }
        // IDで検索
        private void button1_Click(object sender, RoutedEventArgs e)
        {
            int id = int.Parse(textBox1.Text);
            // サービスの作成
            SampleServiceClient client = new SampleServiceClient();
            client.GetProductCompleted += new EventHandler<GetProductCompletedEventArgs>(client_GetProductCompleted);
            client.GetProductAsync(id);

        }
        // 実行後のコールバック
        void client_GetProductCompleted(object sender, GetProductCompletedEventArgs e)
        {
            Product pro = e.Result;
            List<Product> products = new List<Product>();
            products.Add(pro);
            this.dataGrid1.ItemsSource = products;
        }
        // すべてを取得
        private void button2_Click(object sender, RoutedEventArgs e)
        {
            SampleServiceClient client = new SampleServiceClient();
            client.GetAllProductsCompleted += new EventHandler<GetAllProductsCompletedEventArgs>(client_GetAllProductsCompleted);
            client.GetAllProductsAsync();
        }
        // 実行後のコールバック
        void client_GetAllProductsCompleted(object sender, GetAllProductsCompletedEventArgs e)
        {
            IList<Product> products = e.Result;
            this.dataGrid1.ItemsSource = products;
        }
    }
}

コードを見ると分かる通り、
・サービスの呼び出し
・サービスの完了通知
の2つのメソッドが必要になる。
WCFを非同期で呼び出すために完了を待つ仕組みなのだが、、、これが結構うざったい。

ひとつのメソッドにまとめるならば、ラムダ式を使えばよいのだが、、、

        private void button3_Click(object sender, RoutedEventArgs e)
        {
            int id = int.Parse(textBox1.Text);

            SampleServiceClient client = new SampleServiceClient();
            client.GetProductCompleted += new EventHandler<GetProductCompletedEventArgs>(
                (s,ee) => {
                    Product pro = ee.Result;
                    List<Product> products = new List<Product>();
                    products.Add(pro);
                    this.dataGrid1.ItemsSource = products;
                });
            client.GetProductAsync(id);
        }

これは根本的な解決にはなっていない。

なぜならば、SilverlightでWCFを扱うときは非同期呼び出ししかないので「完了待ちをしている間、ユーザが何かアクションを起こさないようにブロックする必要がある」ということだ。これは面倒。
「SilverlightはWebアプリだから非同期であって~」の説明をちらほら見掛けるが、騙されてはいけない。WindowsフォームやWPFの場合は同じWCFを使っているのに、同期メソッドとして扱っている。これは、SiverlightがWebアプリかどうかという話ではなくて、Silverlightが扱うWCFのサブセットに「同期メソッドが抜けている」と言ったほうが良い。何故、抜けているのかは不明。

さて、これをビルドして動かそうとすると

「ドメイン間のポリシーが見つかりません」

と言われて動かない。

Silverlight 2 と WCF を使用したサービス駆動型アプリケーション
http://msdn.microsoft.com/ja-jp/magazine/cc794260.aspx

いわゆるクロスドメインの問題なので、WCFを公開するときに ClientAccessPolicy.xml というファイルを用意する。
# クロスドメインのブロックは、サーバーで行われるのではなくて Silverlight 側で自主的に行っている。
# つまり、、、今後「自主的に行わなくなる」という方向も考えられる。これは問題あり。

このファイルなのだが、ドメインのルートに置かないといけない。

たとえば、http://moonmile.net/ClientAccessPolicy.xml のように置く。

つまり、

http://moonmile.net/~masuda/
http://moonmile.net/masuda/

のようなレンタルホストの場合は Silverlight で使う WCF は公開できないということになる(レンタルサーバー会社で ClientAccessPolicy.xml を置けば別だが)。
また、他のサイトにあるサービスも簡単には使えない。先のエントリでも書いたが、別途プロキシを作る必要がある。
一番制限の緩い(いけない)設定はこんな感じになる。

ClientAccessPolicy.xml

<?xml version="1.0" encoding="utf-8"?>
<access-policy>
   <cross-domain-access>
       <policy>
           <allow-from http-request-headers="SOAPAction">
               <domain uri="*"/>
           </allow-from>
           <grant-to>
               <resource path="/" include-subpaths="true" />
           </grant-to>
       </policy>
   </cross-domain-access>
</access-policy>

SOAPActionのヘッダに対して、どこのドメインからでもアクセスが可能。
サービスを提供するフォルダも自由(かな?)。
というわけで、Visual Studio上でWCFを公開するとポートは変わるし、先のポリシーファイルを置かないと駄目だし、ということで面倒なので、IIS 7.0 の仮想フォルダを使って設定する。

すると、OK。うまく SilverlightからWCFに接続ができる。

■パケットを見る

Siverlight+PHPの組み合わせを作るためにパケットを覗いてみよう。

Microsoft Network Monitor 3.3
http://www.microsoft.com/downloads/details.aspx?FamilyID=983b941d-06cb-4658-b7f6-3088333d062f&displaylang=en

を使うとパケットが見れる。

microsoftのサイトでダウンロードできるのは英語版のみだが、日本語化もできるそうだ。

Microsoft Network Monitor 3.3 の日本語化
http://d.hatena.ne.jp/wwwcfe/20090824/microsoftnetworkmonitor
Microsoft Network Monitor 3.3 日本語化パッチ
http://applications.web.fc2.com/j10n/networkmonitor.html
フィルタに

tcp.Port == 80

を設定してパケットをキャプチャする。

POST /SampleSL2WCF/SampleService.svc HTTP/1.1
Accept: */*
Content-Length: 157
Content-Type: text/xml; charset=utf-8
SOAPAction: http://tempuri.org/ISampleService/GetProduct
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; Trident/4.0; SLCC1; .NET CLR 2.0.50727; Media Center PC 5.0; .NET CLR 3.5.30729; .NET CLR 3.0.30618)
Host: iomante-pc
Connection: Keep-Alive
Cache-Control: no-cache

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" >
 <s:Body>
  <GetProduct xmlns="http://tempuri.org/">
   <id>2</id>
  </GetProduct>
 </s:Body>
</s:Envelope>

 

HTTP/1.1 200 OK
Cache-Control: private
Content-Type: text/xml; charset=utf-8
Server: Microsoft-IIS/7.0
X-AspNet-Version: 2.0.50727
X-Powered-By: ASP.NET
Date: Fri, 16 Oct 2009 06:55:12 GMT
Content-Length: 385

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
 <s:Body>
  <GetProductResponse xmlns="http://tempuri.org/">
   <GetProductResult xmlns:a="http://schemas.datacontract.org/2004/07/SampleSL2WCF" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
    <a:ID>2</a:ID>
    <a:Name>SQL Server 20X0</a:Name>
    <a:Price>50000</a:Price>
   </GetProductResult>
  </GetProductResponse>
 </s:Body>
</s:Envelope>

中身が読みやすい SOAP になっているのが分かる(実際は xml の部分は1行で書かれている)。

なので、Siverlight+PHPを作る場合は、

・サービス参照をするための WSDL を作成
・SOAP で応答を返す

だけで良いので、NuSOAP が結構使えるのではないか、と期待できる。

続きは後日。

カテゴリー: 開発 | 手始めに Silverlight+WCFの組み合わせで実験 はコメントを受け付けていません

CentOSでLAMP、そしてNuSOAP+Silverlightは可能か?

クライアントが Silverlight な場合、バックエンド(サーバー側)はIIS 7.0を使うので、自然サーバーとの通信は WCF を使うことになります。。。っていうか、Microsoft の一番押しらしいんですが、MCF を使う場合には IIS 7.0 が必須なんですかね?

予算がある場合は、SQL Server 2008を買って、SQL Server 2005/2008を買って、IIS 7.0をインストールし、その上で、Silverlight を Visual Studio 2008 で開発して、デザインは Blend 3 を使って、ということが可能なのですが。
あまり予算が無い時はそうはいきません。つーか、そんなに予算をつぎ込めません!

なので、自然とLAMPの組み合わせを使うのが普通なのですが、ブラウザベースでリッチクライアント(俗にいうRIA)を作る場合は、Flash が第一の選択肢になります。あいにく Flex は触ったことがないので、どこまで作りやすいのか/無料なのかが不明なのですが、言語として Java を使う必要があるのが難点。

まぁ、そういうわけで、

・クライアントに Silverlight
・サーバーに PHP + MySQL

というのはどうでしょう?

あいにく Flash で本格的なアプリを作ったことがないので(使ったことはありますが)、想像でしかないのですが、開発時の再利用は結構大変なのではないかと。そういう場合は、Flex を使う訳ですが。

さて、Flex を使うとサーバーに Java が必要ですが、Silverlight を使っても ASP.NET を必要としません。DLL だけを置いてダウンロードできるようにすればOKです。そういう点では、Flash と同等の配置の手軽さが取れます。
Silverlight では開発言語は .NET を使えるので、クラスライブラリ等を使い再利用が可能です。NUnitやSilverlightのUnitTestも使えるので、かなり頑健なものができそうです。

で、問題は、サーバーからデータを持ってくるときは何を使うのか?ですね。

まぁ、サーバーがPHPなので、XMLサービスでも良いし、独自のWEBサービスを使ってもよい。が、基本はWSDLを公開しておけば、それを参照してクライアントのクラスを自動作成できる。

連載 Webサービスのキホン(4)
WSDL:Webサービスのインターフェイス情報
http://www.atmarkit.co.jp/fxml/tanpatsu/21websvc/websvc04.html
Windows Communication Foundation概説
WCFの基本的な概念
http://www.atmarkit.co.jp/fdotnet/wcf/wcf02/wcf02_02.html

なので、基本的なプロトコル?をサポートしてとけば、相手がPHPでも良い訳です。
PHP の場合、SOAP を扱うための NuSOAP が老舗なのでこれを使います。
詳しい実験は後日やるとして、方針をざっと書き出すと。

1.クライアントアプリを Silverlight で作る。
2.サーバーを CentOS の LAMP 環境で作る。
3.PHP の NuSOAP を使ってサービスを WSDL で公開する。
4.永続化等は MySQL を使う。
5.公開済みの WSDL を Silverlight 側でサービス参照する。
6.通信は WCF に準じる?
ただし、いくつか問題があります。

・Silvelight はクロスサイトドメインアクセスの問題があって、他のドメインにサービスを繋げないようになっている。
・NuSOAPを使って WCF 準拠ができるのか不明
・PHPのパフォーマンスの問題

基本は巷のレンタルサーバーのPHPを使って公開、クライアントはSilvelightで。
という一見不思議な(?)環境を整えたいので、ここらはクリアにしておきたいところです。
クロスサイトの問題は特定のhtmlを公開時に置けばよいので、まあ個人運用にはなんとかなりそうですが、既存のサービスを silverlight 側で使えないのはちょっと痛いです。
fs
Silverlightでのクロスドメインアクセス
http://d.hatena.ne.jp/coma2n/20080430/1209516432

まぁ、最初はプロキシを作る。そして、Microsoft が Siverlight + WCF を広めるのを待つ(各サービスが WCF に対応しようとする)のがベターかなと。

カテゴリー: 開発 | CentOSでLAMP、そしてNuSOAP+Silverlightは可能か? はコメントを受け付けていません

PHPとMVCモデル

<愚痴>
今回の仕事で痛感したのは、単体テスト(UnitTest)を活用してないプロジェクトは全然駄目ということですね。品質に理解があるようでいて理解/実践がないし、設計、レビューを繰り返したとしても正常に動作するあるいは作成可能であるかの保障がありません。
そうなると「SIer>下請け」ヒエラルキーの元で、割を食ってしまうのは下請けのプログラマです。単体試験の工費を貰えず、単体試験を省けば、不具合/障害の責任はプログラマにある。また、単体試験を行えばスケジュールに合わない。余計なことをやっているとみられる。時間を削られれば学習の時間(次への投資)ができなくなる。じり貧になってしまうのは、下請けの底辺プログラマばかり。
というわけで、今後そういう仕事は請けないことにします。
</愚痴>

な訳で、インストールマニアックスでPHPを少し触ったことだし、ASP.NETの開発者よりもPHPの開発者が多いわけだし、あるいはJava開発者が多いし、ということで、PHPとMVCあるいはMVVMの組み合わせはどうかな?とざっと調べてみました。

目的は、

・PHPの開発者が理解できる標準的なMVCモデル
・PHPの軽さ/手軽さを活かした開発スタイル
・基本はLAMP(Linux+Apache+MySQL+PHP)
・単体テスト/PHPUnitは必須
・自社開発の効率化/品質Up
・できれば、巷のオープンソースに組み込みやすい/流用しやすい

なところです。

PHPと他各種言語の比較記事
http://phpspot.org/blog/archives/2007/01/php_69.html
PHPUnit3で始めるユニットテスト
http://gihyo.jp/dev/feature/01/php-test/0001?page=1

PHPフレームワーク ちいたん
http://php.cheetan.net/
軽量なMVCフレームワークの自作(改訂版)
http://codezine.jp/article/detail/104

CakePHP
http://cakephp.jp/
CakePHP プログラマーズ リファレンスガイド
http://cakephp.jp/doc/

ざっと調べてみると、フレームワークとしてはCakePHPの評判が良さそうです。ただし、Ruby on Railsの影響を強く受けているみたいなので、Railsを知らない人/好きでない人にとっては、メリットが薄れます。ちなみに私は「Railsを知らない人」です。

現在、PHPを使ってWEBアプリを作るメリットとして、

・PHPの開発者は比較的多い(文法が簡単だから?)。
・LAMPのひとつとして確立されている(メジャー度)。
・レンタルサーバーではPHP、Perlが入っているところがほとんど。

なわけで、安いサーバーを使って、広く技術者を求めることが可能、かつ顧客にも説明しやすいというメリットがあります。

デメリットとしては、

・負荷の話になると、Java/.NETが有利
・データベース接続がMySQLが基本?(既存のOracleに接続するとか)
・クラスが一般に認知されていない?(PHPのクラス解説ブログ/ページは少ない?)
 よって、開発者が減る。
・Visaul StudioやEclipsのような開発環境が無い?(インテリセンスとかコンパイル時エラーとか)

なところでしょうか。
あと、うがった話で言えば「PHP開発者が大勢いるといることは、Javaや.NET、C++ほど優秀な人が少ない?」という偏見も出てきます。これはよくわかりません。モデルの会話とか、フレームワークの会話を実際にしたことがないので。なんかいい勉強会とかありませんかね?

# ちなみに Java の人と会話すると、直ぐにインターフェースとリスナーを駆使したクラス至上主義なモデルになってしまい困りものです。.net だとデリゲートがあるし、Rubyはメソッドを追加することができるし、と言語特有の特徴/有意性があるので、JavaライクなC#コードを書かれても、困るんですが。

さて、ASP.NETにもMVCフレームワークというのがリリースされていますが、何もその実装フレームワークを使わないとMVCモデルが実現できあい訳ではありません。先のエントリにも書きましたが「モデル」≠「実装」という実現の仕方でもよいのです。
特にMVCモデル(Model-Viewパターン)はViewを分離させようという発想、自動化できるUnitTestを使おうという発想が先にあるので、ここをきちんと押さえないと、ただ何かのフレームワークを使っている(お仕着せのライブラリとか)になってしまいます。

MVCフレームワークが気持ち悪くなってきた
http://d.hatena.ne.jp/ghostbass/20081126/1227673389

そういう点で私は上のエントリに同意します。

おそらくJavaのStrutsの影響を受けると、本来のMVCモデルが失われてしまうのかな?と。

少しPHP-MVCモデルと離れますが、思いつきを書いておきます。

PHPの大きな特徴は大抵のレンタルサーバーで使えることです。ですから、WEBサイトの数々の管理ツールをPHPで書いておくことは大きなメリットです。たとえ、サイト自体がJavaやASP.NETで運用されていたとしても、マスター管理の画面など管理者が使う画面をPHPで書いておけば、作成や改修にそれほど時間/工数をかけずに済みます。PHPのインストールは必要でしょうが、特定のPEARに依存せずインストールが楽になれば、別の言語であっても構わないのです。顧客向けのページをJavaやASP.NETで作成し(お金をかけ)、少数の管理レベルであればコマンドライン制御も含めて、PHPで手軽に書く(お金をかけず)、という適材適所の思想が必要です。そう、建築と同じで、どこでも檜を使えばいいってもんじゃないでしょう?

このとき、マスター管理などの画面を作るのに導入コストがかかるフレームワークは避けたほうがいいでしょう。勿論、同じ言語で同じ技術者で作りたい気持ちも分からないのではないですが、それだけ無駄なコストが掛かります。オーバースペックなわけです。

となれば、手早く作る部分には下記の要件が必要です。

・軽量なPHPのフレームワークであること。
・頑健であること。
・導入/学習コストが低いこと。
・不足を補う方法が用意されていること。

先の2つは軽量/頑健さを求め、複雑を排除します。単純であれば学習コストも減るでしょう。拡張性をトレードオフしますが、一定の複雑さを求めるのであれば、別のフレームワークを使うか、javaなりasp.netなりを使えばよいのです。

不足を補う部分を具体的に話せば、巷のオープンソースはPHPのコードをPHPから出力する自己記述を行いうまい具合に閉じていますが(これはこれでいいのですが)、何もかもこれでやるのは無理があります。
なので、設定自体をExcelで書くとか、コードの自動生成をExcelから行う(あるいはJavaのコマンドを使う)などの組み合わせがあってもいいはずです。
# 逆に何もかもExcelというのも無理があります。

こういう観点からPHPにMVCモデルを組み合わせると、いくつかの案がでてきます。
おそらく「ちいたん」がそれに近いと思います。

・VB6.0でCOMを扱うような手軽さ。
・VB6.0でCOMを扱うような頑健さ。
・中身が容易に想像できること。
・設定が一か所にまとまっていること。
・テストツールが用意されていること。

VB6.0とCOMの繋がりは非常に疎ではありますが、疎結合ゆえにメソッド/プロパティが用意に想像できる単純さを持っています(中身は複雑ですが)。
この外見の容易さは、車のハンドルを左に回すと、車が左に曲がるというアクションと動きの一致の重要さに通じます。見えない構造はどれだけ複雑でも構いませんが(実際CPUの回線は複雑だし)、活用する場面においてその複雑さを表に出してはいけません。
なので、MVCモデルを導入する際も、「中身が容易に想像でき」、インターフェースを眺めることができるように「設定を一か所にまとめる」工夫が必要です。あちこちのファイルを修正しないといけない実装は、ケアレスミスを誘発しますし、学習コストを押し上げてしまいます。

そして、忘れていけないのが、設定がきちんとできているか、その実装は思った通り動いているのかをテストするツールが必要なのです。最近、SQL Serverでは設定のチェックを行うようになりましたが、ほとんどのフレームワークは自己テストを行いません。
私がいささかスクリプト言語を敬遠するのは、コンパイル言語であればコンパイルすれば取れるエラーが、スクリプト言語の場合実行時にしか取れないことが多いためです。
# perlには文法チェック -wc というオプションがあります。ruby の場合は -c かな。

これだけ頑健にしておき、実装時のエラーが取れていれば、あとは開発者が作った独自のコードのエラーを取るだけです。UnitTest のフレームワークを活用しておけば(これが必須なのですが)、その延長線上でテストを作ることもできるでしょうし、本来 Model の UnitTest が実践できるという、MVCモデルの発端の思想に沿うものができます。

というわけで、無事 PHPとMVCモデルに戻ってきたわけですが、このあたり、実装できているMVCフレームワークは欲しいですか?

カテゴリー: 開発 | 6件のコメント

MVVMパターンの日本語訳

備忘録として。

M-V-VMパターンについてのエントリを訳してみた 原題:”WPF patterns : MVC, MVP or MVVM or…?”
http://blog.sharplab.net/computer/cprograming/wpf/1812/
M-V-VMパターンについてのエントリを訳してみた2 原題:「David Hill’s WebLog : The ViewModel Pattern」
http://blog.sharplab.net/computer/cprograming/wpf/1840/
M-V-VMパターンについてのエントリを訳してみた3 原題”CollectionViewModel”
http://blog.sharplab.net/computer/cprograming/wpf/1842/
関東地方は台風一過の秋晴れです。
その中で、台風情報を見ていたのですが、Yahoo!の雨雲ズームレーダーが Silverlight でした。

雨雲ズームレーダー
http://weather.yahoo.co.jp/weather/zoomradar/
Yahoo!天気のほうは、Adobe Flashなんですが、この手の複雑なアニメーションはデータ抽出から表示アプリの効率を考えたら Silverlight のほうが良いんではないかなぁ、と思ったり。Flex + java か Silverlight + .net の組み合わせかの選択ですね。

カテゴリー: 開発 | MVVMパターンの日本語訳 はコメントを受け付けていません

続・受託開発の罠

受託開発の罠
http://www.marenijr.net/mymy/solution/008.html

を書いたのは実は5年前です。読み直すと誤字が多いのと、意味不明な形容詞が難ですが。
で、なんとなく検索して、リンクを探してたら、

網助っ人 芥川武日記
http://blog.netsket.com/acty/2007/04/post_9.php
でリンクされていました。ここの芥川さん Netsket という会社の社長さんです。
http://www.netsket.com/

この会社の最近の売り(?)は「マッハ開発」だそうです。
詳しくは会社のページを見てほしいのですが、所謂、WEBサービスを1週間でリリースしますという商売/スタイルです。普通の開発では考えられない(こともない?)わけですが、これがこの会社の強み&差別化です。

強み/差別化の話は起業塾でもとくテーマになります。ソフトウェア業界の場合、製品開発よりも受託開発(システム一品もの)が圧倒的に多いので、どこの会社に発注するのか、どこの会社/人と継続的に繋がりを持つのか(営業の削減、取引コストの削減)が、請け負う会社/人にとっては生命線になります。最悪(でもないけど)会社の場合は営業収入的にじり貧になって畳む、ということになります(当然、最悪の場合は借金ね)。

そう、マッハ開発っぽいことの発想は、WEB開発は当然で、案を出されてから2週間でリリースというのが理想的だそうです。平均的なWEB開発が2か月以内だそうですから、相当の早期リリースを求められているわけですね。

「素早く対応しないといけない日進月歩の業界ですね」

な~んてことは私は言いません。
反感を買うであろうことを敢えて言えば(書いておけば)、

「先見の目がない学習しない業界ですね」

ってことです。所謂、単純な短納期はイコール無計画ゆえの短納期を自己言及しています。
これを流れで示せば、

1.あ~、最近うちの会社のページがパッとしないんだよね。何か集客はあるかな?
2.そういえば、○○ってのが流行っているみたいですね。
3.そうそう、○○ってのは、あの会社もやり始めたしネットでも話題だし。
4.なるほど、じゃあ○○を活用してうちの会社の××と組み合わせれば。
5.それ、いいじゃないですか!
6.でも、見積もりとか期間とかあれだから、そうするとブームが去っちゃいますよね。
7.いや、大丈夫、あそこの会社のスピード開発に頼めば!
8.そうか、じゃあ、お願いします!

ってな具合なのかな~、と妄想していします。そう妄想ですからね、妄想!

というわけで、極端な短納期は差別化の一種ではあるのですが、懸念するのは(いえ、別にネット助っ人という会社を批判している訳ではなく、一般的な市場を話をしています)、そういうコンビニみたいな100円均一みたいな作り方/会社/人生がしたいのか?ってことです。

5年前、受託開発の罠を書いたときに「ソフトウェア業界=ゼネコン」と気づいた時から、建設業界の比較を考えてきました(ずっとではないけど)。5年前から比べると、株価は暴落したし、不況は更にひどくなっているし、ソフトウェア開発者の単価は下がりっぱなしだし、当時そこまでは予想していなかったけど「罠」に嵌ると抜け出せない会社が多々ありそうな雰囲気です。

先の話を書いたときには、顧客>SIer>下請のヒエラルキーから抜け出す方法は、このシステムから抜け出す方法以外にない、とは分かっていました。ですが、具体的に何をすれば抜け出せるのかはよく分かりませんでした。

という訳で、前置きが長くなりましたが、久々の続編ということで、具体的な解決策をいくつか書いておきます。

■製品を作る

第一に思いつくことが、顧客に直結するために製品を作ることです。これは「顧客>SIer>下請」の SIer の項を抜き去り「顧客>自社」とするシステムを確立します(SIerになるのではない!ので注意)。

他業種で言えば、小売業、タクシー、ホテルなどのサービス業種です。顧客(お金を払う人)と直結して取引を行い自社のアイデア/製品を提供し続けます。
小売を目指すのであれば、シェアウェアの提供、iPhoneなどのアプリが思いつきますが、会社として経営するのは難しい面があります(秀丸は例外でしょう)。現状、小売業のように一般人を相手する場合、MS-Officeやウィルス対策ソフトのようなパッケージが一般的です。これらのパッケージソフトは大手の会社が配布している場合が多いので、小さな会社は手がだし辛いでしょう。だいたいパッケージソフト=製品とは言え、自社のみで作ることは少なく、先のSIerが製品のアイデア/提案をし、下請けが作るという仕組みが確立されているので、自社がSIerになるしかない、というジレンマがあります。

ですので、ホテルのようなサービス業種を真似し、顧客を特定の分野に絞っていきます。たとえば、商店街の店であったり、小規模の工場であったり、小規模の官庁であったりします。
この場合、それぞれの顧客について受けいられる予算は小さいものですから、受託開発で行うような一品ものの製品を作っては割が合いません。ひとつの製品を複数の顧客(多数ではないが1つではない)に売るなり、ひとつの製品を自社で手軽に/手早くカスタマイズして顧客に提供する、という方針になります。

手軽に/手早くというのが、会社の暗黙知/資産になります。ノウハウとも言いますね。どちらかと言えば、このノウハウは人あるいはチームに属すると良い面があります。

このあたり、具体例を出せば、

・自社のフレームワークを使うと、開発が効率化できる。
・自社のフレームワークを使うと、超短納期でできる。
・コア技術を持ち、それに派生した製品を作成する。
・コア技術を持ち、それと顧客を組み合わせた製品を作成する。
・自社のチームを使うと、一定分野に特化した開発ができる。

一見、受託開発でも成り立つ論理に見えますが、営業を行わない(こともないけど)受託開発の場合には開発の効率化/短納期がイコール受注額の減少になり、逆効果になります。
当たり前ですが、中間に位置するSIerの取り分を多くするためには、2つの方法がありますが、下請けへの発注額を減らすほうが楽なのです。幸いにして(?)受託専門の会社はたくさんありますので、この不況下金額を下げる価格競争が「競争」になってしまい、下請け/自社の取り分は減ってしまい、単なる生き残り合戦になってしまいます。
しかし、顧客と直結した場合、受注額を減らしたとしても、それ以上の効率化を行う(あるいは売り手として受注額を計算する)ことにより、経験/学習/努力の成果は時間が余るという効果を出し、先行きへの学習/製品作りの時間を取ることが可能になります。
そういう会社/人の成長過程を踏むためにも、顧客>自社の直結が必要であり、なんらかの「製品」を持つことがソフトウェア業界でも必須になります。

■取引コストを下げる

取引コストというのは「何故コンビニで98円のジュースを買わず、コンビニの前にある自動販売機で120円のジュースを買うのか」という話になります。
人が何かモノを買うときには、無意識にいくつかのコストを試算しています。

・ひとつはモノの価格としてのコスト
・ひとつは差別化/付加価値としてのコスト
・ひとつは物珍しさ/驚きとしてのコスト
・ひとつは買うときに交渉する取引コスト

などです。正確には5つあるはずなのですが、ここでは4番目の交渉/取引コストを問題にします。取引コストは企業では営業職の人です。端的に言えば、モノを好きな時に定価で買う、好きな時に定価で売る、ことができれば営業という職種はいりません。残念ながら(?)現在の社会では需要と供給がイコールになることはまれで、そこには競争が発生します。ひとつは価格競争なのですが、もうひとつはコミュニケーションとしての競争があります。これが「営業」です。
さて、営業の厳密な定義はさておき、この営業コスト=取引コストは、価格を交渉したり、レジにジュースを持って行ったり、相手の予定を窺ったり/伺ったり、顔つなぎをしたり、という役目に発生します。だから、コンビニに入って冷蔵庫からジュースを取り出してレジに持っていくのが「うっとおしい」と感じたとき、人はコンビニの前の自動販売機でジュースを買います。
このあたり、生物経済学(だっけ?)に詳しく載っています。

というわけで、人はモノを買うときコストに関しては一見理不尽な行動を取ります。
先の「うっとおしい」という感情は、社会的に問題を抱えつつも、人数が多すぎる日本社会の一面を表しています。

これは、会社間の交渉にも言えます。
常に営業を介して、価格交渉をし続けるよりも、ある程度の幅を持った金額のやり取りを続けるだけで十分な時があります。これが、会社間の継続的な関係です。
継続的な関係は、顧客と自社だけでなく、自社とグループ会社の間でも発生します。昨今のグループ会社を作る動きは、外側への営業を一本化して、自社のあふれた分をグループ会社に廻す、同様にグループ会社のあふれた分を自社に廻して貰うという関係が前提になっています。ここでも「営業」特有の取引を減らす努力をなされています(営業機会を増やす努力でもありますが)。

この会社間の関係を長く続けると、グループ内での営業金額のやり取りは差し引きゼロになります。つまり、トータルで見て営業的なやり取りが不要ということです。つまり、取引コストがゼロになるということです。
こうなると不思議なことに、グループ内でのやり取りはあたかも定価を通じて行っているように見えます。つまり、好きなときに定価で売る、好きなときに定価で買うという状態です。

そう、ヤクルトを社内に置いたり、薬箱を家庭に置いたりするような状態と同じものが作れます。

Amazon を始めとするインターネットでの小売の増加は、当初この取引コストを減らす動きでした。しかし、インターネット自体が普及するに従って(あるいは Amazon が販路を拡大する路線に従って)、インターネットでの製品も価格コストが下げる傾向にあります。
しかし、本来ならば家で買える便利さ/品物の豊富さを見れば、さほど価格を下げる必要はないと思われます。これは(顧客にとって)不要なものを売るという、販売過剰の状態になっていることを示しています。

■システム/サイクルを新しく作る

さて、先の「顧客>SIer>下請け」を脱却するためには、このシステムから脱却することが一番です、と言いました。「顧客>自社」の体制にしてしまえばいい訳で、そうなると別のシステム/サイクルを作るのが一番です。

注意しておきますが、ここから話すことは話し半分に聞いてください。試行中ですから。

株式会社の場合、会社法的に「株主」が会社を所有します。ですから、会社の所有物/付属物である従業員は株主に所有されているわけです。となれば、従業員が会社を支えるときに、配当を利益の一部から出し、他は役員報酬として分配し、従業員が最低限働くだけの賃金を渡せば、それが会社は成り立ちます。法的には。
この負のスパイラルが多くの現不況下で続いている訳で、従業員である限り、この見えない「株主」という存在から抜け出せません。あるいは、会社への金銭的な投資という束縛から抜けられません。
当然、日本の場合、家族経営型の会社が多かったので、この株主の存在だけでなく、チーム/グループとしての従業員を考えた理念を掲げることが多いのですが、会社自体にこの理念を貫く理由が失われつつあります。
となれば、会社法に従う、株主/役員のための会社が社会生物学的に残ります。

こうなると、下請けであっても役員であれば、従業員よりも多くの報酬を得ます。当然「SIer>下請け」の関係同様に「役員>従業員」の関係が成り立ちます。すると、搾取とまではいいませんが実際、モノを作るのは従業員でありつつ、賃金は安くというスパイラルに入ります。

これを脱する方法があるのか?と言われれば、私は「ない」ような気がします。(あまり信頼できませんが)金持ち父さんシリーズのロバート・キヨサキもそう言います(こちらは別途問題ありですが)。
生きる場を変えるということで「E」の場所に身を置くように努力するのもひとつの方法ですが、残念ながら彼の云うマトリクスはゼロサムゲームでしかありません。

なので、この場合は「株主」を廃してしまうのが適当と思われます。
一時期盛んに行われた株式の上場取りやめ、非上場化がその動きです。最近では株価の安定のためか(?)あまり記事になりませんが。
本来は資金を得るために株式を上場するはずだったのが、株式上場を果たして役員が報酬を得る(手持ちの株価を上げる)という形にスライドしています。つまり、本来は広く資金を集めるために株式を上場し、その資金を元に研究開発/雇用者の増大を図ったわけですが、最近は上場した株の多くを役員が持ちストックオプションとして売り、家や車を買うという「報酬」になっています。ここが間違いなのです。

こうなると、システムを変える(あるいはシステムを戻す)方法として対策が思い浮かびます。

・手持ちの資金で製品開発ができる範囲に絞る。
・非公開株で資金を集め、優秀な従業員を手元に集める(チームを作る)。
・取引コストの低い相手のみ、取引対象とする。
・過剰な資金を求めず、欲しい資金を明確にし、資金源を得る。
ここまで書きましたが、まだ整理がついていません。
続きや整理は久しぶりに MyMy-MyCompany でしようと思います。
http://www.marenijr.net/mymy/

カテゴリー: 起業塾 | 続・受託開発の罠 はコメントを受け付けていません

SQL Server Management Studioでアクセス権情報を作る

泣いていてもしかたがないので、SQL Serverのアクセス権をクエリで設定する方法を調べてみると、

grant select on [テーブル名] to [ロール]

な感じで GRANT を使います。ちなみに、削除をすると場合は、↓な感じ。

revoke select on [テーブル名] to [ロール]

# 実は至高の技のワンポイントに書いていたよ…自分で忘れてた。
さて、手作業で設定はできることは分かった。が、GUIで設定した後にアクセス権の構成情報を取得する手段がない。

そう、気付いたのだが、何も SQL Server Management Studio だけが出力だけが出力できないのではなくて、Oracle や MySQL の場合も出力できない。というわけで、何も SQL Server Management Studio だけが悪いってわけではないんだね。う~む。

業務で DBA の真似ごとをしていて必要なのは、順序付ければ以下のようになります。

a) テーブル、インデックスの構成情報
b) データベースの再構成
c) ログインユーザーの構成情報
d) ストアドプロシージャの構成情報
e) アクセス権の構成情報

このあたり、SQL Server の場合は手厚くて、a)からd)まで出してくれる。だけども、Oracleの場合、a)もひと苦労で、b),c)に至っては初期の情報がないと手に入れるのが難しく、d)はスクリプトがあるんだが、という状態。
まぁ、ソフトウェア開発者的には必要ないんだが、SE(システムエンジニア)イコール大手の社員(プロパー)を示すようになってしまった現在においては、DBAなり設計者なりの能力は低いと判断して構わない。インフラ整備をしている個人事業主のSEの実力はよくわからないのだけど。
少なくとも、インフラ関係の情報は google などでは取得しにくいのは確か。

閑話休題。

実はアクセス権の設定は優先順位が低い。というのも、大抵のプロジェクトはa)からc)で力尽きて(?)しまうわけで、ストアドプロシージャとかアクセス権まで手が廻らないからです。
ただし、ストアドプロシージャに関しては、

・適切な分業体制を作る。
・WEBインフラ、C/Sシステムにて適切な実行環境を得る。

ってな訳で相当重要なことなのだが、忘れ去られつつある(ように見える)。

アクセス権に関しては更に重要で、

・情報の保護
・バックアップの重要度により分離

なところがあるにも関わらず、かなりいい加減なログインユーザーの設定をしていたりする。あいにく、銀行系のデータベースを触ったことがないので、そうなのかもしれないが、情報系のデータベースはかなりいい加減だ(社内で使うからどうでもいいという話もあるが)。

というわけで、(もし)アクセス権を設定しているならば、sp_helprotect を使うと取得できる。

ネタ元
http://q.hatena.ne.jp/1122874335

sp_helprotect @username='limit'

何も付けないとすべてのアクセス権を拾ってしまうので、@username にログイン名/ロール名を指定する。この場合は limit ロールのアクセス権を拾うわけだ。

結果をみると↓のようになる。

Owner Object Grantee Grantor ProtectType Action Column
dbo t_person limit dbo Grant      Select (All+New)
dbo t_在庫 limit dbo Grant      Select (All+New)
dbo t_商品 limit dbo Grant      Select (All+New)

主に見ないといけないのは、ProtectType列とAction列らしい。
ここでは設定として、limitロールに次の3つのテーブルに対して参照権限(SELECT)を与えている。
・t_person
・t_在庫
・t_商品
逆に言えば、ほかのテーブルを見ることができない。
与えるほうはProtectType列が「Grant」、拒否が「Revoke」になる。

# ちなみに以下のマニュアルを見ると、「GRANT」「REVOKE」と大文字になっている。
# SQL Serverの初期値では大文字小文字を区別しないから、どちらでもいいのだが、
# 設定次第だと区別することになるので、この記述は問題だなぁ。

Transact-SQL リファレンス
sp_helprotect
http://msdn.microsoft.com/ja-jp/library/aa933420(SQL.80).aspx

 

実データは、sysprotects テーブルらしいので、こっちから取得するのもあり。

Transact-SQL リファレンス
sysprotects
http://msdn.microsoft.com/ja-jp/library/aa260449(SQL.80).aspx

というわけで、元ネタが分かったので簡単なクエリを書いてみよう。

begin
-- 対象のロールを設定
 declare @name sysname = 'limit'
-- 一時テーブルを作成
 create table #temp (
  [owner] sysname,
  [object] sysname,
  [grantee] sysname,
  [grantor] sysname,
  [protecttype] char(10),
  [action] varchar(20),
  [column] sysname
  )
 insert into #temp execute sp_helprotect @username=@name
-- スクリプトを作成
 select
  [protecttype] + ' ' + [action]
  + ' on [' + [object] + ']' +
  + ' to [' + [grantee] + ']' as query
 from #temp
-- 一時テーブルを削除
 drop table #temp
end

sp_helprotect ストアドプロシージャの結果を直接 FROM 句では受けられないので、一時テーブルを使う。この一時テーブルに対して、文字列を加工して GRANT のスクリプトを生成すればOK。括弧で括ってあるのは念のため。スクリプトを加工する場合は置換で面倒なので外したほうがベターでしょう(括弧は正規表現の予約語だからね)。

結果はこんな感じ

Grant      Select on [t_person] to [limit]
Grant      Select on [t_在庫] to [limit]
Grant      Select on [t_商品] to [limit]

# 一時テーブルのアイデアは下記より借用

なにやらかにやらメモ SQLServer
http://www31.atwiki.jp/memo77/pages/22.html
中の技術日誌ブログ – ストアドプロシージャって難しいなぁ
http://blogs.wankuma.com/naka/archive/2004/03/07/1607.aspx

メンテ専用のストアドプロシージャを作るのもよいのだが、まあ、こんな風にブログにぺたと貼り付けておいて、コピー&ペーストで実行するのもありかな、と。

カテゴリー: 開発 | SQL Server Management Studioでアクセス権情報を作る はコメントを受け付けていません

SQL Server Management Studioでデータベース構成情報が作れない

SQL Server 2005/2008を使っているときは、SQL Server Management Studio を使うと便利だ。

Microsoft SQL Server Management Studio Express
http://www.microsoft.com/downloads/details.aspx?displaylang=ja&FamilyID=c243a5ae-4bd1-4e3d-94b8-5a0f62bf7796

使っているけど(多分)Express版でも製品版でも同じ。GUIでぽちぽちするだけで構成がわかるので便利なんだけど。落とし穴がある(のかな?)。

特定のテーブルへのアクセス制限をするときに、

1.ロールを作る。
2.ロールへログインユーザを加える。
3.ログインユーザからdb_ownerを外す。
4.ロールにテーブルアクセス権を与える。

ということをすると思うんだが。
# 最近はアクセス権自体を触らないDBA(とは言わないな)もいるので、
# なんとも言えませんが。

SQL Server 6.0の頃はEnterprise Managerを使って、スクリプトを吐き出していた。そして手で少し修正するぐらい。
で、同じようにのManagement StudioのGUIでロールを作成、アクセス権の設定をした後に、スクリプトを吐き出してみたら、、、あらら、先の2,3,4のスクリプトが全く吐き出されていないじゃないですか!

具体的に書くと、次のように limit ロールと、normal ユーザがスクリプトで作成できているのだが、

CREATE ROLE [limit] AUTHORIZATION [dbo]
GO
CREATE LOGIN [normal] WITH PASSWORD=N'normal', DEFAULT_DATABASE=[sampledb], 
 CHECK_EXPIRATION=OFF, CHECK_POLICY=OFF
GO

以下のように db_owner から取り除いたり、limit ロールに加えたりするコマンドが何処にもありません。

exec sp_droprolemember 'db_owner', 'normal'
go
exec sp_addrolemember 'limit', 'normal'
go

更に言えば、アクセス権の設定も全くでていません。
ん~、どうなんでしょうね。sp_droprolemember, sp_addrolemember のシステム系のストアドをヘルプから探さないとデータベースの構成情報が作れない、というのどんなもんなんでしょう?

というわけで、これから手作業でデータベース構築のスクリプトを書かなければならず。しくしく。

カテゴリー: 開発 | SQL Server Management Studioでデータベース構成情報が作れない はコメントを受け付けていません

咲-saki-とドカベン

完全な雑談です。

「咲-saki-」という漫画/アニメをご存じでしょうか?高校生が麻雀大会で全国決勝を目指すっていう話なのですが、「けいおん!」に近いというか、大会のナレータ役が「らき☆すた」のあれだったりと、なんか違和感がないので。DVDで見ていたりするのですが。いや、違和感がないのほうが普通じゃないですが(苦笑

さて、現時点で漫画のほうは読んではいませんが、アニメのほうはいわゆる萌え系の絵でストーリーも多少そんなところもあります。しかし、ん~、この高校大会って感触は、ひょっとすると、、、「ドカベン」に似ているのでは!?と思った人は、私だけではないハズハズハズ……私だけですかねぇ。

麻雀漫画ですから、役なり流れなり論理思考っぽいとろが出てきます。でも主人公の咲が嶺上開花で上がり捲るあたり、あの哭きの竜の彷彿とさせますが、あちらのほうは一人が戦う個人戦(つーか、やくざの世界だけど)。でも、咲の世界は高校選手権=団体戦っていうあたり、そして、嶺上開花という特技(?)を持っているあたり、サヨナラホームランを武器(?)とする山田太郎にそっくりです!

そして!、チーム戦ですから、相手の高校にもすごい技を持っている人いるんですよね。ステルスモードとか海底摸月狙いだったり、地獄待ちだったり、ネット麻雀と得意とする論理思考だったり、と(見た人は分かるよね)。
このあたりも、坂田三吉とか不知火とか岩鬼とか殿馬とか、野球のルールに則った得意技を持つキャラがいますよね(見た人は分かるよね!)。

そして、更におもしろいのはそれぞれのキャラに何らかの設定(過去)があって、それを踏まえて今の得意技があるってところです。ここらあたりもドカベンと似ていて、厚みっというかその取って付けた過去に好感が持てます。というか、水島新司の創始がそれだけすごいわけですが、この咲の原作者/脚本家/スタッフも偉いですよね。
チームで県予選から全国を目指すってところ、スラムダンクにも似ています。アタックナンバーワンもそうですね。あいにくスラムダンクには詳しくないので、比較している人/サイトがいると有り難いかな(本当にありがたいのか?)。

と思ったら、いました、いましたスラムダンク風に解説しています。

http://d.hatena.ne.jp/tatsu2/20090908/p1
http://ameblo.jp/yorunoyogiri/entry-10327255685.html

ぜひ、ドカベン風にも語って欲しいものです。

カテゴリー: 雑談 | 5件のコメント

Silverlight の UnitTest

これまでのテストをするためにUnitTestを使っていますが、所謂↓を使っています。

Silverlight Unit Test Framework
http://code.msdn.microsoft.com/silverlightut/

ロジックだけであれば、NUnitとかMicrosoftが提供するTest Frameworkを使えばいいのですが、画面が絡むと途端に面倒になるので。

さて、この「Silverlight Unit Test Framework」ですが、ダミーでボタンをぽちぽち押してくれるようなタイプではありません。先のエントリを見ると分ると思いますが、他のUnitTestと同様にメソッドを羅列していきます。
なので、画面のフォーカス/アウトフォーカスみたいなイベントを取れません。
まあ、本当はそれで動かないと駄目なんですけど、試してみると画面の動きが違ったりして(特に間違ったDataContextの指定の仕方とかすると)なにかとややこしいのですが。
このあたりのイベント絡みをやるためには、別途javascriptとかを組まないと駄目そうですね。

画面のほうは、至ってシンプルです。
ただし、テスト結果がブラウザ上でしか確認できないので、自動化には向いていないそうです(というブログがありました)。

さて、使い始めは何事も躓きやすいのでポイントだけ示しておきます。

■プロジェクトを3分割する

UnitTestをする場合、プロジェクトを3つに分けます。

・テスト対象のプロジェクト(SampleTextBinding)
・テストプロジェクト(SampleTextBinding.Test)
・テスト実行のプロジェクト(SampleTextBinding.Test.Web)

画面やロジックしてあるのは SampleTextBinding のプロジェクトです。
出来る限りここには手を加えないようにします。

テストプロジェクトは TestCase を書いていきます。

テスト実行のプロジェクトはWebのプロジェクトです。テストプロジェクトと一緒にしてもいい気もしますが、なんかうまくいかないので、別々にしました。

■テストプロジェクトに3つの参照を追加する

最初にテストプロジェクトに手を入れます。
参照設定に以下のアセンブリを追加します。

・Microsoft.Silverlight.Testing
・Microsoft.Silverlight.Testing.Framework
・Microsoft.VisualStudio.QualityTools.UnitTesting.Silverlight

■テストケースを作る

テストケースのひな形は、マイテンプレートから「Silverlight Test Case」で追加してもよいですし、
次のひな形を使っても構いません。

using System;
using System.Collections.Generic;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using SampleTextBinding;
using Microsoft.Silverlight.Testing;
using System.ComponentModel;

namespace SampleTextBinding.Test
{
  [TestClass]
  public class TestCase001 : SilverlightTest
  {
    private Page _page;
  
    [TestInitialize]
    public void SetUp()
    {
      _page = new Page();
    }

    [TestMethod]
    public void TestMethod001()
    {
      // ここに記述
    }
  }
}

ひとまとまりのテストケースには「TestClass」の属性を付けます。
SilverlightTest クラスは普通は継承しなくてもよいのですが、非同期のテストの場合はこれを継承します。

私の場合は、必ずページを開くことになるので、初期化時のメソッドSetUpに「TestInitialize」の属性をつけて、初回に実行するようにします。

メソッドを書くときは「TestMethod」をつければOK。

最初は練習用に

public void TestMethod001()
{
  Assert.AreEqual( 1,1 );
}

のように必ず通るテストを付けておけばOKです。

そうそう、忘れていけないのは、App.xml.cs の修正です。

private void Application_Startup(object sender, StartupEventArgs e)
{
  //  this.RootVisual = new Page();
  this.RootVisual = (UIElement)UnitTestSystem.CreateTestPage(this);
}

こんな風にテストページを RootVisual に設定してください。
このとき Silverlight 2 の場合は UIElement へのキャストが必要です。

■テストを動作させるプロジェクトを設定する

SampleTextBinding.Test.Web プロジェクトのことですね。このプロジェクトにテストを動作させるページを追加します。
まず、プロジェクトを右クリックして「プロパティ」を選択します。
「Silverlightアプリケーション」のタブを開いて、追加ボタンを押します。
対象のアプリケーションを選択できるので「SampleTextBinding.Test」のほうを選択します。

すると、SampleTextBinding.TestTestPage.html のようなページができるのでスタートアップページに設定します。そうそう、SampleTextBinding.Test.Web プロジェクトをスタートアップに設定してください。

さて、これで準備完了。どんどんテストを行ってください。

画面のプロジェクトを「なるべく触らない」と書きましたが、実はボタンイベントの場合は触らないと駄目です。
実はボタンイベントは private で設定されてます。

private void BtnSave_Click(object sender, RoutedEventArgs e)
{
  ...
}

なので、これを外側から呼び出すことはできません。public に変えてもいいのですが、本番のクラスのアクセス権を変更してしまうのは問題があります。

というわけで仕方がないので internal を使ったメソッドを作ります。

/// 以下、テスト用
internal void TestBtnSaveClick()
{
  BtnSave_Click(this, new RoutedEventArgs());
}

internal は同じアセンブリから呼べる、という protected と public の間みたいな制限を付けられます。
ここで裏ワザ(かな?)があり、AssemblyInfo.cs に次の記述を書くと別アセンブリのクラスから呼び出せます。

[assembly: InternalsVisibleTo("SampleTextBinding.Test")]

こうすることで、TestMethod のほうから、無事 TestBtnSaveClick を呼べるようになり、ボタンクリックのイベントを発生させられるのです。
Silverlight Unit Test Framework のページには、この他に非同期呼び出しのテストの解説も載っているので参考にしてください。

余談

ボタンのイベントなんですが、Java の UnitTest の潮流から言えば、この internal の方法が正しいのですが、テスト用とはいえ、internal メソッドを入れてしまうのが気に入りません。
できることなら、Windows アプリの SendMessage や SendKey みたいなのでエミュレートしたいですねぇ。

カテゴリー: 開発 | Silverlight の UnitTest はコメントを受け付けていません

XAMLの動的バインド

さて、Silverlight+MVVMの話の続きです。
単純に xaml のコントロール名を使えば楽なのですが、MVVM に則るといちいち{Binding なんとか}を書かないといけません。Binding の構文は、定型といえば定型なのですが、View の中に乱立するのがいまいち解せません。

解せない理由はと言うと、

・そもそも View と Model を分離して View はデザイナ、Model はプログラマ、と切り分けができるのではなかったか?
・View 自体は Microsoft Exprssion Blend を使って編集するわけだから、いちいち Binding の分が書いてあるのはどうかと思う。
・間違って消しちゃったら、Binding は直すの大変。ここのコードを書くのはプログラマなんだから、Viewに手を加えるの問題あり。

というわけで、xaml のほうに Binding を書くのは、かなり解せないのです。
勿論、xaml に書かずに済ますには?というか、方法を模索しないといけないわけで、そうなると対応する *.xaml.cs に書くのがいいですよね。

<Grid x:Name="LayoutRoot" Background="White">
    <StackPanel>
        <Button Name="BtnSave" Width="100" Content="保存" Click="BtnSave_Click" />
        <TextBox Name="TextName" Width="100" Text="{Binding LastName, Mode=TwoWay}" />
        <TextBox Name="TextOut" Width="100" Text="{Binding OutName, Mode=TwoWay}" />
    </StackPanel>
</Grid>

名前は仕方がないとして、Text=”{Binding LastName, Mode=TwoWay}” はデザインから見ると異様です。第一文字列が長いし、何を書いているのかさっぱりわかりません。xaml を直接編集しようにも Binding の部分が長くて編集しづらいです。

なので、できるならば↓な風にしたい。

<Grid x:Name="LayoutRoot" Background="White">
    <StackPanel>
        <Button Name="BtnSave" Width="100" Content="保存"/>
        <TextBox Name="TextName" Width="100" />
        <TextBox Name="TextOut" Width="100" />
    </StackPanel>
</Grid>

ほら、すっきりしたじゃないですか。これならば、WidthプロパティとかContentプロパティとかを編集しやすいですよね。Expression Blend を使っても名前(Name)を設定できますが、バインドのほうは無理です。となれば、デザイナが想像するのはこれくらいシンプルなxamlなはずです。
# まぁ、ストーリーボードとかフィルタが入るとごちゃごちゃしますが。

というわけで、バインドはどうするのかと言うと *.xaml.cs のほうで書きます。コードビハインドを利用するわけですね。

まずはコードを見てください。

public Page2()
{
    InitializeComponent();

    // 動的にバインディング
    Binding bi;
    bi = new Binding("LastName"); 
    bi.Mode = BindingMode.TwoWay;
    this.TextName.SetBinding(TextBox.TextProperty, bi);
    bi = new Binding("OutName"); 
    bi.Mode = BindingMode.TwoWay;
    this.TextOut.SetBinding(TextBox.TextProperty, bi);
    
    // コマンドも動的に設定
    this.BtnSave.Click += BtnSave_Click;

    // データコンテキストに設定
    this.DataContext = MyModel;
}
public Model2 MyModel = new Model2();

動的バインドにはBindingクラスを使います。これを、バインド名を指定して new をします。
バインドの方向は「TwoWay」で指定します。
で、コントロールに結びつけるために SetBinding( どのプロパティか、バインドオブジェクト ) で設定しておしまいです。
これは先の「Text=”{Binding LastName, Mode=TwoWay}”」と同じことです。

まあ3行書くのが面倒な場合は、適当なメソッドを作るとよいでしょう。

private void SetBinding( 
    Control ctrl, DependencyProperty prop, 
    string path, BindingMode mode )
{
    Binding bi = new Binding(path);
    bi.Mode = mode;
    ctrl.SetBinding(prop, bi);
}

public Page()
{
    InitializeComponent();

    // 動的にバインディング
    SetBinding(TextName, TextBox.TextProperty, "LastName", BindingMode.TwoWay);
    SetBinding(TextOut, TextBox.TextProperty, "OutName", BindingMode.TwoWay);

    // コマンドも動的に設定
    this.BtnSave.Click += BtnSave_Click;

    // データコンテキストに設定
    this.DataContext = MyModel;
}

配列にしたりして設定っぽくしておけば間違いが少ないと思います。
どっちにせよ、このバインドという代物は、プログラマ側が設定するので、xaml に入れるのは MVVM 的な視点で言えば望ましくありません。

ちなみに、ボタンクリックのイベントのほうは、

this.BtnSave.Click += BtnSave_Click;

な形で簡単に設定できます。
Visual Studio 2008 上で作ると

this.BtnSave.Click += new RoutedEventHandler(BtnSave_Click);

な形を勝手に作ってくれます。
わざわざ new RoutedEventHandler で包む必要はないと思うのですが、どうなんでしょうね?
RoutedEventHandler を継承した独自なハンドラを作ったら、Click に直接入れられないから new しないと駄目とか。

これをUnitTestを使って動かすと、問題なく動きます。

[TestMethod]
public void TestTextAutoBinding()
{
    Model2 model = _page.MyModel;

    Assert.AreEqual("", model.LastName);
    model.LastName = "masuda";
    Assert.AreEqual("masuda", model.LastName);

    /// 動的にバインディングした場合でも即時
    Assert.AreEqual("", model.OutName);
    _page.TestBtnSaveClick();
    Assert.AreEqual("masuda", model.OutName);
}

直接 xaml に書いても、コードから動的にバインドしても同じ動きですね。当たり前ですが。

カテゴリー: 開発 | XAMLの動的バインド はコメントを受け付けていません