素早く顔を入れ替えるとモンスターに見える?

元ネタ

意外と作るのが大変でした…orz ソースは下記で。

private void button1_Click(object sender, EventArgs e)
{
	List lst = new List();
	lst.Add("野田佳彦");
	lst.Add("平野博文");
	lst.Add("平野達男");
	lst.Add("藤村修");
	lst.Add("田中直紀");
	lst.Add("前田武志");
	lst.Add("川端達夫");
	lst.Add("松原仁");
	lst.Add("小川敏夫");
	lst.Add("小宮山洋子");
	lst.Add("鹿野道彦");
	lst.Add("自見庄三郎");
	lst.Add("枝野幸男");
	lst.Add("細野豪志");
	lst.Add("古川元久");
	lst.Add("玄葉光一郎");
	lst.Add("岡田克也");
	lst.Add("安住淳");

	Random rnd = new Random(DateTime.Now.Second);
	int MAX = 100;
	for (int i = 0; i < MAX; i++)
	{
		string fname1 = lst[rnd.Next(lst.Count)] + ".png";
		string fname2 = lst[rnd.Next(lst.Count)] + ".png";
		fname1 = @"D:\temp\野田改造内閣\out\" + fname1;
		fname2 = @"D:\temp\野田改造内閣\out\" + fname2;

		Bitmap bmp1 = new Bitmap(fname1);
		Bitmap bmp2 = new Bitmap(fname2);
		Bitmap bmp = new Bitmap(bmp1.Width * 2, bmp1.Height);

		Graphics g = Graphics.FromImage(bmp);
		g.DrawImage(bmp1, 0, 0);
		g.DrawImage(bmp2, bmp1.Width, 0);

		string fname = string.Format(@"D:\temp\野田改造内閣\out2\{0:000}.png", i);
		bmp.Save(fname);
	}
}
  1. 「野田改造内閣」の面子を google る
  2. wikipedia にあるパブリック…だと足りないので、適当に拾ってくる。
  3. 画像のサイズをツールを使って揃える。
  4. 画像のサイズをツールを使って切り取る。
  5. ランダム配置するツールをこれ専用に作成。
  6. windows live ムービーメーカーで 0.20 秒間隔で表示させる。
  7. youtube にアップ…って、ムービーメーカーからアップできる。

な感じ。

カテゴリー: 雑談, C# | 2件のコメント

ツイートキャッチ★★★ ver.0.2 を公開

先日、以下の記事で過去ツイートをクロールするツールを作ったものの…

指定したTwitterアカウントの全ツイートを取得(暫定.NET版) | Moonmile Solutions Blog
http://www.moonmile.net/blog/archives/2851

あまりに遅いので、やっぱり twitter api を使うように書き直しました。oauth 認証を使わない場合は、150/h の制限が掛かるのですが、oauth 認証をした後だと 500/h 位?は呼び出しが可能なので、大抵の場合は大丈夫かなと。

200 件ずつ api で拾ってきています。@moonmile の約3000件が、1分ちょっとぐらいで取得可能です。過去3200件までなので、それ以前の場合は、別途作らないと駄目かなと。

■ダウンロード

TwiCatchStar.0.2.zip をダウンロードして解凍してください。

EXDoc.dll
TwiLib.dll
TwiCatchStar.exe
読んでね.txt

の4つのファイルがあるので、適当なフォルダにコピーしてください。
TwiCatchStar.exe が本体です。

■初期設定

初回だけ、oauth 認証の設定をします。

1.「初期化」ボタンを押して、oauth 認証をします。

2.内容を確認して「連携アプリを認証」ボタンをクリックします。

3.PIN 番号をコピーします(番号は、その時によって違います)

4.左側にあるテキストボックスに番号をコピーして、設定ボタンをクリック。

5.認証が成功すると、「取得」ボタンに変わります。

■取得の仕方

左側にあるテキストボックスに「moonmile」のように twitter で使われている名前(screen name)を入れて、「取得」ボタンをクリックしてください。

だいたい1分ぐらい経つと取得が終了します。最新のものから取得するので、途中で辞めたい場合は「中断」ボタンをクリックしてください。

「コピー」ボタンをクリックすると、クリップボードにコピーするのでメモ帳かExcelにペーストして使って下さい。

■今後の予定

保存に関しては、

  • 適当なファイルに保存する。
  • 適当なデータベースに保存する。

予定です。

あとは、外部からの呼び出しが可能にするとか。

カテゴリー: 開発, ツール | 2件のコメント

指定したTwitterアカウントの全ツイートを取得(暫定.NET版)

以前、ツイートのクローラーを perl で作ってみたのですが、

指定したTwitterアカウントの全ツイートを取得(perl版) | Moonmile Solutions Blog
http://www.moonmile.net/blog/archives/860

この perl スクリプトは古い公式クライアントにしか対応していないので、現在のバージョンではクロールできません…ってな訳で、C# で作り直してみました。

通称「ツイートキャッチ ★★キュア」…★は各自考えるように、とか。

実行ファイルは、CrawlTwitter.0.1.zip をダウンロードしてください。ドキュメントとか一切ないけど、まぁ、暫定版ということで。
ソースコードのほうは、CrawlTwitter at master from moonmile/etc – GitHub にあります。

実行すると結構かかります。3000 ツイートで 15 分ぐらいなので、掛かり過ぎ。これは高速化する予定です。
「コピー」ボタンをクリックすると、グリッドの情報をクリップボードにコピーします。Excel などに貼りつけて使って下さい。

下にあるテキストボックスは、取得したドキュメントのソースですね。デバッグ用に表示しているので、コピーして使ってみてください。中身がわかります。

仕掛けとしては、「もっと見る」のボタン(アンカー)を javascript でクリックしています。C# から WebBrowser オブジェクト経由でクリックする場合は、下記のように

橋本商会 ≫ C#のWebBrowserコンポーネントからJavaScriptを実行
http://shokai.org/blog/archives/1234

ブックマーレットにするといいみたいです。詳しくは git のソースコードを見てください。


ちなみに、リンク先の質問に「逆にWebBrowserから情報を得られるのか?」という質問がありますが、ソースコードを見るとわかりますが、

HtmlDocument doc = webBrowser1.Document;

な形で、ブラウザの DOM が取得できます。これを使うと javascript から dom を使うのと同じように扱えます。なので、javascript 側から値を返す場合は、適当なタグを作っておいて、取り出した HtmlDocument オブジェクトから直接取り出せばいい(getelementbyidとか)のです。

カテゴリー: ツール, C# | 4件のコメント

Microsoft MVP 受賞しました(ASP.NET/IIS)

そんな訳で(どんな訳で)、MVP を受賞しました。
去年、つーか一昨年は「Visual C++」だったのですが、今回は「ASP.NET/IIS」です。連載! コードで学ぶ ASP.NET MVC アプリケーション開発入門 | Code Recipe | MSDN あたりの記事と、その元ネタの「ひと目でわかる ASP.NET MVC アプリケーション入門」ですね。おそらく。

手元のブログのほうは、諸々(C/C#/C++/VB)と書き綴っているわけで、最近は objective-c とか。あまり言語にとらわれず、工学系の強みを出してというところです。理論的なところはさておき「どうやって使っていくか」「どう使いこなしていくのか?」に焦点を当てる感じ。

そう云えば「コードで学ぶ ASP.NET MVC」って、もともと12回の予定で、例の地震の関係でラストの2回は尻切れトンボになってしまったのですよね。当初の企画としては、

11. セッション管理
12. Oracle や MySQL を利用するには

を追加する予定だったので、このあたりも時間があれば手元のブログで晒しておきましょうか。

セッション管理については、InProc のセッション管理から、セッションサーバーの設定、SQL Server の設定に切り替えていくパターンを。
MySQL を利用する場合は、例の connect/net を使って、asp.net mvc を動かそうという訳です。先日 Entity Model を使うとそのまま linq が使えることが分かったので、これは「コードで学ぶ~」のほう(ASP.NET MVC アプリケーション開発入門: 第 4 回 Entitiy Frameworkを利用する – MSDN サンプル ギャラリー)で使っているコードをそのまま流用できそうです。「ひと目でわかる~」のほうは、LINQ to SQL を使っているので、両方ともに参考になると思います。

という訳で、今後とも宜しゅう。

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

ローカルデータベースである SQL Server Compact Edition を使う

SQL Server や MySQL は、ローカルコンピュータにデータベースをインストールしないといけないので、業務先のコンピュータにインストールするにはちょっと、という感じがします(まぁ、そういう場合は別途サーバー機を作るわけですが)。
逆に、データベース専用にサーバーを作るほどでもない、あるいは、個々のアプリケーションのデータを database として保存しておきたい場合は、「ファイルベース」のデータベースを使うと良いです。
筆頭に上がるのが、SQLite で、データベースのファイルをコピーするだけで使えます。この他にも、

  • xml 形式のファイルをデータベースとして扱う。
  • access 形式ファイルを扱う。
  • Berkeley DB を使う。

という方法もあります。

で、実は余り知られていない(と思う)のですが、SQL Server 互換のファイルベースのデータベースがあります。

ダウンロード詳細 Windows デスクトップ向け Microsoft SQL Server Compact 3.5 および Microsoft Synchronization Services for ADO.Net v1.0
http://www.microsoft.com/downloads/ja-jp/details.aspx?FamilyID=7849b34f-67ab-481f-a5a5-4990597b0297
ダウンロード詳細 Windows デスクトップ用 Microsoft SQL Server Compact 3.5 Service Pack 2
http://www.microsoft.com/downloads/ja-jp/details.aspx?FamilyID=e497988a-c93a-404c-b161-3a0b323dce24
SQL Server Compact 3.5 オンライン ブック
http://msdn.microsoft.com/ja-jp/library/bb734673(v=sql.100).aspx

と前置きはこれくらいにして、SQLite を使うのもいいけど、compact edition もね、という感じで。compact edition に関しては、windows ce の頃に調べていたのですが、windows phone 7 の場合はどうなんでしょうねえ。次期モバイルには入るらしいのですが。

■ローカルデータベースを追加

visual studio にローカルデータベース(compact edition)を追加します。拡張子は「sdf」ですね。ちなみに、「サービスベースのデータベース」というのは、本家の sql server が使うデータベースファイルです。こっちの拡張子は「mdf」です。

Visual Studio 2010 で作成されるローカルデータベースは、v3.5 のプロバイダでアクセスしています。
「System.Data.SqlServerCe.3.5」のところ。

<?xml version="1.0" encoding="utf-8"?>
<configuration>
 <connectionStrings>
 <add name="Model1Container" connectionString="metadata=res://*/Model1.csdl|res://*/Model1.ssdl|res://*/Model1.msl;provider=System.Data.SqlServerCe.3.5;provider connection string=&quot;Data Source=|DataDirectory|\Database1.sdf&quot;" providerName="System.Data.EntityClient" />
 </connectionStrings>
</configuration>

■Entity Model を作成

ちょっと工夫が要りますが、Enity Model を作成します。初期値は「Model1Container」になってます。

[img 20120110_05.jpg]

作成されたスクリプトはこんな感じ

-- --------------------------------------------------
-- Creating all tables
-- --------------------------------------------------

-- Creating table 'Company'
CREATE TABLE [Company] (
    [ID] int IDENTITY(1,1) NOT NULL,
    [Name] nvarchar(100)  NOT NULL
);
GO

-- Creating table 'Person'
CREATE TABLE [Person] (
    [ID] int IDENTITY(1,1) NOT NULL,
    [Name] nvarchar(100)  NOT NULL,
    [Age] int  NULL,
    [UpdateDate] datetime  NOT NULL,
    [Company_ID] int  NULL
);
GO

-- --------------------------------------------------
-- Creating all PRIMARY KEY constraints
-- --------------------------------------------------

-- Creating primary key on [ID] in table 'Company'
ALTER TABLE [Company]
ADD CONSTRAINT [PK_Company]
    PRIMARY KEY ([ID] );
GO

-- Creating primary key on [ID] in table 'Person'
ALTER TABLE [Person]
ADD CONSTRAINT [PK_Person]
    PRIMARY KEY ([ID] );
GO

-- --------------------------------------------------
-- Creating all FOREIGN KEY constraints
-- --------------------------------------------------

-- Creating foreign key on [Company_ID] in table 'Person'
ALTER TABLE [Person]
ADD CONSTRAINT [FK_CompanyPerson]
    FOREIGN KEY ([Company_ID])
    REFERENCES [Company]
        ([ID])
    ON DELETE NO ACTION ON UPDATE NO ACTION;

-- Creating non-clustered index for FOREIGN KEY 'FK_CompanyPerson'
CREATE INDEX [IX_FK_CompanyPerson]
ON [Person]
    ([Company_ID]);
GO

compact edtion の場合は、文字列は nvarchar に統一されています。なので、sql server からスクリプトを流用するときは、ちょっと変換が必要ですね。

■接続テスト

簡単な接続テスト

///
/// 接続テスト
///
/// <param name="sender" />
/// <param name="e" />
private void button1_Click(object sender, EventArgs e)
{
	Model1Container ent = new Model1Container();
	var persons = from t in ent.Person select t;
	dataGridView1.DataSource = persons;
}

ナビゲーションプロパティを使って person と company を結合。

///
/// リンク付きで検索
///
/// <param name="sender" />
/// <param name="e" />
private void button2_Click(object sender, EventArgs e)
{
	Model1Container ent = new Model1Container();
	var items = from t in ent.Person
				orderby t.Age
				select new {
					t.Name,
					t.Age,
					CompanyName = t.Company.Name };
	dataGridView1.DataSource = items;
}

データベースファイル自体は、「Database1.sdf」というひとつのファイルになるので、これをコピーすればどこでも使えます。ああ、あと .net framework 4.0 も必要ですが。

.NET Framework 4 ダウンロード: SDK | MSDN
http://msdn.microsoft.com/ja-jp/netframework/ff687189

ダウンロード詳細 Windows デスクトップ用 Microsoft SQL Server Compact 3.5 Service Pack 2
http://www.microsoft.com/downloads/ja-jp/details.aspx?FamilyID=e497988a-c93a-404c-b161-3a0b323dce24


追記
SQL Server Compact の場合は、実行ファイルとデータベースファイル(*.sdf)をコピーすれば動くのかと思ったら、別途 SQL Server Compact 3.5 等のインストールが必要になる模様。動かしたときに、プロバイダーが作成できない、という例外が発生する。なので、クリーンなクライアントマシンで単純コピーでは動かないから、数百台のインストールのようなものが発生する場合は、SQLite のほうがいいのかなぁ。できれば、SQL Server Compact のインストールはしたくないので(.NET Framework 4.0 は仕方がないにしても)、適当なアセンブリ(*.dll)をコピーすれば、クリーンな windows 7 マシンでも動作する、っていうのがいいなぁ。

 

カテゴリー: 開発, C# | 1件のコメント

ADO.NET Entity Data Modelの接続先を動的に切り替える

LINQを使う時に、ADO.NET Entity Data Modelを作成する、ってのが今後の主流な訳ですが、Data Model を作るときの接続先をアプリケーションを実行するときに、どうやって切り替えられるのか?という問題があって…いや、知らなかっただけなんですけどね。良く見たら MSDN に書いてあったので。

ConnectionString プロパティ
http://msdn.microsoft.com/ja-jp/library/system.data.entityclient.entityconnection.connectionstring.aspx

普通に作成すると、web.config/app.config に書かれるので、さてこれをどうやって実行時に変えたらいいものか?と悩むわけです。某ひと目 Azure でもかなり悩んだ。

ソースコードは MySQL 接続になっていますが、基本的なところは SQL Server でも同じです。

■web.config/app.confg の connectionStrings で切り替える

Data Entity を作ると、config に connectionStrings/add が作成されます。

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <connectionStrings>
    <add name="wordpressEntities" connectionString="metadata=res://*/Model1.csdl|res://*/Model1.ssdl|res://*/Model1.msl;provider=MySql.Data.MySqlClient;provider connection string=&quot;server=localhost;User Id=wordpress;password=wordpress;Persist Security Info=True;database=wordpress;Allow Zero Datetime=True;Convert Zero Datetime=True;&quot;" providerName="System.Data.EntityClient" />
  </connectionStrings>
</configuration>

こんな風に、作成時の接続文字列が作られるので、同じように別の接続文字列も作ってみます。

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <connectionStrings>
    <add name="wordpressEntities" connectionString="metadata=res://*/Model1.csdl|res://*/Model1.ssdl|res://*/Model1.msl;provider=MySql.Data.MySqlClient;provider connection string=&quot;server=localhost;User Id=wordpress;password=wordpress;Persist Security Info=True;database=wordpress;Allow Zero Datetime=True;Convert Zero Datetime=True;&quot;" providerName="System.Data.EntityClient" />
    <add name="wp33jEntities" connectionString="metadata=res://*/Model1.csdl|res://*/Model1.ssdl|res://*/Model1.msl;provider=MySql.Data.MySqlClient;provider connection string=&quot;server=localhost;User Id=wpuser;password=wppass;Persist Security Info=True;database=wp33jp;Allow Zero Datetime=True;Convert Zero Datetime=True;&quot;" providerName="System.Data.EntityClient" />
  </connectionStrings>
</configuration>

これを、

// 公開している記事一覧を表示
wordpressEntities ent = new wordpressEntities();

のように接続すると、デフォルトで「wordpressEntities」に接続されるようになっています。ここのコードは、model のファイルを開いて「Model1.Designer.cs」を見ると、次のように name が指定されています。

/// <summary>
/// アプリケーション構成ファイルの 'wordpressEntities' セクションにある接続文字列を使用して新しい wordpressEntities オブジェクトを初期化します。
/// </summary>
public wordpressEntities() : base("name=wordpressEntities", "wordpressEntities")
{
    this.ContextOptions.LazyLoadingEnabled = true;
    OnContextCreated();
}

なので、config ファイルに書いていある別の接続文字列を持ってきたい場合は、次のようにコンストラクタで「name=接続名」を指定します。ここでは、config にあらかじめ設定した「wp33jEntities」を取得して表示します。

private void button3_Click(object sender, EventArgs e)
{
	// web.config から設定値を取得
	wordpressEntities ent = new wordpressEntities("name=wp33jEntities");
	var posts = from t in ent.wp_posts
				where t.post_status == "publish"
				&& t.post_type == "post"
				orderby t.post_date descending
				select new { t.ID, t.post_title, t.post_date };
	// バインド
	dataGridView1.DataSource = posts;
}

■接続文字列を動的に設定する

上記の方法だと、あらかじめ web.config/app.config に接続文字列を書いておかないとだめなので、何らかの独自の設定ファイルからの読み出した情報からはコネクションを構築できませんよね。ユーザー名やらパスワードや、サーバー名などを動的に切り替えたい場合があります。
そんな場合は、接続文字列を独自に作成して渡します。先の「wordpressEntities」クラスは、ObjectContext クラスを継承しているので、直接接続文字列を渡せそうなのですが、普通にデータベースに接続する場合とはちょっと違うので、ConnectionString プロパティ を参考にして、EntityConnection オブジェクトを渡すようにします。

private void button4_Click(object sender, EventArgs e)
{
	// 接続文字列を直接指定
	MySqlConnectionStringBuilder sb = new MySqlConnectionStringBuilder();
	sb.Server = "localhost";
	sb.Database = "wp33jp";
	sb.UserID = "wpuser";
	sb.Password = "wppass";
	sb.AllowZeroDateTime = true;
	sb.ConvertZeroDateTime = true;

	EntityConnectionStringBuilder esb = new EntityConnectionStringBuilder();
	esb.Provider = "MySql.Data.MySqlClient";
	esb.ProviderConnectionString = sb.ToString();
	esb.Metadata = "res://*/Model1.csdl|res://*/Model1.ssdl|res://*/Model1.msl";
	EntityConnection cn = new EntityConnection(esb.ToString());

	wordpressEntities ent = new wordpressEntities(cn);
	var posts = from t in ent.wp_posts
				where t.post_status == "publish"
				&& t.post_type == "post"
				orderby t.post_date descending
				select new { t.ID, t.post_title, t.post_date };
	// バインド
	dataGridView1.DataSource = posts;
}

なにやらややこしいですが、MySqlConnectionStringBuilder クラスで「接続文字列」を作成しておいて、EntityConnectionStringBuilder クラスでプロバイダ名やメタデータ(テーブル情報)を指定するという具合です。メタデータ自体は Entity Model クラスのファイル名が出てくるので、それぞれ合わせる必要があります。ファイル名が違っているとリソースから読み込めずにエラーになります。ちなみに、SQL Server に接続する場合は、「SqlConnectionStringBuilder」クラスです。
なお、MySqlConnectionStringBuilder クラスを使うには、「using MySql.Data.MySqlClient;」することになります。

このあたりの接続文字列は、Release時とDebug時に切り替えたり、SQL Azure接続とローカルコンピュータへの接続とかに使えますね。

カテゴリー: C#, MySQL | ADO.NET Entity Data Modelの接続先を動的に切り替える はコメントを受け付けていません

MySQL で LINQ を使う

このブログの MySQL の記事をまとめていると「あれ?意外に少ない」と気が付いて、ADO.NET 絡みでMySQLを扱うTipsでも追加しようかと探していたところなのですが、なんと、LINQ で MySQL を使えることを発見。と言いますか、「至高の技」の執筆中に確認しなかったのが痛恨の一撃。

MySQL :: MySQL 5.1 Reference Manual :: 20.2.4.5 Tutorial: Using an Entity Framework Entity as a Windows Forms Data Source
http://dev.mysql.com/doc/refman/5.6/en/connector-net-tutorials-entity-framework-winform-data-source.html

どうも、日本語の情報が少ないなぁと思っていたら、該当するマニュアルは英語版しかないみたいですね。mysql linq で検索するあまり日本語のページは引っ掛かりません。

ざっと、英語のマニュアルを読めば分かるのですが、手順は非常に簡単です。SQL Server を LINQ で使うのと同じように、MySQL も DataAdapter 経由で LINQ を使うことができます。.NET Framework v4.0 からは、ここの ADO.NET 絡みが大きく変わっているので、かなり楽になっています。

という訳で、簡単に手順など。C# でサンプルコードを書いていますが、VB でも同じことができます。

■ADO.NET Data Entity Model を作成

0. 最初に MySQL :: Download Connector/Net から、Connector/Net 6.4.4 をダウンロードして、インストールしておきます。

1.「新しい項目の追加」で「ADO.NET Entity Data Model」を追加します。

2.「データベースから生成」で「次へ」

3.「新しい接続」ボタンをクリック

4.「変更」をクリック

5.「MySQL Database」をクリックして「.NET Framework Data Prodvider for MySQL」を選択。

6.「接続のプロパティ」で、サーバー名などを設定

7.「詳細設定」ボタンをクリックしたあと、「Allow Zero Datetime」を「True」、「Convert Zero Datetime」を「Ture」」に変更しておく。
これは、MySQL の Datetime は「0000-00-00 00:00:00」が許されるのですが、.NET の Datetime は「1900-01-01」あたりが最小値なためにエラーがでるため、これを自動でコンバートします。
「Unable to convert MySQL date/time value to System.DateTime」という例外が発生したら、ここを設定してみてください。

8.後は、テーブルを選択してエンティティクラスを自動生成します。ここでは、wordpress のテーブルから作成しています。


ここまで出来れば普通にSQL Sever で使う時のように LINQ が使えます。どうやら、sqlite も同じパターンなので、後で試して見る予定です。

■LINQ で検索する

DataGridView を使って、単純に一覧を表示した例です。

private void button1_Click(object sender, EventArgs e)
{
	// 単純に post テーブルの内容を表示
	wordpressEntities ent = new wordpressEntities();
	var posts = from t in ent.wp_posts select t;
	dataGridView1.DataSource = posts;
}

 

公開されている記事だけを検索する例です。

private void button2_Click(object sender, EventArgs e)
{
	// 公開している記事一覧を表示
	wordpressEntities ent = new wordpressEntities();
	/*
		select id, post_date, post_title, post_status, post_type from wp_posts
		where post_status = 'publish' and post_type = 'post'
		order by post_date desc
		*/
	var posts = from t in ent.wp_posts
				where t.post_status == "publish"
				&& t.post_type == "post"
				orderby t.post_date descending
				select new { t.ID, t.post_title, t.post_date };
	// バインド
	dataGridView1.DataSource = posts;
}

 

そんな訳で、LINQ to MySQL(という名前ではないけれど)は簡単に利用できます。wordpress などの oss の場合には mysql が使われていることが多いので、解析したり修正したりするときには勢い php になってしまうんですよね。自分が python あたりが使えるようになると、また別なんでしょうが、どうせならば .NET 関係のツールで揃えられれば windows から扱うのも手軽になるかなと。特に gui は作り易いですからね。

簡単なデータ更新の話とか、統計用の SlimStat のテーブル調査なんかは後ほど時間を取ってやっていこうと思います。SlimStat は、CakePHP から SlimStat へアクセスする(準備) | Moonmile Solutions Blog で準備だけして、頓挫しているので .NET のチャートが使えると結構便利なので。

カテゴリー: C#, MySQL | MySQL で LINQ を使う はコメントを受け付けていません

ひと目でわかるAzureにゃんの準備メモ

久し振りに、.NETラボ 勉強会 2012年01月 で発表をします。で、前々回(行ってないのだけど)も、大和屋氏による発表があったので、.NETラボ 勉強会 2011年11月 それとダブらないように…と思ったけど、先方はインフラとしての Windows Azure の話なのでダブらないですね。私のほうは、開発として Windows Azure の話をします。

主な内容としては、手元の「ひと目でわかる Azure アプリケーション入門」の原稿からピックアップするのと、実際執筆中に起こった落とし穴(まだ校正中で、落とし穴がちらほら)あるので、それをまとめていく予定です。

落とし穴の話は、ぼちぼちこちらのブログにも並べていくと思うのですが、少しメモ書きとして。

  • Windows Azure SDK の日本語版が、非常に簡単に見つけらる件(以前は、探し回らないと駄目だったのですが、Web Platform Installer からインストールできるようになっている)。
  • Tools v1.6 では、web.config の session の項目が直っている(v1.4 の時は、ビルド→デプロイが動かないという)
  • Web ロールを作るときに、ログインを含むマスターページが作られる(ログイン機能が実装されていないので、これは不要…というか、難しいところ)。
  • セッション情報、ログイン情報絡みは、SQL Azure を使わずにテーブルストレージを使いたいところ(所見だけど、Windows Azure だけで閉じておきたい)。
  • ローカルでエミュレーターを動かしたときに、テーブルストレージをリセットする方法(エミュレーターのサービスをリセットすればOK)。
  • Windows Azure 上で、テーブルストレージのリセット(これは、ストレージを再作成するほうが早い)
  • SSMS で、SQL Azure 用のスクリプトを吐き出せる件(SQL Server Management Studio をアップデートする必要あり)。
  • SQL Azure 上では、日本語は nvarchar, nchar を使う(ローカルで作るときは、nvarcharあまり使わない?のだが、SQL Azure上では必須。業務アプリのテーブル設計/移行時に重要)。

のような形で、開発者視点でピックアップしていきます。基本、アプリ開発自体は ASP.NET アプリケーションを作る時と変わらないので、修得は楽なんでしょうが、デバッグやらストレージの設計(特に、テーブルストレージを使う場合)をするときにはまりそうな感じがしてます。

カテゴリー: Azure | ひと目でわかるAzureにゃんの準備メモ はコメントを受け付けていません

キャッシュを作ってDataTableのアクセスを高速にする(2)

データベースアクセスのパフォーマンスチューニング例 | Moonmile Solutions Blog
http://www.moonmile.net/blog/archives/2423
キャッシュを作ってDataTableのアクセスを高速にする | Moonmile Solutions Blog
http://www.moonmile.net/blog/archives/2780

の続きで、複数キー対応のDictonaryを作成してキャッシュを有効に使う例をば。
つまりは、「Dictonary」のような形で、2つの int型のキーを持っていて、それから ProductRow オブジェクト(Productテーブルの列オブジェクト)を取ってくる。

■利用方法

まずは、使い方を。

/// <summary>
/// Product のキャッシュ
/// </summary>
private ProductCache pcache = null;
/// <summary>
/// 複数キーのキャッシュ実行
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void button2_Click(object sender, EventArgs e)
{
	if (cache == null)
	{
		SqlConnection cn = new SqlConnection(CNSTR);
		SqlDataAdapter da = new SqlDataAdapter("SELECT * FROM PRODUCT", cn);
		DataTable dt = new DataTable();
		da.Fill(dt);

		// キャッシュに設定
		Product product = new Product();
		DataBind.Conv(dt, product);
		this.pcache.Fill(product.Rows);
	}
	int id = int.Parse(textBox1.Text);
	int subid = int.Parse(textBox2.Text);
	// 2つのキーで検索
	ProductRow row = this.pcache[id, subid];
	textBox2.Text = row.name;

}

初回だけデータベースに接続して、キャッシュを作成します。
DataRow から独自の Product テーブルクラスにコピーした後に、キャッシュ pcache に保存します。
キャッシュへのアクセスは、pcache[ id, subid ] のように配列でアクセスできて、戻り値は ProductRow オブジェクトという具合。DataRow よりも型付の ProductRow のほうが、row.id とか row.name とかのプロパティアクセスができるので、コードの品質はあがるはずです。インテリセンスが使ええるし、コンパイル時に列名が間違っているとエラーになるし。

■内部実装

キャッシュクラスはこんな感じ。

/// <summary>
/// 複数キーに対応したDictonary
/// キーが2つの場合
/// </summary>
/// <typeparam name="K1"></typeparam>
/// <typeparam name="K2"></typeparam>
/// <typeparam name="V"></typeparam>
public abstract class Dictonary<K1, K2, V> : Dictionary<string, V> where V : new()
{
	public V Empty { get; set; }

	/// <summary>
	/// コンストラクタ
	/// </summary>
	public Dictonary()
	{
		this.Empty = new V();
	}

	// protected override
	protected void Add(string key) { }
	protected new  V this[string key] { get { return Empty; } set { } }
	protected new bool ContainsKey(string key) { return false; }

	/// <summary>
	/// キーと値を追加
	/// </summary>
	/// <param name="key1"></param>
	/// <param name="key2"></param>
	/// <param name="value"></param>
	public void Add(K1 key1, K2 key2, V value)
	{
		this.Add(MakeKey(key1, key2), value);
	}
	/// <summary>
	/// キーを作成
	/// </summary>
	/// <param name="key1"></param>
	/// <param name="key2"></param>
	/// <returns></returns>
	public string MakeKey(K1 key1, K2 key2)
	{
		return key1.ToString() + "_" + key2.ToString();
	}
	public abstract string MakeKey(V value);

	/// <summary>
	/// []演算子でアクセス
	/// </summary>
	/// <param name="key1"></param>
	/// <param name="key2"></param>
	/// <returns></returns>
	public V this[K1 key1, K2 key2]
	{
		get
		{
			string key = MakeKey(key1, key2);
			return this.ContainsKey(key) ? this[key] : Empty;
		}
		set
		{
			string key = MakeKey(key1, key2);
			if (this.ContainsKey(key))
			{
				this[key] = value;
			}
			else
			{
				this.Add(key, value);
			}
		}
	}
	/// <summary>
	/// 指定したキーを含むか
	/// </summary>
	/// <param name="key1"></param>
	/// <param name="key2"></param>
	/// <returns></returns>
	public bool ContainsKey(K1 key1, K2 key2)
	{
		string key = MakeKey(key1, key2);
		return base.ContainsKey(key);
	}
	/// <summary>
	/// データを設定
	/// </summary>
	/// <param name="table"></param>
	public void Fill(IList<V> items)
	{
		foreach (V it in items)
		{
			this[MakeKey(it)] = it;
		}
	}
}

抽象クラスになっているのは、DataRow にあたる V から主キーは直接取り出せないので、一度継承するという方式です。データエンティティに「主キーの属性」みたいなのをつければ、ここは解決するかも。

もうひとつは、DataRow から型付のDataRowクラスにコンバートするためのクラスが必要と思います。毎回コンバートだけコードを書くのは面倒なので、テンプレートとリフレクションを使って自動化します。

/// <summary>
/// リフレクションを簡単にするための準備
/// </summary>
public interface IDataTable
{
	Type GetRowType();
	object CreateRow();
}
/// <summary>
/// 型付DataTable用のテンプレート
/// </summary>
/// <typeparam name="DRType"></typeparam>
public class DTTemplate<DRType> : IDataTable, IListSource
{
	/// <summary>
	/// DataRow の型を返す
	/// </summary>
	/// <returns></returns>
	public Type GetRowType()
	{
		return typeof(DRType);
	}
	/// <summary>
	/// DataRow のリスト
	/// </summary>
	protected List<DRType> _rows = new List<DRType>();
	public List<DRType> Rows
	{
		get { return _rows; }
	}

	/// <summary>
	/// 新しい DataRow を作成
	/// </summary>
	/// <returns></returns>
	public DRType NewRow()
	{
		return (DRType)Activator.CreateInstance(typeof(DRType));
	}
	/// <summary>
	/// 新しい DataRow を作成(object型)
	/// </summary>
	/// <returns></returns>
	public object CreateRow()
	{
		return NewRow();
	}

	// インターフェースの実装
	/// <summary>
	/// 内部リストから IList を使う
	/// </summary>
	public bool ContainsListCollection
	{
		get { return true; }
	}
	/// <summary>
	/// IListのコレクションを返す
	/// </summary>
	/// <returns></returns>
	public System.Collections.IList GetList()
	{
		return this.Rows;
	}
}

/// <summary>
/// <summary>
/// 形無しDataSetを型付DataSetにコンバートするクラス
/// </summary>
public class DataBind
{
	public static void Conv(DataTable src, IDataTable dest)
	{
		Type rowType = dest.GetRowType();
		System.Collections.IList rows = ((IListSource)dest).GetList();
		foreach (DataRow row in src.Rows)
		{
			object item = dest.CreateRow();
			// リフレクションを使って、列ごとにコピーする
			foreach (DataColumn column in src.Columns)
			{
				// この部分はキャッシュすると高速化する
				string key = column.ColumnName;
				PropertyInfo pi = rowType.GetProperty(key);
				if (pi != null)
				{
					// object型 -> 元の型のキャストでエラーになるため、
					// 一度元の型に ChangeType してから代入する。
					pi.SetValue(item,
						Convert.ChangeType(row[key], pi.PropertyType),
						null);
				}
			}
			rows.Add(item);
		}
	}
}

これを作成しておいて、自前のエンティティのクラスやキャッシュクラスを作成。

/// <summary>
/// 複数キーを持つ Product エンティティクラス
/// </summary>
public class ProductRow
{
	public int id { get; set; }		// PK
	public int subid { get; set; }	// PK
	public string name { get; set; }
	public int parent_id { get; set; }
	public string desc { get; set; }
	public DateTime updatedate { get; set; }
}
/// <summary>
/// 型付DataTable
/// </summary>
public class Product : DTTemplate<ProductRow> { }

/// <summary>
/// Product キャッシュクラスを作成
/// </summary>
public class ProductCache : Dictonary<int, int, ProductRow>
{
	/// <summary>
	/// ProductRow からキー文字列を取得
	/// </summary>
	/// <param name="value"></param>
	/// <returns></returns>
	public override string MakeKey(ProductRow value)
	{
		return MakeKey(value.id, value.subid);
	}
}

テンプレートのまま使ってもいいけど、もう一度のエンティティクラスの名前を付け直すとコーディングが楽です。テーブル名と揃えておくと、データベース仕様書とのマッチングが楽とか。

ちなみに、キーが3つある場合は、先の Dictonary のコードをコピーして、

public abstract class Dictonary<K1, K2, K3, V> : Dictionary<string, V> where V : new()

のように倍増させておきます。あらかじめ、6個ぐらい作っておけば大抵のテーブルは大丈夫かと。デリゲートの <Action> と同じ仕組みですね。

カテゴリー: C# | キャッシュを作ってDataTableのアクセスを高速にする(2) はコメントを受け付けていません

キャッシュを作ってDataTableのアクセスを高速にする

データベースアクセスのパフォーマンスチューニング例 | Moonmile Solutions Blog
http://www.moonmile.net/blog/archives/2423

の続きを作ろうと思って「3分間クッキング」ならぬ「30分間プログラミング」になってしまいました。テンプレートを使ったものと、DataRow をそのまま使ったものとを 2 つ作ってしまったかですね。DataTable や List<> の場合には、Select メソッドを使えるのではないか?というのもありますが、Dictionary を使っているので速度的には断然高速なはずです。少なくとも、20万件ぐらいでも、Dictionary の場合は、一瞬(0.001秒以下)で返ってきます。

■利用の仕方

private string CNSTR = "";
private AnkenCache cache = null;

private void button1_Click(object sender, EventArgs e)
{
	if (cache == null)
	{
		SqlConnection cn = new SqlConnection(CNSTR);
		SqlDataAdapter da = new SqlDataAdapter("SELECT * FROM ANKEN", cn);
		DataTable dt = new DataTable();
		da.Fill(dt);
		// キャッシュに設定
		this.cache.SetData(dt);
	}
	int id = int.Parse(textBox1.Text);
	DataRow row = cache.Get(id.ToString());
	textBox2.Text = (string)row["name"];
}

こんな風にキャッシュ利用します。1度目はデータベースから検索するけど、2度目以降はキャッシュを使うってことで。
キー情報は、string 型なので、適当な MakeKey なる関数を作っておくと便利です。

■内部コード

DataTable を渡して DataRow の中身を Dictionary にキャッシュしているだけです。某案件では、これに似たことをやりましたが、こっちのほうが洗練されている…と思う。

public abstract class SelectCacheTableRow
{
	protected Dictionary<string, DataRow> _dic;
	public DataRow Empty { get; set; }

	public SelectCacheTableRow()
	{
		_dic = new Dictionary<string, DataRow>();
		this.Empty = new DataTable().NewRow(); // 空を設定
	}
	public void Set(string key, DataRow value)
	{
		if (_dic.ContainsKey(key) == true)
		{
			_dic[key] = value;
		}
		else
		{
			_dic.Add(key, value);
		}
	}
	public DataRow Get(string key)
	{
		if (_dic.ContainsKey(key) == true)
		{
			return _dic[key];
		}
		else
		{
			return this.Empty;
		}
	}
	/// <summary>
	/// キー情報を作成
	/// </summary>
	/// <param name="t"></param>
	/// <returns></returns>
	public abstract string MakeKey(DataRow it);
	/// <summary>
	/// データを設定
	/// </summary>
	/// <param name="table"></param>
	public void SetData(DataTable table)
	{
		foreach (DataRow it in table.Rows)
		{
			this.Set(MakeKey(it), it);
		}
	}
}

public class AnkenCache : SelectCacheTableRow
{
	/// <summary>
	/// キー情報を作成
	/// </summary>
	/// <param name="t"></param>
	/// <returns></returns>
	public override string MakeKey(DataRow row)
	{
		return row["id"].ToString();
	}
}

■テンプレートを使う

上記の例だと、DataRow しか扱えないので、単純なデータクラスを使いたい場合(こっちのほうがお勧めなんだが)、テンプレート(正確にはジェネリック)版を使います。「Project」というプロパティだけのクラスを作っておいて、プロパティ名でアクセスできるようにします。DataRow からデータクラスへのコンバートは、リフレクションを使ったバージョンを用意しておくと(型なしDataTableから型付きDataTableにコピーする方法 | Moonmile Solutions Blog 参照)いいでしょう。

public abstract class SelectCache<T> where T: new()
{
	protected Dictionary<string, T> _dic;
	public T Empty { get; set; }

	public SelectCache()
	{
		_dic = new Dictionary<string, T>();
		this.Empty = new T(); // 空を設定
	}
	public void Set(string key, T value)
	{
		if (_dic.ContainsKey(key) == true)
		{
			_dic[key] = value;
		}
		else
		{
			_dic.Add(key, value);
		}
	}
	public T Get(string key)
	{
		if (_dic.ContainsKey(key) == true)
		{
			return _dic[key];
		}
		else
		{
			return this.Empty;
		}
	}
	/// <summary>
	/// キー情報を作成
	/// </summary>
	/// <param name="t"></param>
	/// <returns></returns>
	public abstract string MakeKey(T it);
	/// <summary>
	/// データを設定
	/// </summary>
	/// <param name="table"></param>
	public void SetData(List<T> items)
	{
		foreach (T it in items)
		{
			this.Set(MakeKey(it) , it);
		}
	}
}
public class Project
{
	public int id { get; set; }
	public int projnum { get; set; }
	public string name { get; set; }
	public int parent_id { get; set; }
	public string desc { get; set; }
	public DateTime updatedate { get; set; }
}
public class ProjectCache : SelectCache<Project>
{
	// キー情報を作成
	public override string MakeKey(Project it)
	{
		return it.id.ToString() + "_" + it.projnum.ToString();
	}
}

Empty プロパティを null にしないのは、以下のような書き方でもエラーにならないようにするためです。DataRow の場合は名前がマッチングしないと例外を発生しますが、こちらの場合は1行で書けるから便利かと。if文でいちいちチェックしなくてよいので。

string name = cache.Get( key ).name ;

敢えて空を調べたいときは、以下のように書きます。

string name = "";
if ( cache.Get(key) != cache.Empty ) {
  name = cache.Get( key ).name ;
}
カテゴリー: C# | キャッシュを作ってDataTableのアクセスを高速にする はコメントを受け付けていません