シンプルで高速な構文解析ライブラリ「Parsec」を.NETで使う^^
Parsecって何?
Parsec は Haskell のモナドパーサコンビネータライブラリで、文脈依存文法や無限先読み文法を解析できますが、 LL文法と同等の性能を出します。
コンビネータパーサはプログラムの他の部分と同じプログラミング言語を使って書かれます。文法の形式化 (Yacc) とそこで使われるプログラミング言語 (C) のギャップがありません。ユーザはたったひとつの言語を学ぶだけで済みます!
参考参考
2004-07-30
http://alohakun.blog7.fc2.com/blog-entry-654.html
ライブドアブログ(livedoor Blog)| 読みたいブログが見つかる
これは魅力的><
僕が今欲しいのはまさにこれじゃないか!!!
で、いろんな言語に移植されてるみたいです。
.NETでも使えるやつ
NParsec、C#で使えるよー NParsec+Tutorial
FParsec、F#で使えるよー FParsec Documentation
さて、どっち使うか?
って、考えるまでもなく、FParsecですねw
NParsecのソースだと、魅力は半減(C#3.0だと少しは違うんでしょうけど、まぁ、無理やりですよね)
英語読めないので、機械と人の手を借りて読みましたが、
この先は誤った解釈などがあるかもしれませんが、悪しからず^^;
FParsecはコンビネータでできてる
コ、コンビネータのことは、Y Combinatorが凄すぎる! - yuji1982の日記の件で調べたけど、何とか理解した程度。。
SとかKとかI、、ですよねー><
と、濁しておしまい。
実際、活用してるかっつーと、全くしてなかったけど、でも、パーサコンビネータ見ると、コンビネータっておもしろいなー、と思う
環境構築から
必要なもの
- C#のビルド環境
- F#の1.9.4.15以降 download
- FParsec FParsec-downloadのhereから
準備手順
- FParsecCSとFParsecをビルド。
- F#のプロジェクトを追加して、参照パスに"-I ..\\..\\Build\\Debug -R FParsecCS.dll -R FParsec.dll"を追加。※-Iのパスは上の二つのdllがある場所
- F#のソースには、以下が必要
#light open FParsec.Primitives open FParsec.CharParser
これでOK。。と。
では、FParsecの勉強開始
パーサコンビネータは一つもしくは複数のパーサを引数として取り、「結合された」パーサを返す関数です。とな。
まさに、コンビネータってやつですね!
で、これが基本形のようです。
type GenParser<'a,'s> when 's :> IState = 's -> Reply<'a,'s>
「FParsecのすべての基本コンビネータと派生コンビネータは、この型のパーサが引数になる」
んー、わかるけど、、具体的に使わないとピンと来ないな^^;
結果はこんなやつらしいです。
type Reply<'a, 's> =
ConsumedOk of 'a * 's * ParserError |
ConsumedError of ParserError |
EmptyOk of 'a * 's * ParserError |
EmptyError of ParserError |
では、早速具体的な例から
type Parser<'a> = Parser<'a,unit> let simple: Parser<_> = parse {let! c = upper do! skipChar '#' return c}ふむふむ、、これを呼んでみる。
let main()= print_any(run simple "F#") System.Console.ReadKey(false) |> ignore結果 Success 'F' なるほど simpleってパーサに"F#"を渡すと、結果型のFになる。 この結果は、charパーサの結果型らしいです。
type ParserResult<'a> = Success of 'a | Failure of string * ParserErrorつまり、upperって言う大文字一文字のパーサと、skipCharって言う(たぶん検査だけして取得しない?)パーサーを 合成(結合?)して、simpleってパーサを作ったわけですね! で、このupperのような定義済みパーサを組み合わせて行くわけですね。
ちょっとだけ、じっくり見てみる
let simple: Parser<_> = parse {let! c = upper do! skipChar '#' return c}このparse {}って書き方は何でしょう?よく見ると、letじゃなくてlet!ですし、do!ってのもありますね。 これはcomputation expressionsと言う、シンタックスシュガーのようです。
let parseXY = parseX >>= fun x -> parseY >>= fun y -> preturn (x, y)こんな書き方を、
let parseXY = parse {let! x = parseX let! y = parseY return (x, y)}こうできるらしいです。
computation expressionsの前に元のやつを、もうちょっとだけ見てみる
まず単純な文字パーサに代えて考えてみるlet parseXY = parse {let! x = anyChar let! y = anyChar return (x, y)}文字を二つ(xとy)タプルで返すパーサになります。 結果は val it : ParserResult
let parseXY = anyChar >>= fun x -> anyChar >>= fun y ->preturn (x, y)>>=とpreturn >>= val (>>=): GenParser<'a,'s> -> ('a -> GenParser<'b,'s>) -> GenParser<'b,'s> preturn let preturn x = fun state -> EmptyOk x state NoError >>=でパーサーを結合して、最後に結果を取るためのパーサpreturnを使ってるってことですね。 んー、わかるにはわかりますが、、ややこいです。 そこでcomputation expressionsに頼るわけですね。
computation expressionsはモナドライク!?
let parseXY = parse {let! x = anyChar let! y = anyChar return (x, y)}わかり易くこう書けるのは、computation expressionsのおかげなんですが、これは何なのか? いげ太センセーが紹介してくれてます。 http://igeta.cocolog-nifty.com/blog/2008/02/computation_exp.html http://igeta.cocolog-nifty.com/blog/2008/04/maybe.html http://igeta.cocolog-nifty.com/blog/2008/04/f_monad.html 僕はまだイマイチ理解できてませんが、モナドライクなわけですね。 とりあえずは、、楽にしてくれる便利なものってことで^^;
ファイルから入力して解析する方法
runParserOnFile simple () "test.txt" System.Encoding.Default
FParsecのパーサーをいろいろ見る
FParsec Documentation 文字系 いろいろあるんですねー char, anyChar, anyCharOrNL, upper, lower, letter, digit
newline, unicodeNewline
whitespace, spaces, unicodeWhitespace
satisfy
string anyString
regex
manyChars, manySatisfy, manyCharsTill, manyTillString
skipped
いくつか試してみる
run (char 'a') "abc";; val it : ParserResult選択 <|>= Success 'a' run (anyChar) "abc";; val it : ParserResult = Success 'a' run (string "ab") "abc";; val it : ParserResult = Success "ab" run (satisfy (fun c->c<>'a')) "bcd";; val it : ParserResult = Success 'b' run (anyString 4) "abcde";; val it : ParserResult = Success "abcd"
run (char 'a' <|> char 'b') "abc";; val it : ParserResultchoice= Success 'a' run (char 'a' <|> char 'b') "bc";; val it : ParserResult = Success 'b'
run (choice [(char 'a');(char 'b')]) "abc";; val it : ParserResultchoice [p1; p2; p3; ...] は p1 <|> p2 <|> p3 <|> と同じ。 でもchoiceの方が効率がいいらしい(?) .>>、>>. .の付いてる側を取得して、付いてない方は消費するだけ?でいいのかな= Success 'a'
run (char 'a' .>> char 'b') "abc";; val it : ParserResultとりあえず、おk 繰り返し many、skipMany= Success 'a' run (char 'a' >>. char 'b') "abc";; val it : ParserResult = Success 'b'
run (many digit) "123abc";; val it : ParserResultなるほど、数字を複数処理したわけですね、、と言うことは間に文字を入れると、、= Success ['1'; '2'; '3']
run (many digit) "12a3bc" >val it : ParserResultうんうん、おk ※前から思ってるけど、カナ打ちの僕が「おk」って、、違いますよねw= Success ['1'; '2']
run (skipMany (char 'a') >>. char 'b') "aaaabc";; val it : ParserResultmanyTill、skipManyTill= Success 'b'
run (manyTill (char 'a') (char 'b')) "aaaabca";; val it : ParserResultおそらくbは消費してますよね?= Success ['a'; 'a'; 'a'; 'a'] run (skipManyTill (char 'a') (char 'b')) "aaaabca";; val it : ParserResult = Success ()
run ( (skipManyTill (char 'a') (char 'b') ) >>.char 'c') "aaaabca";; val it : ParserResultうん、おk manyと文字の組み合わせ manyChars、manySatisfy= Success 'c'
run (manyChars (char 'a')) "aaba";; val it : ParserResultセパレータで区切られた解析 sepBy, endBy sepEndBy= Success "aa" run (manySatisfy (fun c->c<>'z')) "abczdef";; val it : ParserResult = Success "abc"
run (sepBy (anyString 2) (char ';')) "ab;cd;ef";; val it : ParserResultsepEndByは最後にセパレータがあっても良い。sepByだとエラー。 run (endBy (manyChars (satisfy (fun c->c<>';'))) (char ';')) "abcdef;";; val it : ParserResult= Success ["ab"; "cd"; "ef"] run (sepEndBy (anyString 2) (char ';')) "ab;cd;ef;";; val it : ParserResult = Success ["ab"; "cd"; "ef"]
>> |
run ( (lookAhead (string "ab") ) >>. (manyChars anyChar) ) "abc";; val it : ParserResult追記 between= Success "abc"
> run (between (char '(') (char ')') (regex "[^)]+")) "(abc)";; val it : ParserResult= Success "abc"
脳が痛がってるので、今回はここまで>< (たぶん追記します)
まだ理解してないものメモ chainl1最後に注意!!
FParsec Documentationで、以下の"ような"ことが書かれてるので注意です! このページのFParsecは未リリース版です。 かなり総合的なテストは行われてますが、まだバグがあると思われるので、 もし製品環境で使う場合は、徹底的にテストする必要がありますよ。WPFで「検索結果のハイライト表示機能」ってどう作ればいいんだろう?
少し前から考えてるけど、いまいちピンと来ない。
ドキュメントページに限らず、○○(商品などやブクマ)リストのページとか、検索結果ページとか、いろいろな場面での利用を想定した、ハイライト機能が欲しい。
ブラウザベースの世界ではあたりまえのあれ。
.NETアプリに、ウェブ風の簡易検索やGMailフィルターのようなものを実装してみる
F#とC#で、簡易検索用の動的LINQ生成をやってみました。
こんな風に使います。
//適当なデータ
var data = new[]{
new { Name="yuji1982",Age = 26,Like="F#,Visual Studio", Man=true, Luckyday = new DateTime(2008,1,1) },
new { Name="amachang",Age = 26,Like="JS,javascript,1000sp",Man=true, Luckyday = new DateTime(2008,7,1)},
new { Name="adeton",Age = 20,Like="C#",Man=true, Luckyday = new DateTime(2008,12,1)},
new { Name="zio3",Age = 20,Like="C#,x-box360,XNA Game Studio,Silverlight",Man=true, Luckyday = new DateTime(2008,8,2)},
new { Name="no-0",Age = 126 ,Like="Visual Basic, Expression Studio",Man=false, Luckyday = new DateTime(2008,10,3)},
new { Name="no-1",Age = 0 ,Like="a26a",Man=false, Luckyday = new DateTime(2008,2,1)},
new { Name="no-2",Age = -9,Like="abc",Man=false, Luckyday = new DateTime(2008,3,15)},
new { Name="no-3",Age = 99,Like="Visual Studio",Man=false, Luckyday = new DateTime(2008,12,1)},
new { Name="no-9",Age = 10,Like="C言語",Man=false, Luckyday = new DateTime(2008,4,1)},
new { Name="no-10",Age = 21,Like="javascript",Man=false, Luckyday = new DateTime(2008,4,2)},
new { Name="no-11",Age = 27,Like="C#",Man=true, Luckyday = new DateTime(2008,4,2)},
new { Name="no-12",Age = 23,Like="C",Man=false, Luckyday = new DateTime(2008,4,3)},
};//クエリ検索の実行
var result = data.Search("(C# or F# or javascript) 20..30 2008/1/1..2008/10/1 Name:-no 1");//結果を表示してみる。
foreach (var item in result)
{
Console.WriteLine(item);
}
結果
{ Name = yuji1982, Age = 26, Like = F#,Visual Studio, Man = True, Luckyday = 2008/01/01 0:00:00 }
{ Name = amachang, Age = 26, Like = JS,javascript,1000sp, Man = True, Luckyday = 2008/07/01 0:00:00 }
このクエリは以下の内容のand検索になっています。
・どこかにC#かF#かjavascriptが含まれる
・数値が20から30の範囲 (数値型プロパティのどれでも)
・日付が2008/01/01から2008/10/01 (DateTime型プロパティのどれでも)
・Nameにnoが含まれない
・数値が1か文字列に1が含まれる
どうやって動いてるか
検索クエリ
↓
解析(Fsyacc/Fslex)
↓
AST(F#)
↓
Expressionに変換(F#) ← 検索対象指定用のデータ(プロパティ名一覧)
↓
LINQで使用
これをC#でラップして呼べるようにしただけです
何に使えるか?
- 簡易ツールに使える
とりあえずは、簡単なツール(クライアントツール)とかで使えるかなーと思う。
自分用はてブツールとか、twitterクライアントとか、、パフォーマンスが要求されない部分ならとりあえずあれば便利?
まだ、簡単なパターンのテストしかできてないけど、単純な式ツリーだから、大丈夫だと思う。
Linq to Object、Linq to SQLの両方に気軽に使えるのが、LinqのExpressionで構築する一番の良さだと思ってる。
と、言うことで、データベースにも使えるので、webサービスでも使えますね^^
- GMailフィルターのようなものを簡単に作れる
文字列に名前を付けて保存するだけで、あの便利なGMailフィルターを真似できますよね^^
- WebService(Soap,REST)を作るのが楽^^
引数が文字列一つで済むから楽>< (この理由はLinq to RESTがリリースされれば関係ないけどw)
クエリに使える演算子について
演算子 | 説明 | 例 |
---|---|---|
AND and & | AND(かつ)検索 | C# F# C#とF#含まれる ※スペースを入れてもANDになる |
OR or | | OR(または)検索 | C# or F# C#かF#が含まれる |
() (括弧) |
グループ化 | (C# or F#) (2005 or 2008) |
Not not ! | 否定 | !C# C#が含まれない |
- (ハイフン) |
除外 | -C# C#が含まれない ※基本は否定と同じ、違いはグループ化した時の動作 -(a b)は!(a or b)と同じ 数値の前に使うとマイナスとして判断されるので-(9)などにする必要がある |
ターゲット名: | 検索対象のプロパティを指定する ターゲット名はプロパティ名の別名として設定できる ターゲットを指定しないものはAllとして設定したプロパティ全てが対象になる。 |
名前:yuji Nameプロパティにyujiが含まれる |
"" (引用符) |
フレーズ検索 | "Visual Studio" "x-box" とかに使う。 また、数値9を文字"9"にするなど |
{} (中括弧) |
ORでグループ化する | {C# F#} (C# or F#)と同じ |
.. | 範囲指定 | 20..30 20から30 ※数値か日付にのみに使える。 |
< > <= >= | 関係演算子(って言うのかな^^;) | <20 20未満 |
is:ターゲット名 | bool演算 | is:Man Manプロパティがtrueかどうか |
数値と日付の扱いについて
- 数値と文字の違い
数値9で検索すると、9が含まれる文字列と数値9にヒットする。
文字"9"で検索すると、9が含まれる文字列と9が含まれる数値(19,-90とか)にヒットする。
- 日付のフォーマット
yyyy/MM/ddとyy/MM/ddを想定してる(Fslexの正規表現がうまくいってないので、適当ですが・・・)
yy/MMやMM/ddって対応したいけど、どうやろうかなー
課題
- エラー処理とかまだ全くできてない。
- システム変数のようなものを実装したら便利かなーとか(@NowDateみたいな)
ちょっとコードがひどいすぎるからリストラクチャリングするムリ^^;諦め- Google Bookmarksとはてブの差分を出してみる。その1 - yuji1982の日記の続きで利用する。(フィルターとか作ってみる)
おわり
F#の練習のつもりでやってたから、コードがぐちゃぐちゃ^^;コメントになってるところがありすぎるw
ソースはあとでうpします。とりあえず、dll上げときますので、試してみる方はどうぞ^^
(追記)ソース晒しておきます。
ソースダウンロード(DynamicSearch.zip)
DLLダウンロード(DynamicSearchDLL.zip)
※.NET Fx3.5とF#が必要です。
F#のダウンロードは以下のページから。ライセンスもこのページから見てください。
http://research.microsoft.com/fsharp/release.aspx
F#のDLLが数個あればいいので、zip版からサクっと使うだけでいいんだけど、
非商用なら、一緒に配っちゃってもいいのかなー・・・(必死で読んでもよくわからん^^;
Fslexは量指定子使えない?
量指定子が使えない(と思う)
なぜか以下はコンパイルは通るけど(実際使えるかどうか、どう動くかは未検証)
['0'-'9']{2,4} { ... } |
以下はエラー
['0'-'9']{2,4}['0'-'9']{2,4} { ... } |
当然、以下の二つもエラー
let date= ['0'-'9']{2,4}
let digit = ['0'-'9']
let date = digit{2,4}
量指定子ないのは辛すぎる><
と言うか、正規表現が全般的に使いづらい><
.NETと同じ先読みと後読みがない(?)のも辛い。
マクロ定義のようなことできないのもめんどい・・
もっとスマートに書く方法はないのかなー
何より
[^' ' '\t' '\r' '\n']
これはやっぱり
[^ \t\r\n]
こうだよね(´Д`)
C#3.0の匿名型を使ってて、ふと思ったこと
匿名型はこんな風に書ける
プロパティ名を書いて、値を指定するだけ。
var me = new { Name = "yuji", Age = 26, Like = "C#" };
プロパティ名は省略できる。
省略すると同じプロパティ名になる。
var me2 = new { Name = me.Name , me.Age };//Ageが省略したやつ
もちろん、ローカル変数でもおk
var name = "yuji";
var me3 = new { name };
だったら、こうしたくない?
var Name = "yuji";
var me4 = new { Name };
ローカル変数をPascal 形式で書きましたが何か?(^ω^)
駄目ですか、そうですか……
Select文の中とかで書きたくならないですか?
var data = new[]
{
new { Name = "yuji", Age = 26, Life = 100},
new { Name = "adeton", Age = 3, Life = 9999 }
};var sum4 = data.GroupBy(d => d.Age/10)
.Select(gp =>
{
var Life = gp.Sum(d => d.Life);
return new
{
Life,
Title = string.Format("{0}代は、合計{1}です。", gp.Key*10, Life),
};
});
ないですか、そうですか、そうですね、名前書くぐらいサボんなって話ですよね。
そもそも、同じ名前にならない事の方が多い(正しい)ですよね。(↑だとTotalLifeとか?)
LINQでたくさん帳票作ってたら、少しでも手抜きしたくなっただけですorz
Google Bookmarksとはてブの差分を出してみる。その1
Google Bookmarkとはてブ両方使ってるけど、全部はてブに移そうと思ったので(思いつきで特に理由もないけど)
差分を出してみることに。
F#の練習に丁度いいと思ったけど、フレームワークを使うのに慣れてないので、まずはC#でやってみる。
データを取得
C#ではてなブックマークのポストができなくて参ってる - ブログ執筆中を参考に。
public static string Dump(string username, string password)
{
HttpWebRequest req = (HttpWebRequest)WebRequest.Create("http://b.hatena.ne.jp/dump");
req.AddHeader(username,password,"GET");var res = req.GetResponse();
var sr = new StreamReader(res.GetResponseStream());
var xml = sr.ReadToEnd();
return xml;
}
とりあえず、細かいことは気にせず適当。AddHeaderは割愛^^;
- google bookmarks
やり方がわかりませんorz
いろいろ追ったら混乱してきたので、今度やることにしてやめました。
とりあえず、手動で取得して次に進む><;
まず、googleからパースしてみる
はてブはxmlだけど、googleはhtmlだから面倒だなー……と思いつつ正規表現で遊びながらウニウニやるのは意外に楽しい^^
google bookmarksのフォーマットはこんな感じです。
<DT><H3 ADD_DATE="1198640344791968">Wiiリモコン</H3>
<DL><p>
<DT><A ADD_DATE="1198640899163631" HREF="http://japanese.engadget.com/2007/12/22/wii-opera-sdk/">Wii インターネットチャンネル 非公式SDK公開 - Engadget Japanese</A>
<DT><A ADD_DATE="1198640546058701" HREF="http://www.nintendo.co.jp/wii/topics/interview/vol4/ailive.html">AiLive LiveMove 紹介ムービー - Wii</A>
<DT><A ADD_DATE="1198640795500908" HREF="http://japanese.engadget.com/2007/12/20/multi-touch-pen-tablet-using-wiimote/">Wiiリモコンで今度はマルチペンタブレット - Engadget Japanese</A>
ソースはこんな感じになりました。
var regex = new Regex("(<H3.+>(?<tag>.+)</H3>)|(<A +ADD_DATE=\"(?<utc>[0-9]{13}).+\" +HREF=\"(?<url>[^\"]+)\" *>(?<title>[^<>]+)</A>)", RegexOptions.Compiled);
var matchies = htmlTextLines
.Select(str => regex.Match(str))
.Where(m => m.Success);
return matchies.ScanSelect(
string.Empty,
(match, tag) =>
Tuple.Create(
new
{
Tag = tag = match.GetGroup("tag") ?? tag,
Date = match.GetGroup("utc").ParseLong().IsNotNull(l=>l.ToUtc()),
Url = match.GetGroup("url"),
Title = match.GetGroup("title"),
},
tag)
)
.Where(bm => bm.Url != null)
.GroupBy(bm=>bm.Url)
.Select(gp=>
new BookMarkBase.BmItem
{
Tags = gp.Select(bm=>bm.Tag).ToArray(),
AddDate = gp.First().Date ?? DateTime.MinValue,
Url = gp.Key,
Title = gp.First().Title
})
.ToArray();
短くしたいので、適当に拡張メソッドを作る。
- GetGroups
Matchから結果取り出す、失敗したらnullにしてるだけ
- ParseLong
TryParseして失敗したらnullにするだけ。だから結果はNullable
- IsNotNull
nullじゃなかったら、結果を受け取って変換する。nullならnull返すから結果はNullable
- ToUtc
longからUtc作ってる
意外だったんですが、フレームワークにないんですね><
あと、google bookmarkのADD_DATEがマイクロ秒(?)なのを知らずに何度も失敗しました。(この情報ってどこにあるんでしょう??)
TimeSpanを作るのに、ミリ秒の方が楽だったから13桁で切って手抜き……
ScanSelectってのを作ってみたが……いろいろ微妙
タグをどう扱うか
一行ずつ見るにはLinqのSelectじゃ扱えないので、F#のscanを真似することに。(そもそもscanが適切かはすごく微妙ですが^^;)
で、scanで、いいかなーと思ったけど、型を推論させるために、最初に匿名型を入れるのが意外に面倒……
結果の値と、次でも使う値(タグ)を分けることに
既に、scanの概念からズレてるのに、強引にこういうことするの良くないのか悩む^^;
分ける方法は、配列か、匿名型か……
配列は何となく嫌。匿名型にすると、推論の問題で逆戻り。
こうなってくると、やはりTupleが欲しい。
C# 3.0 Supplemental Library: Achiral - NyaRuRuが地球にいたころから頂きました
NyaRuRuさんのAchiralは本当にいろいろと参考になります^^
(あれもこれも真似しまくってます>< Ms-PLありがとうございます>NyaRuRuさん)
で、Tupleを生成するときも型を書きたくないので、Createメソッド作って推論させる。
やっぱTupleがあるし、F#の方がいいなー、なんて思ったり。。
ただ、C#で書いたクラスの呼び方はあまり好きじゃないけど・・・
F# at Microsoft Research - Microsoft Research
落とし所としては、問題ない(むしろよくできてる)んだろうけど、やっぱりちょっと違和感が・・・
はてブをパースしてみる。
xmlはこんな感じ。
ソースはこれだけ
<entry>
<title>応答: Speech Server 2007 を使用した音声応答ワークフロー</title>
<link rel="related" type="text/html" href="http://msdn2.microsoft.com/ja-jp/magazine/cc500549.aspx?pr=blog" />
<link rel="alternate" type="text/html" href="http://b.hatena.ne.jp/yuji1982/20080424#bookmark-8354393" />
<issued>2008-04-24T19:37:39+09:00</issued>
<id>tag:hatena.ne.jp,2005:bookmark-yuji1982-8354393</id>
<summary type="text/plain">Speech Server 2007とWFでごにょごょ。まーやんないだろうけど・・・</summary>
<dc:subject>Speech Server 2007</dc:subject>
<dc:subject>WF</dc:subject>
</entry>
var doc = XDocument.Load(new StringReader(xml));
var ns = XNamespace.Get("http://purl.org/atom/ns#");
var dc = XNamespace.Get("http://purl.org/dc/elements/1.1/");
return doc.Descendants(ns + "entry").Select(
el =>
new BmItem
{
Title = el.Element(ns + "title").Value,
Url = el.Element(ns + "link").Attribute("href").Value,
AddDate = DateTime.Parse(el.Element(ns + "issued").Value),
Tags = el.Elements(dc + "subject").Select(te=>te.Value).ToArray()
}
)
.ToArray();
XMLは楽でいい><
と言うより、
Visual Studio2008ではDataSetとTableAdapterは別プロジェクトにできる
できるべきなのに何でできないのかなー、と思ってたら2008からできたんですね><;*1
何か怪しいプロパティあるなーとは思ってたのに前回の仕事じゃ気づかなかったなーorz
おまけ
「この機能とpartial、サービスが参照DLL内の型を利用する機能」などの機能がなくて、どうしていいかわからずに、失敗してるのを見た過去の例
※ないなりの設計ができてないと言う意味です^^;
第三位 サーバー側とクライアント側で同じロジックを実装ある。
第二位 サービス参照で生成したデータセットソース内の名前空間を都度変更して同じ型とする。
#.NETの型の決定を悪用…
第一位 サービスで設計されているのをなかったことにして、クライアントからこっそりデータベースにアクセス
#たまたま運用がイントラになったからとか…
こんなソースは二度と見たくない><
早く2008が普及するといいなー^^
*1:もしかして2005からあったり・・・ないよね?