けさらんぱさらん

方向性は定めず、ただ思いつくままに

AutoMapperではまった件

また間が空いてしまった。
継続できないマンです。

ちょっとAutoMapperではまったことをメモっておきます。
AutoMapperは、簡単な設定で異なるクラスのオブジェクトをマップしてくれる便利なライブラリです。

こんな感じ

Mapper.CreateMap<Custormer, CustomerDTO>();
var dto = Mapper.Map<Custormer, CustomerDTO>(new Custormer { Id = 1 });

これだけでCustomerDTOのIdプロパティに1が代入されます。
ちなみにCreateMapメソッドはアプリケーションの中で一度呼べばOKです。
処理ごとに呼ぶと遅くなるので注意。
これも知らなくてはまったわけですが・・・

例えばCustomerクラスはこんな構造です。

class Custormer
{
    public int Id { get; set; }
    public Name Name { get; set; }
}

Customerクラスが持っているNameクラスはこんな感じ

class Name
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

そしてMapされる側のCustomerDTOはこんな感じ

class CustomerDTO
{
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

持っている情報は同じですが多少構造が異なります。
こうゆう場合AutoMaperでは

Mapper.CreateMap<Custormer, CustomerDTO>()
    .ForMember(t => t.FirstName, m => m.MapFrom(s => s.Name.FirstName))
    .ForMember(t => t.LastName, m => m.MapFrom(s => s.Name.LastName));

このようにMapの設定を明示的に記述します。
これでMapメソッドを呼べば無事名前もDTOに代入されます。
簡単ですね。

ただこれがNameプロパティがnullだったときに問題が発生します。
例外はでませんが処理がかなり遅くなります。
なかなかこの問題に気づかずに悩んでました。

これを解決するには、まあnullチェック入れろってことなんですかねー

Mapper.CreateMap<Custormer, CustomerDTO>()
    .ForMember(t => t.FirstName, m => m.MapFrom(s => s.Name == null ? String.Empty : s.Name.FirstName))
    .ForMember(t => t.LastName, m => m.MapFrom(s => s.Name == null ? String.Empty : s.Name.LastName));

これで手元では6倍くらい速くなりました。

CI環境でNuGetサーバにつながらない件

これは条件が限定されるんですけど
ソリューションの設定で「NuGetパッケージの復元の有効化」をしていて且
デフォルトのNuGetサーバ以外にサーバがある場合のCIをするときの話です。

基本的にCIサーバ上ではMSBuildでビルドするのでVisualStudioの設定とか関係ありません。
VisualStudio上であればツールのオプションでNuGetサーバを増やせば良いのですが
MSBuildでビルドするときのNuGetサーバの設定方法が分かりませんでした。
なんかどうやって調べて良いかも分からなかった。

結果
「NuGetパッケージの復元の有効化」を実行して作成される
.nugetフォルダ配下にある「NuGet.targets」というファイル内の
f:id:cer1974:20140528232952p:plain:w500
赤線の部分をコメントインしてURLを変更します。
これを入れるとデフォルトのNuGetサーバに接続できなくなるのでそちらも明示的に設定する必要があります。

ASP.NET WebAPIで差分更新する

なんか久しぶりになってしまった(反省

ASP.NET WebAPIで差分更新(?)をする方法です。
ここで言う差分更新は、クライアントから送られてきた項目だけを
更新するという意味の差分更新です。

普通にモデルバインドするとnullだったりデフォルト値がクライアントから送られてきたのか
送られてこなかったからnullなのかが分かりません。
さてどうするのん?って思ってたらちゃんとやり方がありました。

Putメソッドを使っても良いですが最近はどうやら差分更新の時は
Patchメソッドを使うらしいのでそちらを使います。
でもPatchじゃなきゃできないわけじゃないです。

まずモデルクラスはこんな感じ

public class User
{
  public int Id { get; set; }
  public string FirstName { get; set; }
  public string LastName { get; set; }
}

でクライアントはこんな感じでデータを送るとします

$("#button").on("click", function () {
  $.ajax({
    url: "http://localhost:62434/api/values",
    dataType: "json",
    method: "PATCH",
    contentType: 'application/json',
    data: JSON.stringify({Id:1, FirstName:"taro"}),
  });
});

上記の場合は、LastNameが送られてきてないので通常はnullになって更新されてしまいます。
これをちゃんとDBの値と組み合わせてnullにならないようにします。

public void Patch([FromBody]Delta<User> value)
{
  // DBから取得したとする
  var user = new User { Id = 1, FirstName = "jiro", LastName = "suzuki" };
  value.Patch(user);
}

Deltaクラスを使うと差分でデータバインドすることができます。
この場合は、FirstNameが"taro"でLastNameは"suzuki"のままです。
もちろんクライアントがFirstNameをnullで送ればそのままnullで更新ができます。

Deltaクラスは、System.Web.Http.ODataに定義されているのでNuGetで取得してください。

VisualStudioでファイルをリンクする方法

いつもやり方忘れちゃうのでメモっておく

たまにプロジェクト間で同一のConfigファイルとか持ちたいことがあります。
単純にコピーするとメンテが大変なのでエイリアス的なものを作りたいです。
VisualStudioではリンクとして追加という機能でこれを実現できます。

エイリアスを作成したいプロジェクトで「既存項目を追加」を選択して
ダイアログを表示します。
エイリアス元のファイルを選択し「追加」ボタン横の▼から「リンクとして追加」を選択すればOK
これだけなんですがたまにしかやらないので忘れるという
そしてなんて検索していいのかも分からず途方にくれるという

f:id:cer1974:20140409233314p:plain:w500

Moqでメソッドが呼ばれていないことを確認する方法

たまーに分岐処理の判定でこのメソッドが呼ばれてなければ
OKにしたいってことがあります。
Moqでそれを判定するにはどうするんだっけって悩んだんですが簡単でした。

// メソッドのモック
mock.Setup(x => x.FindBy());
・・・
// 呼ばれていないことを確認
mock.Verify(x => x.FindBy(), Times.Never);

Verifyの第2引数にTimes.Never付けるだけでした。

TFSの作業項目に色がつかない件

TFS2013から作業項目に色がつくようになりました。
こんな感じ
f:id:cer1974:20140302204644p:plain:w500
※画像は、VisualStudioOnlineのものですがまあ同じってことで。

しかしTFS2012からUpdateした場合、元から存在したプロジェクトの作業項目には色がつきません。
これをコンフィグファイルを変更することによって色がつくようにします。

まずエクスポートします。

witadmin exportprocessconfig /collection:http://hoge.com:8080/tfs/defaultcollection /p:projectName /f:c:\temp\config.xml  

これでc:\tempにxmlファイルができるので適当なテキストエディタで開きます。

<WEEKENDS>
     <DAYOFWEEK>Sunday</DAYOFWEEK>
     <DAYOFWEEK>Saturday</DAYOFWEEK>   
</WEEKENDS>
<!-- ここから -->
<WORKITEMCOLORS>
     <WORKITEMCOLOR primary="FF009CCC" secondary="FFD6ECF2" name="プロダクト バックログ項目" />
     <WORKITEMCOLOR primary="FFF2CB1D" secondary="FFF6F5D2" name="タスク" />
     <WORKITEMCOLOR primary="FFCC293D" secondary="FFFAEAE5" name="バグ" />
     <WORKITEMCOLOR primary="FFFF9D00" secondary="FFFCEECF" name="コード レビュー要求" />
     <WORKITEMCOLOR primary="FFFF9D00" secondary="FFFCEECF" name="コード レビュー応答" />
     <WORKITEMCOLOR primary="FFFF9D00" secondary="FFFCEECF" name="フィードバック要求" />
     <WORKITEMCOLOR primary="FFFF9D00" secondary="FFFCEECF" name="フィードバック応答" />
     <WORKITEMCOLOR primary="FFFF9D00" secondary="FFFCEECF" name="懸案事項" />
     <WORKITEMCOLOR primary="FFFF9D00" secondary="FFFCEECF" name="共有ステップ" />
     <WORKITEMCOLOR primary="FFFF9D00" secondary="FFFCEECF" name="テスト ケース" />
</WORKITEMCOLORS>
<!-- ここまで --> 

weekendsタグはもともとあるのでその下にworkitemcolorsタグを追加します。
上記は、Scrumテンプレートを選択したときの場合はなので適宜変更してください。

これをインポートします。

witadmin importprocessconfig /collection:http://hoge.com:8080/tfs/defaultcollection /p:projectName /f:c:\temp\config.xml 

これで色がつきます。

OracleClientをインストールせずにC#からOracleへ接続する方法

メモメモ

OracleDBへ接続する際にOracleClientをインストールして
tnsnames.oraに接続設定を書きアプリケーションからはtnsnameを指定する方法がありますが
これはnugetで取得した「Oracle Data Provider for.NET」だけで接続する方法です。

var connStr = "user id=userName;password=pass;data source=(DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=servername)(PORT=1521))(CONNECT_DATA=(SERVICE_NAME=serviceName)))";
var conn = new OracleConnection(connStr);

ちょっと長いですけどインストールがいらないので楽です。
ちなみに通常は接続文字列は設定ファイルに書きますので長くてもキニシナーイ