kazuk は null に触れてしまった

C# / .NET 系技術ネタ縛りでお送りしております

月別アーカイブ: 11月 2010

コードは伸びるよどこまでも


寒くなってきましたねー というわけで C#たんの寒さから入る流れを踏襲してみます。

っていうのもフレームワークを使ったアプリケーション開発っていうのが今回の話題。

C#たんと学ぶ/わりと硬派なソフトウェア開発講座 第1回「C#でできること」

の2ページめ、今時のアプリケーション開発に欠かせない「フレームワーク」

 

フレームワークって便利ですよねー、本当に。 LINQ とかで簡単にデータアクセスとかして簡単にアプリケーション書けちゃいますよねー。

 

アプリケーションで顧客データを参照するなら

var blackListedCustomers = from cust in db.Customers where cust.BlackListed select cust;

でブラックリスト入りしてるお客の一覧を取得できると。

んでご会計ページとかではこれを元にブラックリスト入りしてたらなんかするって訳で、こんな事になるでしょう。

var blackListedCustomers = from cust in db.Customers where cust.BlackListed select cust;
if( blackListedCustomers.Any( c=>c.UserId == session[“UserId”] ) )
{
     Mail.Send(
        new MailMessage( config.GetAdminMailAddr(), “Blacklisted customer notice “,
            string.Format( “要注意購入者情報 {0}”, blackListedCustomers.Where( c=>c.UserId==session[“UserId”] ) ) ) );
}

これはアプリケーションコードとしては非常にまっとうなんだが、いかんせん Any と Where の2回クエリが実行されるとか起こる。

まぁね、要注意人物なんてそんなに居ないだろーという訳で最初の blackListedCustomers に ToList()って書いておけば良いよね。

 

良いのよ、前提を解っていて、それに対処するって事は。でも駄目なのよ、前提を知ってるのは、「そんなに居ないだろー」って前提は少なくとも完全に駄目。

「Any とWhereで2回実行される」のが駄目ってのも前提を知ってるって事で言えば駄目、なんでっていうと「フレームワーク」が隠ぺいしている機能についての詳細だから。

むにゃむにゃあって、フレームワークを使っても結局は漏れのある抽象化にしかならないケースが多い。中を気にするのは本来は駄目な話、フレームワークは中の事を気にせずにものごとができる様に作られなきゃいけないはずなんだが、それが実現されてるなんて「夢見てんなよ」だったりする。

夢なら夢で諦めてフレームワークの中の事をちっと気にしてプログラム書いてあげるのはやぶさかではないって事で諦めよう。

 

んな訳で、先に進む。

データを取ってキャッシュしないで済ませられるのは小学生までらしいのでキャッシュする事にしよう。

ASP.NETなんでAPIとしては System.Web.Caching あたりにある物を使おう。

ここでストレージフレームワークと、Webフレームワークの境界線のあたりの不毛地帯を通る必要がある。LINQはクエリ言語であり、その実装が「ストレージアクセスフレームワーク」で隠ぺい範囲は「ストレージアクセス」なんだから「キャッシュなんて知りません」は至極当然まっとうな結果だったりするんだが、本来簡単なはずの話がどこまで複雑になるか見てみるとげっそりする。

var blackListedCustomers = from cust in db.Customers where cust.BlackListed select cust;

の一行をちゃんとキャッシュ意識して書くとこーなるってのが以下の通り。

var cache = HttpContext.Current.Cache;
string cacheKey = “BlacklistedCustomers”;
List<Customer> blackListedCustomers;
blackListedCustomers = cache.Get(cacheKey);
if( blackListedCustomers==null )
{
    blackListedCustomers = (from cust in db.Customers where cust.BlackListed select cust).ToList();
    lock( cache )
    {
        List<Customer> ready = cache.Get(cacheKey);
        if( ready ==null )
        {
            cache.Add( cacheKey,blackListedCustomers ); 
        } 
        else
        {
            blackListedCustomers = ready;
        }
    }
}

check-lock-check (check-execute-lock-check-update) はちゃんとやらずにあちこちでロックしてるとアプリケーションがガチガチとブロッキングするのでキャッシュ制御では必須ね、この辺ちゃーんと複数スレッドでのコード実行を意識できる人でないと忘れがち。

っておい、「アプリケーションプログラマはキャッシュだけでなく並行制御もちゃんと知らなきゃ駄目なのかい?」って事になるし、本来アプリケーションでやりたいと思ってる事よりこっちの方が行数があるってのも問題で、アプリケーションコードなんてのは実際にやりたい事がまばらにあるだけで8割は色々なフレームワークが「自分の範疇じゃないし、そんな事しったこっちゃねーよ」と言った事の掃溜めと言っていいぐらいだったりするのはむしろ普通な事なんだ。(LINQ to SQL 固有の CompiledQuery にするとか、Entity Framework にも固有の事象とかその辺は今回入れてないんだよ)

 

とりあえず、以下のリンクに飛んで、スクロールバーの状態だけでも見てもらえれば、クエリの実行にまつわるどれだけの事が隠ぺいされたかは解る。

.NET アプリケーションのパフォーマンスとスケーラビリティの向上 – 第 12 章 「ADO.NET パフォーマンスの向上」

昔(2005年当時)はデータアクセスにこんだけ注意事項があった、がその辺全部をLINQとクエリプロバイダの中で解決してくれる(はず!)。(実際は LINQ to SQLのクエリ実行ではここに書かれたいくつかのパフォーマンス上のtipsは実行されていない)

しかし、アプリケーション全体ではまだまだ新しい注意事項がどんどん出てきているし、キリガ無い。その上に RIAやRESTだ。

タイトルの通り。

フレームワーク?素晴らしいものかもしれんが、まだまだ夢のような話だよねって感じであります。

広告

TIPS: VS2010 でHTMLタグの選択のキーボードショートカットを作る


前回のポストでタグの整形規則を良い感じにしたらデザインビュー上でテキストをパタパタと打っていける便利な環境ができたわけだったりしますけど。

Heading のいくつかとか、pとか、キーボードからショートカットが無いのが非常に残念な今日この頃。ショートカットが無いだけならまだしも単発コマンドも無いんで、キーボードへのマッピングもやりようが無く…

てな訳で、マクロのレコーディングからやってみたら結構さっくりできたので公開。

マクロエクスプローラでこんなモジュールを登録して

Option Strict Off
Option Explicit Off
Imports System
Imports EnvDTE
Imports EnvDTE80
Imports EnvDTE90
Imports EnvDTE90a
Imports EnvDTE100
Imports System.Diagnostics

Public Module HtmlEditorExtention
    Sub Para()
        DTE.Commands.Raise(“{1496A755-94DE-11D0-8C3F-00C04FC2AAE2}”, 245, “段落 <p>”, Customout)

    End Sub
    Sub Heading1()
        DTE.Commands.Raise(“{1496A755-94DE-11D0-8C3F-00C04FC2AAE2}”, 245, “見出し 1 <h1>”, Customout)

    End Sub

    Sub Heading2()
        DTE.Commands.Raise(“{1496A755-94DE-11D0-8C3F-00C04FC2AAE2}”, 245, “見出し 2 <h2>”, Customout)

    End Sub

    Sub Heading3()
        DTE.Commands.Raise(“{1496A755-94DE-11D0-8C3F-00C04FC2AAE2}”, 245, “見出し 3 <h3>”, Customout)

    End Sub

    Sub Heading4()
        DTE.Commands.Raise(“{1496A755-94DE-11D0-8C3F-00C04FC2AAE2}”, 245, “見出し 4 <h4>”, Customout)

    End Sub
End Module

ツールのオプションからキーボードでマクロと結合!

20101111191200

どういうマッピングにするかはお好みですが、自分は Word も見出し1~4をCtrl+1~Ctrl+4にしてる人なんで当然に統一して登録。(標準段落はCtrl+スペースです)

アウトライン的なタグとかで良く選ぶタグはショートカットがあると素晴らしく便利ですよと。

HTMLがきれいにできるのとそれをパタパタ効率よくできるって面でこれでVS最強であります。

Expression Webが残念な件については触れないでおこう。

TIPS: Visual Studio 2010のHTMLの改行規則を変更する


デフォルトだと p タグの中身まで前後で改行されたりして結構だらだら長いHTMLになってしまってカッコ悪い。

 

ツールのオプションからテキストエディターのHTML、タグ指定オプションを変更する。

20101111133625

20101111133936

クライアントHTMLタグの配下になぜかpが無いので「新しいタグ」からpを追加して改行を「前後」にする。

20101111134103

デフォルトは「開始タグの前と内部、および終了タグの後」となっている。

brもなぜか無いんだが、これは「タグの後」で改行するように設定するとすごく見やすくなる。

 

li とか、基本的に中身が単純な物とかは改行を「前後」にした方が基本として見やすいHTMLになると思うよ。

各タグの色付けなんか替えるとコードの表示もカラフルになるんで、気にしたくないタグをグレーにするとかするのも結構便利。お試し頂きたい。

 

設定変えたら、編集メニューから、詳細、ドキュメントのフォーマットで一気に適用だ!

ではでは、きれいなHTML書こうぜ!

dbml のAssociationの逆参照を得る


先日のttソースの貼り付けで酷い目にあったから。コード貼り付けのテストだったりする。

    XElement GetReverseAssociation( XElement assoc )
    {
        var typeElement = (from db in dbmlDoc.Elements( dbmlNs+"Database" )
                          from table in db.Elements( dbmlNs+"Table" )
                          from type in table.Elements( dbmlNs+"Type" )
                          where type.Attribute("Name").Value==assoc.Attribute("Type").Value
                          select type).Single();
        return (from revAssoc in typeElement.Elements( dbmlNs+"Association" )
                where revAssoc.Attribute("Name").Value==assoc.Attribute("Name").Value
                select revAssoc ).Single();
    }

 

って感じで dbml (LINQ to SQL のメタデータ)から参照に対する逆参照を得られる。

    XDocument dbmlDoc;
    XNamespace dbmlNs;
をフィールドとして用意する事、それぞれ以下の様な初期化をする必要がある。
    dbmlNs = "http://schemas.microsoft.com/linqtosql/dbml/2007";
    dbmlDoc = XDocument.Load( dbmlFilePath );

パラメータの XElement assoc には dbml内の Association 要素を指す XElement を渡してね!

テストだから崩れても泣かない。

POCO Entities with LINQ to SQL


dbml ファイルはノーマルなXMLなんで、T4を介して LINQ to XML で舐めて生成してあげればおっけー

下のttで POCO エンティティとのコピーコンストラクタを LINQ to SQL クラスに partial part で足せるよっと。

dbmlで定義されたクラスに基づいてPOCOクラスを作るとか発展は皆さまにお任せ。

 

<#@ template debug="false" hostspecific="true" language="C#" #>
<#@ assembly name="System.Core.dll" #>
<#@ assembly name="System.Xml.dll" #>
<#@ assembly name="System.Xml.Linq.dll" #>
<#@ import namespace="System.Collections.Generic"#>
<#@ import namespace="System.IO" #>
<#@ import namespace="System.Xml.Linq" #>
<#@ import namespace="System.Linq" #> <#
/************************************** configurations */
string dbmlFilePath = Path.Combine( Path.GetDirectoryName( Host.TemplateFile ), "Northwnd.dbml" );

PocoEntityTypeNames = new Dictionary<string,string> {
/* example
{ "L2SClassName", "PocoClassName" } */
};
FallbackPocoEntityTypeName = s => s+"Entity";

/************************************** implementation */
XNamespace ns = "http://schemas.microsoft.com/linqtosql/dbml/2007&quot;;
var dbmlDoc = XDocument.Load( dbmlFilePath );
var tables =
from db in dbmlDoc.Elements( ns+"Database" )
from table in db.Elements( ns+"Table" )
select table;

Generate( tables, table=>{
var typeElement =table.Element(ns+"Type");
var typeNameAttr = typeElement.Attribute("Name");
#>
public partial class <#=typeNameAttr.Value#>
{
#region CopyCtor
public <#=typeNameAttr.Value#>( <#=GetPocoEntityTypeName(typeNameAttr.Value)#> source )
{
<# Generate( typeElement.Elements(ns+"Column"), col=>{#>
this.<#=col.Attribute("Name").Value#> = source.<#=col.Attribute("Name").Value#>;
<#});
#>
}
#endregion
}
<#});

#>
<#+
Dictionary<string,string> PocoEntityTypeNames;
Func<string,string> FallbackPocoEntityTypeName;

string GetPocoEntityTypeName( string typeName )
{
string pocoTypeName;
if( PocoEntityTypeNames.TryGetValue( typeName, out pocoTypeName ) )
{
return pocoTypeName;
}
return FallbackPocoEntityTypeName( typeName );
}

void Generate<T>( IEnumerable<T> collection, Action<T> generateAction )
{
foreach( T item in collection )
{
generateAction( item );
}
}
#>