2010年3月29日月曜日

UIパターン

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

2010年3月27日土曜日

Interfaceは参照型

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; }
}
結果
image

正解。

つまり、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; }
}

image

2010年3月25日木曜日

SQLite

以前のエントリで、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

  1. If the declared type contains the string "INT" then it is assigned INTEGER affinity.
  2. If the declared type of the column contains any of the strings "CHAR", "CLOB", or "TEXT" then that column has TEXT affinity. Notice that the type VARCHAR contains the string "CHAR" and is thus assigned TEXT affinity.
  3. If the declared type for a column contains the string "BLOB" or if no type is specified then the column has affinity NONE.
  4. If the declared type for a column contains any of the strings "REAL", "FLOA", or "DOUB" then the column has REAL affinity.
  5. Otherwise, the affinity is NUMERIC.

つまり、NONEのaffinityを適用されるためには、フィールドをBLOB型もしくは型指定なし(NONEの指定ではない)で作成しなくてはならないようだ。NONE指定すると、その他になり5番のNUMERICが適用されるわけだ。

ちなみに、↓がテーブルを作成する際に使用したSQLiteStudioのテーブル作成画面。
カラム追加のデフォルトデータタイプがNONEになっている。
image

そして、しっかりDDLにNONEと記載される。
image
引っかかった。

その後、データタイプをBlobで作成したところ、文字列として格納されることを確認した。

2010年3月22日月曜日

boxing(ボックス化)

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

image
違いは一目瞭然。

2010年3月21日日曜日

値型と参照型

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は参照型であり、代入は参照がコピーされる。なので、 valとVal2の指す実体は同じ。 
image

(値渡)

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

structは値型であり、代入はコピーが作成されるので、valを変更してもval2に影響はない。
image

2010年3月20日土曜日

本を買った


今更ながら、EffectiveC#。More~は持ってるけど一応。


@ITの記事を見てて、欲しくなりました。


Amazonに勧められました。

2010年3月19日金曜日

FriendFeed

以前のエントリで、RDBにスキーマレスなデータを格納する方法を2種類検討した。

で、2種類の方法をうまくいいとこ取りした製品があるようだ。
http://www.infoq.com/jp/news/2009/04/friendfeed-schemaless-mySQL

検索を行うカラムのみ分解しておけ、ということなのね。

2010年3月18日木曜日

SQLite-型のないカラム

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

2010年3月16日火曜日

CSVとSQL

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,佐賀,男

2010年3月15日月曜日

SQLクエリ

以前のエントリで、データをプロパティに分解して格納する方法、検索方法がどのように変わるか検討してみた。

通常
<Person>テーブル

ID Name Age Address Sex

Select * From Person Where Age >= 20 And Age <= 29 And Sex='男'

20代男性を検索

分割
<Person>テーブル


IDPropertyNameValue
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に変換すれば良さそう。でもパフォーマンス大丈夫か?

2010年3月14日日曜日

DbDataSourceEnumerator

DbProviderFactoryから作成できる、DbDataSourceEnumerator。一度も使ったことがなかったので、使ってみた。

if (factory.CanCreateDataSourceEnumerator)
{
DbDataSourceEnumerator dse = factory.CreateDataSourceEnumerator();
dataGridView1.DataSource = dse.GetDataSources();
}
なかなか、CanCreateDataSourceEnumeratorプロパティーがTrueにならない。
どうやら、SQLClient(SQLServer用)でしか使えないっぽい(?)

Commonに必要か?

2010年3月13日土曜日

DbProviderFactory(その2)

前回の続き(?)。
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

DbProviderFactoryを使用することにより、データプロバイダに依存しない実装が可能。で、備忘録

使用可能なProvider一覧を取得
DbProviderFactories.GetFactoryClasses();
machine.configより、DataProviderの情報を取得している。
戻り値はDataTable型なので、DataGridViewを使えば簡単に内容を確認することができる。
image

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>
そうすることで、DbFactoryが取得可能となる。
image

app.configよりConnectionStringを取得してDbProviderFactoryを作成 

ConnectionStringSettings css = ConfigurationManager.ConnectionStrings["MyConnection"];
DbProviderFactory factory = DbProviderFactories.GetFactory(css.ProviderName);
ConfigurationManagerからConnectionStringSettingsを取得。(System.Configuration.dllの参照が必要)
ConnectionStringSettingsよりProviderNameを取得し、DbProviderを作成

あとは、factory.CreateXXXXX()で作成し放題!

2010年3月11日木曜日

SQLite

というわけで(?)、ファイル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はえー。

2010年3月10日水曜日

スキーマレス

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が大変そう。

File DB

業務で使用するDBとして以下のような要件を満たすDBを探している

  • インストール不要
  • 共有フォルダにおいて数人に共有

現在のところMSAccess(MDB)なのだが、負荷をかけると結構重い。
他によい選択肢はないのだろうか?