redwarrior’s diary

C#, ASP.NET, WEB, G*などの雑多な情報

(小ネタ)SQL Server のテーブルをまとめて選択して削除したい

データベースの削除やSQLを実行するのではなく、GUIでテーブルを選択して削除したい場合のメモ。

SQL Server Management Studioや、Visual Studio上のサーバー エクスプローラーではなく、SQL Server オブジェクト エクスプローラーを使う。

外部キー制約なども考慮して削除してくれます。

Unity で AOP

Unity で AOP

.NETのDIコンテナとしてUnityを使用していて、DIコンテナを使うならばAOPでログをはさみこめると便利だなと思って調べてみました。

Javaだとバイトコードをいじって目的を達成してしまう黒魔術なライブラリがあるけれど、C#ではCLR周りは良くわからないのでそう言った方法ではなく、インターフェースを使用する方向で調べました。

たどり着いたのが以下のサイト。Unityのバージョンが少し古いですが、設定方法は同じで行けました。

code.msdn.microsoft.com

あとは公式サイトの以下の「Dependency Injection with Unity」というドキュメントに詳しい情報が載っています。(英語ですが)

patterns & practices - Unity - Documentation

ASP.NET MVCAOP

Windows Forms等の場合は上記を応用してということになると思いますが、ASP.NET MVC はそうもいかないです。さらに言うとこっちの方が必要になると思うので引き続き調べました。

色々とググったのでサイトは覚えていませんが、最終的に以下の手順になりました。

ASP.NET MVC への導入方法

  1. NuGetから「Unity」「Unity.Mvc」「Unity.Interception」をインストール
  2. App_Startフォルダに出来たUnityMvcActivatorクラスのStartメソッドのコメントを外す
  3. UnityConfigクラスのRegisterTypesメソッドマッピングを記入する

修正したソースはこんな感じです。

2.UnityMvcActivatorクラス

public static void Start() // 修正後
{
    var container = UnityConfig.GetConfiguredContainer();

    FilterProviders.Providers.Remove(FilterProviders.Providers.OfType<FilterAttributeFilterProvider>().First());
    FilterProviders.Providers.Add(new UnityFilterAttributeFilterProvider(container));

    DependencyResolver.SetResolver(new UnityDependencyResolver(container));

    // TODO: Uncomment if you want to use PerRequestLifetimeManager
    Microsoft.Web.Infrastructure.DynamicModuleHelper.DynamicModuleUtility.RegisterModule(typeof(UnityPerRequestHttpModule));
}

3.UnityConfigクラス

public static void RegisterTypes(IUnityContainer container) // 修正後
{
    // NOTE: To load from web.config uncomment the line below. Make sure to add a Microsoft.Practices.Unity.Configuration to the using statements.
    // container.LoadConfiguration();

    // TODO: Register your types here
    container.AddNewExtension<Interception>();
    container.RegisterType<IProductRepository, ProductRepository>(GetInterfaceMembers());
}

private static InjectionMember[] GetInterfaceMembers() // 追加
{
    return new InjectionMember[]
               {
                   new Interceptor<InterfaceInterceptor>(),
                   new InterceptionBehavior<LoggingInterceptionBehavior>()
               };
}

LoggingInterceptionBehaviorクラスは上記サイトと大して変わらないですが、ログ出力にNLogを使用しているので自分の備忘録として残しておきます。

LoggingInterceptionBehaviorクラス

using Microsoft.Practices.Unity.InterceptionExtension;
using NLog;
using System;
using System.Collections.Generic;

namespace SampleApp.Utility
{
    public class LoggingInterceptionBehavior : IInterceptionBehavior
    {
        private static readonly Logger logger = LogManager.GetLogger("TLog");

        public IMethodReturn Invoke(IMethodInvocation input, GetNextInterceptionBehaviorDelegate getNext)
        {
            var methodBase = input.MethodBase;

            logger.Trace($"{methodBase.DeclaringType.Name}.{methodBase.Name} Start");

            var result = getNext()(input, getNext);

            logger.Trace($"{methodBase.DeclaringType.Name}.{methodBase.Name} End");

            return result;
        }

        public IEnumerable<Type> GetRequiredInterfaces()
        {
            return Type.EmptyTypes;
        }

        public bool WillExecute => true;
    }
}

これでDIしたオブジェクトのメソッドが実行される前後で、インターフェース名とメソッド名をログに出力することが出来ます。

インターフェース名を実装クラス名にしたい気もしますが、プロダクトではインターフェースと実装クラスは1対1になるのでまあいいかなと。

おまけ

Controllerでも同じことをしたい場合は、ActionFilterAttributeを実装したフィルターを用意することになりますね。

2016/5/11 追記

Disposeメソッドで困る(ログ出力が全てIDisposableになる)ので、ログ出力を実装クラス名にする方法を追加で調べました。以下の方法で可能でした。

LoggingInterceptionBehaviorクラスのInvokeメソッドの抜粋

logger.Trace($"{input.Target.GetType().Name}.{methodBase.Name} Start");

確認環境:

Unity 4.0.1

Unity.MVC 4.0.1

Unity.Interception 4.0.1

ASP.NET MVC 5.2.3

Visual Studio 2015

Windows 8.1 Pro

(小ネタ)Firefoxのはてなブックマークの拡張機能がなおった

しばらく前から、Firefoxはてなブックマーク拡張機能で、 サイドバー表示した場合に、タグによる表示の絞り込みが出来なくなっていたのだけど、

今確認してみたら、なおっていた(絞り込まれて表示されるようになっていた)。

地味に不便だったので、なおって良かった。

第17回 Tokyo Atlassian ユーザーグループ に参加した

こちらも2カ月くらいたってしまって今更感がありますが、参加したので感想を書いておきます。

Confluence上でKPIをレポート自動生成によって共有する

レポートにテンプレートエンジンを使用して、Webアプリのようにレポートを作成するという内容でした。ConfluenceはREST APIを提供しているので、色々なことが出来るのだなと思いました。

Bambooによる継続的デリバリー

ConfluenceやJIRAに比べて、取り上げられる機会が少ないBambooについてまとまった内容を発表されていました。また、Gebという画面周りのテストを行うツールを使ったテストのやり方なども取り上げられていました。

質疑応答でプルリクエストの様子(マージされるためには何人のレビュー承認が必要か等)の話もあり、勉強になりました。

JIRA / Confluenceの必須プラグインはこれだ

便利そうなプラグインが色々と紹介されていました。庶務さんがConfluenceを使用しているというのが面白かったです。それとプラグインをインストールすると管理者の仕事が増えるというのは、導入して見ないとわからないことだなと思いました。

あと、スピーカーの方の発言ではないのですが、ネットワーキングの時間にAtlassian社員さんに聞いたConfluenceの使い方で、

「まずはユーザースペース(自分用スペース)にメモを残していって、他の人にも役に立ちそうだったり、マニュアルとして残す必要が出てきたときに清書する」

が良さそうだったので、がんばらずに使っていこうと思いました。

イベント情報サイト(プレゼン資料が公開されています)

augj.connpass.com

(小ネタ)プロダクトバックログについて勘違いしていたこと

プロダクトバックログについて勘違いしていた。

プロダクトバックログは、複数の要求に優先順位をつけたリストのことだった。

今までプロダクトバックログを個別の要求だと思っていて、プロダクトバックログの一覧とかいう言葉を使ってしまっていた。ああ、はずかしい。

だからTFSでは個別の要求を、プロダクトバックログ項目と記していたのか。

CodeIQの忘年会に参加した

4カ月近くたってしまいましたが、昨年末にCodeIQ大忘年会に参加したのでその記録を残しておきます。

最初は元グーグルエンジニアの及川さんの話を聞いた。Qiitaの社内で行われている取り組みなどが知れて興味深かった。ChatOpsすごい。

資料:CodeIQ 2015大忘年会での講演の補足資料

次に、倉貫さんと栗栖さんの対談があった。リモートワークの話。採用の話。コードの品質の話があった。リモートワークはなかなか自分がその状態になることは想像できないのですが、エンジニアの仕事は時間に縛られるものではないので有りなのかもしれないと思った。

採用の話では、面接を何度も行う事や、仕事を一部まかせてみる、1年以上検討するなど、息の長い丁寧な取り組みが必要なのだと思った。また、重視することとして、技術力はもちろんだけど、向上心が重視されていて、それを会話でを通じて引き出し見極めるというのが、よく考えられているなと思った。

その次は、Caveさんの話。ゲーム会社の生々しい話で面白かった。ゲーム会社もコミュニケーションでなんとなるということで意外とSIerみたいなところがあるなあと感じた。

その次は、澤さんのプレゼンのプレゼンでした。プレゼンで重要なのは1.ビジョン:Why何のためと2.What何を伝えるが大事で、3.話術:Howどうやってはその次だというのが印象でした。内容盛りだくさんなのでプレゼン資料を見ると良いと思います。

澤円の"リアル"プレゼン塾

LT前の最後は、伊藤さん、増井さん、喜屋武さんによるパネルディスカッション。伊藤さんの影響力はすごいと思う。寿司対談の最終回が炎上した話をされていて、そう言えば見逃していたので見ようと思った。

あとはLT。ちょまどさんが可愛い外見に反してガチなエンジニアだったのですごい。あときゃんちさんは美人。

他の方のレポート他:

hackonair.hrtoolhub.com

Google検索結果(codeiq忘年会)

Angular 2のビルド周りについて調査した

以下の記事で入門して、公式チュートリアルの学習には、Plunker を使用していたので、手元で試していなかった。

npm i angular2してHello World!するところまで - Qiita

いざ、自作プログラムを作成しようと思った時に、色々とわからなかったので、調べたことをまとめた。

TypeScriptのコンパイル方法

tsファイルから、jsファイルへの変換(トランスパイルというらしい)には、tscコマンドの-pオプションで現在のディレクトリを指定する。

beta.6でtsconfigの書き方が変わったことによるエラーが出て、自分のミスと切り分けがつかず、少し悩んだ。targetに"es6"を設定することでエラーが出なくなった。

tsconfigのfilesは、エントリポイントを指定すると書いてあったので、boostrapメソッドを記述したファイルを指定した。 公式チュートリアルのフォルダ構成だと、app/main.ts になる。

jsファイルをBrowserifyで一つにまとめる

tscによって変換したjsファイルを一つにまとめる。とりあえずコマンドの引数にjsファイルを複数指定したら動作した!

でもtsファイルを作るたびに、指定を増やすなんてことはしたくないなあ~と思い、解決方法について調べた。

これはbrowserify-handbookというサイトに書いてあった。requireメソッドのツリーをたどって依存関係を構築してくれるらしいので、こちらもエントリポイントだけで良いみたい。

github.com

その結果、なんとか複数ソースファイルを使用したHello Worldが動作することを確認できた。

Httpサーバーで実行するにはどうするのか?とかリリースどうするのか?とかあるけど、とりあえず作成に入ろうと思う。

Angular 2 Router挑戦

Routerでつまずく。browserifyでやろうとすると壁があちこちにあるな。......そうか!Routerを使う場合はサーバー上で実行しなければならないのか。

browserifyのサイトにあったbeefyを-gオプションでインストールして実行。beefyにbundle.jsを渡したら動いた。

ところが翌日に、昨日は動いたのに今日は動かないという現象が発生。buf.copyでエラーになる。リソースの読み込みに関するエラーのようだ。 そこでbudle.jsの代わりに、bundle.jsを読み込んでいるindex.htmlをbeefyに渡すようにしたらエラーが出なくなった。

Firefox, Chromeでの動作確認

今までEdgeで動作確認をしていたが、他の人に確認してもらうために、FirefoxChromeで試してみると、動作しない!変換されたJSを見てみると、classという文字がある。つまり、ES6形式のJSになっていた。

そこで入門に使用した記事を再び見ると、beta.6対応版になっていた。それに従って、babelifyを使用すると、今度はbabelifyでエラー。presetsで指定したes2015が見つからないというので、babel-preset-es2015をインストール。

そうすると、FirefoxChromeで動作するようになった。 index.htmlで指定していたxxxpolifill.jsも不要になった。

今後

チュートリアルに手を加えて、検索機能や保存ボタンを作ってみた辺りで、調査は一区切りとした。Angular2の開発が進んで、ng2-bootstrapとかIonic 2の情報が増えてきたら、また調べようと思います。

参考までにpackage.json、tsconfig.json、index.htmlの内容を残しておく。

package.json

{
  "name": "angular2-sample",
  "version": "1.0.0",
  "description": "",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "tsc": "tsc -p ./",
    "browserify": "browserify ./app/main.js -t babelify -o ./bundle.js",
    "beefy": "beefy ./index.html",
    "build": "npm run tsc && npm run browserify",
    "bs": "npm run tsc && npm run browserify && npm run beefy"
  },
  "babel": {
    "presets": [
      "es2015"
    ]
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "babel-preset-es2015": "^6.5.0",
    "babelify": "^7.2.0",
    "browserify": "^13.0.0",
    "typescript": "^1.7.5"
  },
  "dependencies": {
    "angular2": "^2.0.0-beta.6",
    "es6-promise": "^3.0.2",
    "es6-shim": "^0.33.3",
    "reflect-metadata": "^0.1.2",
    "rxjs": "^5.0.0-beta.0",
    "zone.js": "^0.5.14"
  }
}

tsconfig.json

{
  "compilerOptions": {
    "target": "es6",
    "noImplicitAny": true,
    "removeComments": true,
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true,
    "moduleResolution": "node"
  },
  "files": [
    "./app/main.ts"
  ]
}

index.html

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>Angular 2 Sample</title>
</head>
<body>
  <my-app>Loading…</my-app>
  <script src="./bundle.js"></script>
</body>
</html>