引き続きやってます。めでたくレベル2になりました。
Linqも、SelctManyやAggregateの使い方がわかってきた気がします。
一つわからないのが下の様なケース。TakeWhileやAggregateで出来そうな気もするんですが。どうするのがいいんだろう。
・合計がxを越えない範囲で列挙する。
あと、.NET Framework4でBigIntegerが追加されてることを知りました。便利。
#というか反則っぽいなぁ
技術ブログを始めたくてなんとなくスタートです。 たぶん長続きしないはず…
引き続きやってます。めでたくレベル2になりました。
Linqも、SelctManyやAggregateの使い方がわかってきた気がします。
一つわからないのが下の様なケース。TakeWhileやAggregateで出来そうな気もするんですが。どうするのがいいんだろう。
・合計がxを越えない範囲で列挙する。
あと、.NET Framework4でBigIntegerが追加されてることを知りました。便利。
#というか反則っぽいなぁ
Project Eulerなるものを知った。数学の問題をプログラムで解くらしい。
おもしろそうなので、登録してみた。
#そういえば、TopCoderに登録してそのままなことを思い出した。
http://projecteuler.net/
日本語のWikiもあり、問題も日本語化されているようだ。<やった。
http://odz.sakura.ne.jp/projecteuler/index.php?Project%20Euler
番号が若いうちは比較的簡単。出来るだけLinqを使って書くように心がける。
#でもLinqだと無駄が出やすい気がする。スピード重視なら、手続き指向になるのかなぁ。
とりあえず、今日は8問目まで。
いい加減、Azureへデプロイです。
プロジェクトを右クリックメニューより発行を選択。
表示されたダイアログで、「サービスパッケージだけを作成」を選択しOKをクリックします。
.cspkgファイルとcscfgファイルが作成されます。
これを、Azure環境にデプロイします。
まず、Windows Azure Developer Portal(http://windows.azure.com/)にアクセスします。その後、New Serviceをクリックします。
Hosted Servicesを選択します。
Service Labelと、Service Descriptionを入力します。適当でOK.
次にURLと、デプロイ場所を決定し、Createボタンをクリックします。
とりあえず、出来ました。そして、Deployボタンをクリック。
Application Packageに.cspkgファイルを、Configuration Settingsに.cscfgファイルを設定。Deployment Nameを入力したら、Deployボタンをクリック。
デプロイ完了です。最初は、Stoppedなので、Runボタンをクリックしアプリケーションを開始します。
しばらく、待つとデプロイが完了します。ステータスは次のように変更されます。
以上で、すべて完了。
油断すると、すぐ間隔が開いてしまって困ったことです。
前回の続きです。今回は、とりあえず実行するために、デフォルト設定で突き進みます。
まずは、プロジェクトの作成から。
VisualStudio2010を管理者モードで起動し(これ重要!いつも忘れます(^^;))、新しいプロジェクトの作成より、Windows Azure クラウドサービスを選択。
プロジェクトには、とりあえず、ASP.NET Webロールを一つだけ追加。
すると、ソリューションが作成されます。
その後、いきなりF5で実行。ASP.NETで作成されたテンプレートページが表示されます。一見、普通のWeb開発と変わりません。
違いはというと、タスクトレイのアイコンぐらい
右クリックメニューより、Development Fabric UIとDevelopment Storage UIを表示することが可能です。
まずは、Development Fabric UI。
続いて、Develpment Storage UI。
以上で、ローカルでのAzureプロジェクトの実行は完了です。 次は、ようやくデプロイ。
昨日、Azureの申し込みを完了したので、今日は環境の整備。
Windows Azure Toolsをインストールします。
ダウンロードは、下記から。
「Windows Azure Tools for Microsoft Visual Studio 1.2」
http://www.microsoft.com/downloads/details.aspx?displaylang=ja&FamilyID=2274a0a8-5d37-4eac-b50a-e197dc340f6f
リンク先ページにある、注意事項を参考にしつつインストールしました。
Windows7(x64)×VS2010なので、↓だけ追加でインストール。
http://code.msdn.microsoft.com/KB981002
というわけで、環境の準備は終了。そして今日の作業も終了。
明日はサンプル作りたいな。
MSDNサブスクリプションを利用中の方には特典が用意されています。詳しくは↓。
http://msdn.microsoft.com/ja-jp/subscriptions/ee461076.aspx
幸いにもPremiumの割り当てがあるので、申し込みを行ってみます。
入り口は、MSDNサブスクリプションのページから。
WIndows Azure プラットフォームのリンクをクリックすると、Microsoft Online Service カスターマー ポータルにサインインします。(アカウントがない場合は、ここで作成します。)
すると、ショッピングカートに商品が入っている状態に。あとは、同意するチェックボックスにチェックを入れて、購入手続きへ。(クレジットカード情報等を入力します。なぜか途中のValidationに引っかかって、何回か最初からやり直しましたが…)
あとは、サービスをアクティブにすれば終了。とりあえず、きょうはここまでか。
今更ですが、ダウンロード可能になってました。
http://www.microsoft.com/japan/products/expression/
さて試すか。
SQLServerCeで行ロックがかかっているかの実験。
using (var connection1 = new SqlCeConnection(Settings.Default.Setting))
using (var connection2 = new SqlCeConnection(Settings.Default.Setting))
{
connection1.Open();
connection2.Open();
var command1 = connection1.CreateCommand();
var command2 = connection2.CreateCommand();
using (var tran1 = connection1.BeginTransaction())
using (var tran2 = connection2.BeginTransaction())
{
command1.Transaction = tran1;
command2.Transaction = tran2;
command1.CommandText = "select * from table1 with (updlock) where id = 1";
command2.CommandText = "select * from table1 with (updlock) where id = 1";
using (var reader1 = command1.ExecuteReader())
using (var reader2 = command2.ExecuteReader())
{
while (reader1.Read() && reader2.Read())
{
Console.WriteLine(reader1.GetInt32(0));
Console.WriteLine(reader2.GetInt32(0));
}
}
}
}
確かに。
Visual Studio 2010 Pro Power Toolsをインストールしてみた。 http://visualstudiogallery.msdn.microsoft.com/en-us/d0d33361-18e2-46c0-8ff2-4adea1e34fef
など盛りだくさん。 あと、Alt+↑ とか Alt+↓とかで、コード移動が出来るみたいなのだが試してもよくわからなかった。
Entity Framework4では、外部キーをスカラプロパティーとして公開することが出来ます。
外部キーの使用 (Entity Framework)
http://msdn.microsoft.com/ja-jp/library/ee794150.aspx
でも、追加方法がよくわからずはまったので備忘録。
①追加からアソシエーションを選択すること。
(ツールボックスからではダメ。ここではまった。)
②外部キープロパティーの追加にチェックを付ける
③プロパティーが追加されます。
ところで、追加されたプロパティーが、どのアソシエーションに関連しているかを確認する方法がない?
長らくx64環境からMDBファイルにアクセスすることが出来なかった。
(正しくは、32bitアプリとしてWOW64上で動作する様に、ビルド構成としてx86を指定する必要があった。)
Office2010(x64)をインストールすることにより、64bitのアプリケーションでもMDBファイルにアクセスできるようになる。ConnectionStringで”Microsoft.ACE.OLEDB.12.0”するだけである。
データ接続コンポーネントも、今はベータがとれていないが、そのうちベータがとれるだろう。
(2010 Office system Beta ドライバ: データ接続コンポーネント)
http://www.microsoft.com/downloads/details.aspx?familyid=C06B8369-60DD-4B64-A44B-84B371EDE16D&displaylang=ja
これにより、32bitでは"Microsoft.JET.OLEDB.4.0"、64bitでは"Microsoft.ACE.OLEDB.12.0”と接続文字列を切り替えることにより、どちらの環境でも動作させることが可能になる。
※32bitでも下記ドライバをインストールすれば、"Microsoft.ACE.OLEDB.12.0”で接続が可能。
2007 Office system ドライバ: データ接続コンポーネント
http://www.microsoft.com/downloads/details.aspx?FamilyId=7554F536-8C28-4598-9B72-EF94E038C891&displaylang=ja
なお、32bitで動作しているか64bitで動作しているかは、IntPtr.Sizeで判定が可能だそうだ。
(とあるコンサルタントのつぶやき:Part 2. .NET Framework 2.0 アプリケーションの 64 ビット対応)
http://blogs.msdn.com/nakama/archive/2008/11/06/part-2-net-framework-2-0-64.aspx
なお、アプリケーションの内部から、現在自分が 64 ビット/32 ビットどちらで動作しているのかを知る一番簡単な方法は、IntPtr.Size プロパティをチェックするというものです。
- IntPtr.Size = 8 の場合は 64 ビットモードでプロセスが動作している。
- IntPtr.Size = 4 の場合は 32 ビットモードでプロセスが動作している。
週末を利用して、viliv S5にWindows7をインストールすることを画策中。
とりあえず、ブータブルのUSBメモリを作成中。
最近はMicrosoftがISOからブータブルUSBメモリを作成するツールを配布してるんですな。
ISOのダウンロード販売といい、便利な時代になったもんだ。
http://www.microsoftstore.jp/Form/Guide/downloadTool.aspx
Visual Studio 2010(.NET Framework 4)より、PLINQ (Parallel LINQ)が追加され、並列処理が簡単にできるようになっています。
10 行でズバリ !! 並列プログラミング - PLINQ (C#)より
PLINQ は LINQ によるデータ コレクションに対するクエリ処理を並列化するためのものであり、PLINQ を使用することで LINQ クエリの Select 句や Where 句で実行されるロジックを容易に並列処理化することが可能です。PLINQ の機能は ParallelEnumerable クラス (System.Linq.ParallelEnumerable) の拡張メソッドとして提供されており、LINQ クエリの処理対象データ コレクションに対して ParallelEnumerable クラスの拡張メソッドの呼び出しを追加するだけでデータ コレクションへの処理を並列化し、マルチプロセッサー/マルチコア CPU の処理能力を活用して処理時間を短縮することができます。
と言うわけで、先日の重いLinq構文を何とか出来ないかとあちこちにAsParallelを追加してみました。
が、Aggregate構文を途中で使用しているためか、追加前より速くなることはありませんでした。
残念。
というわけで、早速お試し。
気になる機能その1 Coded UI Test
なんと、GUIのUnitテストが出来るらしい。
①まずは、超簡単なWinFormアプリを作成
テキストボックスに文字列をいれて、ボタンを押下すると下記ダイアログを表示する
ただそれだけです。
②テストの追加
Testメニューから、NewTestを選択。そうすると下記ダイアログが表示されるので、Coded UI Testを選択する。
そうすると、どうやってUIテストを作成するかを聞いてくる。一番最初はテスト資産がないので、上を選択。
画面の右下に、小さなポップアップ?が表示されるので、準備OK。
ここからの正しい使い方がよくわかっていないので、適当に。
(i)テスト対象アプリの起動
とりあえず、テスト対象が起動していないことにはどうにもならないので、起動します。
(ii)赤いボタンを押下
操作記録が開始されます。
(iii)適当に操作
検証したいポイントの手前まで操作します。今回の場合、はテキストボックスに文字入力&ボタン押下。
(iv)一番右のボタン押下
行った操作に対するコードが生成されます。
(iv)照準アイコンを検証したいコントロールへD&D
対象コントロールのプロパティーが表示されます。
(v)検証したいプロパティーを選択し、Add Assertionをクリック
表示されるダイアログで、検証の条件を追加します。
(vi)再び、コードを生成
今度はAssertionコードが追加されます。
以上で、テストケースの作成は終わり。
③テストの実行
[TestMethod]
public void CodedUITestMethod1()
{
this.UIMap.RecordedMethod1();
this.UIMap.AssertMethod1();
}
気付き
IMEが有効になっていると、途中でテストが失敗する。
いまいち。自動テストとかできるんだろうか。。。
前々から欲しかったPocket Wifiを買ってしまった。
http://emobile.jp/pocketwifi/
買った後で、何なんですが、コース別の料金を比べてみた。
スーパーライトのライト加減がよくわかりますな。
どのコース従量の部分が狭いこと。。。
0~9までの数字のカードがN枚ある。同じ数字のカードは1枚とは限らない。
そこから、M枚使用しM桁の整数を作るとき、小さい方から数えi番目の数値は何か?
※先頭桁に0はこないものとする。
という問題が与えられた場合、これを効率よく説くにはどうすればよいか?
とりあえず、効率よくという部分は無視して、順列を作成して前から数えてみた。順列を作成するに当たりLinqを使用しているサイトが見つかったので、利用にさせていただいた。
http://d.hatena.ne.jp/taguo/20080722/1216745650
class Program
{
static void Main(string[] args)
{
int[] array = {0,0,1,2,2,3};
var result = array.GetPermutation(4)
.Where(x => x.ElementAt(0) != 0)
.Select(x => string.Join("", x.Select(i => i.ToString()).ToArray()))
.Distinct();
result = result.Skip(10).Take(1);
foreach (var item in result)
{
Console.WriteLine(item);
}
}
}
public static class Util
{
public static IEnumerable<IEnumerable<T>> GetPermutation<T>(this IEnumerable<T> source, int count)
{
return Enumerable.Range(0, count)
.Select(_ => source)
.Select(et => et.Select((t, i) => new { t, i }))
.Aggregate(Enumerable.Repeat(Enumerable.Repeat(new { t = default(T), i = default(int) }, 0), 1)
, (ac, et) => from a in ac
from t in et
where !a.Contains(t)
select a.Concat(Enumerable.Repeat(t, 1)))
.Select(ea => ea.Select(a => a.t));
}
}
実は、delegateの非同期呼出は使ったことがなかったり…
というわけで、MultiCastDelegateといえどもターゲットが複数あるとダメなのは初めて知りました。
出来ても良さそうなのにね。
delegateは、メソッド情報(Methodプロパティー)を呼びメソッドの定義されるインスタンスの情報(Targetプロパティー)を持ち、それを同期または非同期に呼び出すことができる。
というわけで、まずは同期呼出から。とっても基本的な使い方。
delegate void Hoge(string str);
static void Main(string[] args)
{
Hoge hoge = null;
hoge = Console.WriteLine;
hoge("AAA");
hoge.Invoke("AAA");
}
delegateで宣言すると、MultiCastDelegateのサブクラスとなるので、常にInvocation Listを作ることが可能。
static void Main(string[] args)
{
Hoge hoge = null;
hoge = Console.WriteLine;
hoge += delegate(string str)
{
Console.WriteLine(str.ToLower());
};
hoge("AAA");
}
メソッドの引数は同じオブジェクトが順番に渡されるため、1番目のメソッドで行われた変更を2番目のメソッドへと引き継ぐ。MultiCastDelegateの場合、最後のメソッドの戻り値以外は捨てられるので、引数のオブジェクトを通してしか情報の伝達ができない。
class Program
{
//戻値の型 デリゲートの型名 引数の型
delegate void Hoge(Class c);
static void Main(string[] args)
{
Hoge hoge = null;
hoge += AAA;
hoge += AAA;
hoge += AAA;
hoge += AAA;
hoge(new Class());
}
static void AAA(Class c)
{
Console.WriteLine(c.MyProperty);
++c.MyProperty;
}
}
class Class : Interface
{
public int MyProperty { get; set; }
}
UIはどうも苦手。勉強しなきゃ何だけどなぁ。
後で読むようにまとめておく。
エンジニアにもわかる「ユーザーインターフェース設計」
http://techblog.yahoo.co.jp/cat207/how_to/post_12/
ソシオメディア:UIデザインパターン
https://www.sociomedia.co.jp/category/uidesignpatterns
インフラジスティックス:Quince
http://jp.quince.infragistics.com/
あと、WPF向けのコントロールとか欲しくなるなぁ。個人では絶対無理だけど。
http://jp.infragistics.com/dotnet/netadvantage/wpf.aspx#Overview
http://www.devcomponents.com/dotnetbar-wpf/
http://www.actiprosoftware.com/Products/DotNet/WPF/WPFStudio/Default.aspx
Interfaceは参照型だったよな。と思い確認。
class Program
{
static void Main(string[] args)
{
Struct s = new Struct();
s.MyProperty = 1;
Interface i = s;
i.MyProperty = 2;
Interface i2 = i;
i2.MyProperty = 3;
Console.WriteLine("s :{0}", s.MyProperty);
Console.WriteLine("i :{0}", i.MyProperty);
Console.WriteLine("i2:{0}", i2.MyProperty);
}
}
interface Interface
{
int MyProperty { get; set; }
}
struct Struct : Interface
{
public int MyProperty { get; set; }
}
正解。
つまり、Interface型に代入する際にボックス化が行われているということ。
class Program
{
static void Main(string[] args)
{
var val = new Struct();
var val2 = new Class();
Stopwatch sw = Stopwatch.StartNew();
for (int i = 0; i < 1000000000; i++)
{
object obj = val;
}
Console.WriteLine("struct -> object :" + sw.Elapsed);
sw.Reset();
sw.Start();
for (int i = 0; i < 1000000000; i++)
{
Interface obj = val;
}
Console.WriteLine("struct -> Interface:" + sw.Elapsed);
sw.Reset();
sw.Start();
for (int i = 0; i < 1000000000; i++)
{
var obj = val;
}
Console.WriteLine("struct -> struct :" + sw.Elapsed);
sw.Reset();
sw.Start();
for (int i = 0; i < 1000000000; i++)
{
Interface obj = val2;
}
Console.WriteLine("class -> Interface:" + sw.Elapsed);
sw.Reset();
sw.Start();
for (int i = 0; i < 1000000000; i++)
{
object obj = val2;
}
Console.WriteLine("class -> object :" + sw.Elapsed);
sw.Reset();
sw.Start();
for (int i = 0; i < 1000000000; i++)
{
var obj = val2;
}
Console.WriteLine("class -> class :" + sw.Elapsed);
}
}
interface Interface
{
int MyProperty { get; set; }
}
struct Struct : Interface
{
public int MyProperty { get; set; }
}
class Class : Interface
{
public int MyProperty { get; set; }
}
以前のエントリで、Decimal型がDoubleに丸められてDBにInsertされることを書いた。
その後いろいろ調べた結果、フィールド型にNONEを指定していたのが原因だった。フィールド型をNONEで作成すると、NUMERICのaffinityが適用されるらしい。
http://www.sqlite.org/datatype3.html
Datatypes In SQLite Version 3 - 2.1 Determination Of Column Affinity
つまり、NONEのaffinityを適用されるためには、フィールドをBLOB型もしくは型指定なし(NONEの指定ではない)で作成しなくてはならないようだ。NONE指定すると、その他になり5番のNUMERICが適用されるわけだ。
ちなみに、↓がテーブルを作成する際に使用したSQLiteStudioのテーブル作成画面。
カラム追加のデフォルトデータタイプがNONEになっている。
そして、しっかりDDLにNONEと記載される。
引っかかった。
その後、データタイプをBlobで作成したところ、文字列として格納されることを確認した。
boxing(ボックス化)とは、値型を参照型であるobjectに変換する機能。
これにより、参照型であるobjectに値型を代入することができる。
ただし、パフォーマンス上の注意が必要。Generic型を使用することにより、boxingを防ぐことができる。
static void Main(string[] args)
{
var val = new Struct();
var val2 = new Class();
Stopwatch sw = Stopwatch.StartNew();
for (int i = 0; i < 1000000000; i++)
{
object obj = val;
}
Console.WriteLine("struct -> object:" + sw.Elapsed);
sw.Reset();
sw.Start();
for (int i = 0; i < 1000000000; i++)
{
var obj = val;
}
Console.WriteLine("struct -> struct:" + sw.Elapsed);
sw.Reset();
sw.Start();
for (int i = 0; i < 1000000000; i++)
{
object obj = val2;
}
Console.WriteLine("class -> object:" + sw.Elapsed);
sw.Reset();
sw.Start();
for (int i = 0; i < 1000000000; i++)
{
var obj = val2;
}
Console.WriteLine("class -> class :" + sw.Elapsed);
var list = new List<Struct>(100000000);
var list2 = new List<Class>(100000000);
sw.Reset();
sw.Start();
for (int i = 0; i < 100000000; i++)
{
list.Add(val);
}
Console.WriteLine("generic list <- struct:" + sw.Elapsed);
sw.Reset();
sw.Start();
for (int i = 0; i < 100000000; i++)
{
list2.Add(val2);
}
Console.WriteLine("generic list <- class :" + sw.Elapsed);
var list3 = new ArrayList(100000000);
var list4 = new ArrayList(100000000);
sw.Reset();
sw.Start();
for (int i = 0; i < 100000000; i++)
{
list3.Add(val);
}
Console.WriteLine("normal list <- struct:" + sw.Elapsed);
sw.Reset();
sw.Start();
for (int i = 0; i < 100000000; i++)
{
list4.Add(val2);
}
Console.WriteLine("normal list <- class :" + sw.Elapsed);
}
struct Struct
{
public int MyProperty { get; set; }
}
class Class
{
public int MyProperty { get; set; }
}
EffectiveC#を読んで、基本に立ち返ってみた。
(参照渡)
class Program
{
static void Main(string[] args)
{
var val = new Class();
var val2 = val; //参照渡し
val.MyProperty = 100;
//100 が表示される
Console.WriteLine(val2.MyProperty);
}
}
class Class
{
public int MyProperty { get; set; }
}
(値渡)
class Program
{
static void Main(string[] args)
{
var val = new Struct();
var val2 = val; //値渡し
val.MyProperty = 100;
//0 が表示される
Console.WriteLine(val2.MyProperty);
}
}
struct Struct
{
public int MyProperty { get; set; }
}
以前のエントリで、RDBにスキーマレスなデータを格納する方法を2種類検討した。
で、2種類の方法をうまくいいとこ取りした製品があるようだ。
http://www.infoq.com/jp/news/2009/04/friendfeed-schemaless-mySQL
検索を行うカラムのみ分解しておけ、ということなのね。
SQLiteはデータ型を指定せずにカラムを作成することがで、様々な型を適切(?)に格納することができる。
これは、オブジェクトをプロパティで分割して格納する際に非常に都合がよい。
というわけで、検証。
DecimalがDoubleとして格納されるために有効桁数が小さくなってしまってるので困りもの。どうにか回避方法はないものか。
static void Main(string[] args) { ConnectionStringSettings setting = ConfigurationManager.ConnectionStrings["SQLiteConnection"]; DbProviderFactory factory = DbProviderFactories.GetFactory(setting.ProviderName); using (DbConnection connection = factory.CreateConnection()) { connection.ConnectionString = setting.ConnectionString; connection.Open(); DbCommand insertCommand = connection.CreateCommand(); insertCommand.CommandText = "Insert into Data Values (?, ?, ?)"; { DbParameter p1 = factory.CreateParameter(); p1.DbType = DbType.Int32; insertCommand.Parameters.Add(p1); DbParameter p2 = factory.CreateParameter(); p2.DbType = DbType.String; insertCommand.Parameters.Add(p2); DbParameter p3 = factory.CreateParameter(); p3.DbType = DbType.Object; insertCommand.Parameters.Add(p3); } PropertyInfo[] props = typeof(Data).GetProperties(); var propDic = props.ToDictionary(x => x.Name); using (DbTransaction tran = connection.BeginTransaction()) { foreach (var data in GetPersonList().Take(2)) { insertCommand.Parameters[0].Value = data.IntVal; foreach (var prop in props) { insertCommand.Parameters[1].Value = prop.Name; insertCommand.Parameters[2].Value = prop.GetValue(data, null); insertCommand.ExecuteNonQuery(); } } tran.Commit(); } DbCommand selectCommand = connection.CreateCommand(); selectCommand.CommandText = "Select * From Data"; using (DbDataReader reader = selectCommand.ExecuteReader()) { while (reader.Read()) { var prop = propDic[reader.GetString(1)]; Console.Write(prop.Name + ":" + reader.GetFieldType(2) + ":"); if (prop.PropertyType == typeof(int)) { Console.WriteLine(reader.GetInt32(2)); } else if (prop.PropertyType == typeof(long)) { Console.WriteLine(reader.GetInt64(2)); } else if (prop.PropertyType == typeof(string)) { Console.WriteLine(reader.GetString(2)); } else if (prop.PropertyType == typeof(bool)) { Console.WriteLine(reader.GetBoolean(2)); } else if (prop.PropertyType == typeof(DateTime)) { Console.WriteLine(reader.GetDateTime(2)); } else if (prop.PropertyType == typeof(double)) { Console.WriteLine(reader.GetDouble(2)); } else if (prop.PropertyType == typeof(decimal)) { Console.WriteLine(reader.GetDecimal(2)); } } } } } static IEnumerable<Data> GetPersonList() { int id = 0; while (true) { yield return new Data { IntVal = id, LongVal = long.MaxValue, Text = Path.GetRandomFileName(), Flag = id % 2 == 0, Date = new DateTime(2010, 1, 1).AddDays(id), DoubleVal = double.MaxValue, DecimalVal = decimal.MaxValue, }; id++; } } private class Data { public int IntVal { get; set; } public long LongVal { get; set; } public string Text { get; set; } public bool Flag { get; set; } public DateTime Date { get; set; } public double DoubleVal { get; set; } public decimal DecimalVal { get; set; } }
CSVファイルに対してSQLを発行できるらしい。知らんかった。
サンプルはJScript。
var args = WScript.Arguments; var fullName = args(0); var fso = new ActiveXObject("Scripting.FileSystemObject"); var path = fso.GetParentFolderName(fullName); var basename = fso.GetBaseName(fullName); var filetype = fso.GetExtensionName(fullName); var con = new ActiveXObject("ADODB.Connection"); con.ConnectionString = "Provider=Microsoft.Jet.OLEDB.4.0;" + "Data Source=" + path + ";" + "Extended Properties=\"text;HDR=YES;FMT=Delimited;\";"; con.Open(); // CSVファイルから、SQLでクエリ var rs = con.Execute("select * from [" + basename + "#" + filetype + "] where sex = '男' order by Age;"); while (!rs.EOF) { var id = rs.Fields("ID"); var name = rs.Fields("Name"); var age = rs.Fields("Age"); var address = rs.Fields("Address"); WScript.Echo(id + "," + name + "," + age + "," + address); rs.MoveNext(); } rs.Close(); con.Close();
読み込んだファイルは以下の通り
ID,Name,Age,Address,Sex
1,佐藤,30,東京,女
2,鈴木,12,北海道,男
3,高橋,35,兵庫,男
4,田中,28,愛媛,女
5,渡辺,14,愛知,女
6,伊藤,22,佐賀,男
以前のエントリで、データをプロパティに分解して格納する方法、検索方法がどのように変わるか検討してみた。
通常
<Person>テーブル
ID | Name | Age | Address | Sex |
Select * From Person Where Age >= 20 And Age <= 29 And Sex='男'
20代男性を検索
分割
<Person>テーブル
ID | PropertyName | Value |
Select * From Person Where ID in (Select Id From Person Where PropertyName = 'Age' And Value >= 20 And Value <= 29 Intersect Select Id From Person Where PropertyName = 'Sex' And Value = '男')
AndはIntersectに、ORはUnionに変換すれば良さそう。でもパフォーマンス大丈夫か?
DbProviderFactoryから作成できる、DbDataSourceEnumerator。一度も使ったことがなかったので、使ってみた。
if (factory.CanCreateDataSourceEnumerator)
{
DbDataSourceEnumerator dse = factory.CreateDataSourceEnumerator();
dataGridView1.DataSource = dse.GetDataSources();
}
Commonに必要か?
前回の続き(?)。
Providerに依存しないコードを書いてみた。
ConnectionStringSettings css = ConfigurationManager.ConnectionStrings["MyConnection"];
DbProviderFactory factory = DbProviderFactories.GetFactory(css.ProviderName);
DbConnectionStringBuilder sb = factory.CreateConnectionStringBuilder();
sb.ConnectionString = css.ConnectionString;
//この辺はProviderに依存するよなぁ
sb.Add("Password", "xxxx");
using (DbConnection connection = factory.CreateConnection())
{
connection.ConnectionString = sb.ConnectionString;
connection.Open();
//factory.CreateCommand()でもいいけど、こっちの方が素直な気がする。
DbCommand command = connection.CreateCommand();
//ここも依存することが多いよね。
command.CommandText = "SELECT * FROM TABLE1 WHERE ID = ?";
DbParameter p = factory.CreateParameter();
p.DbType = DbType.Int32;
p.Value = 1;
command.Parameters.Add(p);
using (DbDataReader reader = command.ExecuteReader())
{
while (reader.Read())
{
reader.GetInt32(0);
}
}
}
DbProviderFactoryを使用することにより、データプロバイダに依存しない実装が可能。で、備忘録
使用可能なProvider一覧を取得DbProviderFactories.GetFactoryClasses();
ApplicationでDataProviderを追加したい場合は、app.configに設定することで追加可能。 例えば、PostgreSQLの場合は、↓の様に設定。
<system.data> <DbProviderFactories> <add name="Npgsql Data Provider" invariant="Npgsql" description=".Net Framework Data Provider for PostgreSQL" type="Npgsql.NpgsqlFactory, Npgsql, Version=2.0.8.0, Culture=neutral, PublicKeyToken=5d8b90d52f46fda7" /> </DbProviderFactories> </system.data>
app.configよりConnectionStringを取得してDbProviderFactoryを作成
ConnectionStringSettings css = ConfigurationManager.ConnectionStrings["MyConnection"]; DbProviderFactory factory = DbProviderFactories.GetFactory(css.ProviderName);
あとは、factory.CreateXXXXX()で作成し放題!
というわけで(?)、ファイルDBのSQLiteを評価中。
とりあえずパフォーマンス比較をやってみた
DB | Insert | Update | Delete | Select |
MDB | 21.32 | 32.07 | 27.18 | 46.05 |
SQLServer CE | 3.53 | 4.90 | 4.05 | 9.59 |
SQLite | 0.89 | 1.05 | 0.85 | 1.07 |
※処理
Insert×10万回
Update×10万回(主キーを条件に1レコード更新)
Select×10万回(主キーを条件に1レコード取得)
Delete×10万回(主キーを条件に1レコード削除)
テーブルは、2カラム(主キー、値)のみ。
※環境
OS | WIndows7 Ultimate(x64) |
CPU | Core-i7 920 |
Memory | 12G |
SQLiteはえー。
RDBにスキーマレスなデータを格納する方法を検討中
スキーマレスといえばXMLDBという気もするが、インストール不要のお手軽ファイルDBがよいので、やっぱりRDBになってしまう。格納方法として思いついたのが以下の2通り。
オブジェクトをシリアライズしてそのままBlob型のカラムに格納する方法
ID | Data |
1 | <?xml version="1.0"?> <名前 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <姓>山田</姓> <名>太郎</名> </名前> |
2 | <?xml version="1.0"?> <名前 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <姓>山田</姓> <名>次郎</名> </名前> |
プログラム側はC#なので、オブジェクトをXMLSerializerに突っ込んでDBに保存・読込を行えばよいので実装が楽そう。問題はどうやって検索するのか?
オブジェクトのプロパティー単位でレコードを追加する方法
ID | PropertyName | Value | Type |
1 | 姓 | 山田 | System.String |
1 | 名 | 太郎 | System.String |
2 | 姓 | 山田 | System.String |
2. | 名 | 次郎 | System.String |
こちらは検索はできそう。でもレコード数が多くなるので、Insertが大変そう。