きよくらの備忘録

「三日坊主と呼ばせない!日記」改め。主にソフトウェア開発関連の話題。

Visual Studio 2013 Update 3 の ASP.NET 周りの変更まとめ

Visual Studio 2013 Update 3 がRTWしました。

f:id:kiyokura:20140805164647p:plain

ダウンロードはこちらあたりから。

 

オフィシャルなアナウンスとしては下記あたりになると思います。

 

ASP.NET及びWEB開発関連の更新

せっかくなので(?)、前述の Announcing new Web Features in Visual Studio 2013 Update 3 RTM の内容に沿って、Web周りの変更を確認してみます。

Microsoft ASP.NET and Web Tools 2013.3 の新機能

  • ASP.NET 5.2 technologies (MVC, Web API) のスキャフォールディングサポートの追加
    • スキャフォールディング時にプロジェクトで利用しているフレームワークのバージョンを正しく検出するので、5.1.2を利用している場合は、5.2ではなく5.1.2としてスキャフォールディングする
  • JSON エディタの改善
    • オートフォーマットをoffに出来るようになった
      • 今までオートフォーマットは自動的に有効になっておりoffにすることは出来なかったが、設定でoffに出来るように
        • f:id:kiyokura:20140805163452p:plain
      • オートフォーマットをoffに設定しても、手動でのフォーマット(コンテキストメニュー or ショートカット)は有効
    • ブレース(波カッコ、{})とブラケット(角カッコ、[])のマッチング
      • 対応するカッコ同士をハイライト表示するようになった
      • f:id:kiyokura:20140805163910p:plain
  • CSS エディタの改善
    • インテリセンスのSelectors Level 4の対応の改善
      • インテリセンスが策定中の Selectors Level 4 のより多くのパターンに対応
      • 例::mutches():not()の()内でもインテリセンスが効く
      • f:id:kiyokura:20140805163938p:plain
    • フォント及び画像ファイルの.cssファイルへのドラッグ&ドロップの対応
      • ソリューションエクスプローラからフォント(eot/svg/ttf/woffの各形式)や画像ファイルをドラッグ&ドロップすると、適切なcssの記述を自動で挿入する
  • MVCおよびWeb Forms (One ASP.NET )にて二要素認証のスケルトン実装の追加
    • One ASP.NET テンプレートのMVCおよびWeb Formsを選択した際のサンプル実装に、ASP.NET Identityの二要素認証のサンプル実装が追加
      • ManageController.csManageViewModels.cs等が追加され、その他のファイルにもコードが追加されている
      • f:id:kiyokura:20140805164006p:plain
  • facebookアプリケーションテンプレートの組み込みの廃止(Visual Studio Gallerへ移動)
    • One ASP.NET テンプレート内からfacebookアプリケーションのテンプレートが無くなりました
      • 廃止されたわけではなく、独立したVS拡張としてVisual Studio Galleryに移動しています
      • facebook側のAPIの変更などに柔軟に対応するための措置と思われます
    • 注意:2014.08.05現在、まだfacebookアプリテンプレートは公開されていない?
  • Enabled creation of ASP.Net projects using AAD when signing in with Microsoft account
    • Azure ADで認証を行うアプリケーションを作成する際、プロジェクト作成時にマイクロソフトアカウントでサイン・インしてプロビジョニングが行えるようになった
      • ……とあるのですが、試してみてもうまいこと行きませんでした……。
      • 私のポータルでのAzure ADの表示あたりからしてスクリーンショットと異なるので、なにか状態が違うのかも?
  • Microsoft Azure WebJobsへのパブリッシュ機能の追加
    • Visual Studioでコンソールアプリケーションを作成して、WebJobsとして発行できるようになった模様
    • f:id:kiyokura:20140805162232p:plain
    • f:id:kiyokura:20140805162237p:plain

フレームワークのアップデート

  • テンプレートで標準で展開されるテンプレートがアップデートされています

その他関連ツールのアップデート

  • WebDeploy 3.5 refresh
  • IIS Express 8.0 June 2014 Hotfix

既知の問題

これからASP.NETを始める人が読むべき本

これからASP.NETをはじめる人に、『まずこの本を読みましょう』と即答できる書籍がようやく発売されました。

.NET開発テクノロジ入門 2014年版 VisualStudio2013対応版 (MSDNプログラミングシリーズ)

.NET開発テクノロジ入門 2014年版 VisualStudio2013対応版 (MSDNプログラミングシリーズ)

今年の6月4日に発売されたこの本、5末に開催されたde:codeの会場で先行販売されていたのをゲットしていたのですが、実は今日になって初めてまともに目を通しました(すみません)。

 

タイトルからして、.NET開発全般を広く浅く紹介してると思えるかもしれません*1。が、版社である日経BPのサイトの目次を見ていただけるとわかるのですが、第6章以外はほぼほぼ、ASP.NETもしくはASP.NETでも押さえておくべきテクノロジとなっています*2

 

なので、この書籍は『One ASP.NET入門』と思って読んでも差しさわりにない無いないようになっていると思います。というかむしろ、まずはこの書籍を読みましょう。

 

 

ASP.NET MVCを深く学ぶには…

特に『ASP.NET MVCの開発をこれから始めよう』と思った場合、まずはこの書籍の1章~3章を読んだうえで、公式サイトのチュートリアル(※ただし英語)をやってみるというステップが良いのではないかと思います*3

その上で、ASP.NET MVCについてもっと深く知りたい場合は、こちらの書籍がお勧めできます。

プログラミングMICROSOFT ASP.NET MVC (Microsoft Press)

プログラミングMICROSOFT ASP.NET MVC (Microsoft Press)

 

この書籍の内容はMVC 3準拠で記述されています*4。が、MVC 5であっても概念や仕組みについてはMVC 3の知識がほぼそのまま生かせます。またこの書籍は『初心者向けのチュートリアル』や『TIPS集』的なものではありませんASP.NET MVCの概念や仕組み、これを使ってアプリケーションを実装・設計する際に必要な知識について述べている書籍です。そういったこともあって、少なくともMVC 5世代では大半の記述内容がそのまま通用すると思います。

(現在、翻訳元のほうはMVC 5対応のバージョンの isbn:1430265299 が発売されていますが、基本的にはMVC 5での差分が加筆されている程度のように見えますし。)

 

ということで、重ねてになりますが、ASP.NET MVCの日本語書籍が欲しい方は、まずは一度チェックしてみてください。

*1:何を隠そう私も最初はそう思っていました

*2:Entity FrameworkもMSの雰囲気的には、どちらかというとASP.NET寄りと思っても差しさわりが無い感じだったりもしますし。

*3:Entity FrameworkはASP.NET MVCの必須テクノロジーではありませんが、公式チュートリアルの大半はEntity Frameworkを利用しています。そのため、『チュートリアルをちょっと改造して何かしよう』とか思った際にも、Entity Frameworkの基礎をある程度は知っておかないと変なはまり方をする恐れがあるんじゃないかと思ってます

*4:MVC 4についても巻末で補足されています

ASP.NET Identityのカスタマイズ-既存のユーザ情報を使っての認証

ASP.NET MVC 5になって、認証のインフラとしてASP.NET Identitiyが登場しました。今までのMembership Providerと比べてかカスタマイズもし易く、使い勝手の良いところが素敵です。

ちょうど、『既に別システムで運用されている独自のユーザー情報をそのまま利用し、アプリケーションのユーザー認証を行う』という要件で実装する機会がありました。せっかくなので要点を抜粋したサンプルをgithubにおいてみました。

kiyokura/AspNetIdentityCustomStoreSample

 

 

以下に、ポイントを軽く紹介してみたいと思います。

概要

基本的には、@okazuki さんの ASP.NET Identityカスタマイズに挑戦 で取り上げられているサンプルそのままです。ですので、差分となるポイントを中心に(というかそこだけ)を紹介しようと思います。

ポイントとなるのは

  1. 独自のユーザー情報クラスの作成
  2. 独自のユーザーストアクラスの作成
  3. 独自のパスワードハッシュアルゴリズムの実装
  4. UserManagerで利用するパスワードハッシュアルゴリズムを設定

です。

@okazuki さんのサンプルに対して追加されているのが、特に『3. 独自のパスワードハッシュアルゴリズムの実装』となっています。 また、既に存在するデータを使って認証を行うだけなので、ユーザーの登録に必要な処理などは実装していません(そのあたりは既存の別の管理ツールなどがある想定)。

 

 

独自のユーザー情報の例

既に別システムで利用されているユーザ情報の例として、以下のようなテーブルに格納されているものを想定します。

CREATE TABLE [dbo].[UserInfo] (
    [UserID]    UNIQUEIDENTIFIER NOT NULL,
    [LoginCode] NVARCHAR (50)    NOT NULL,
    [FirstName] NVARCHAR (50)    NOT NULL,
    [LastName]  NVARCHAR (50)    NOT NULL,
    [EMail]     NVARCHAR (MAX)   NOT NULL,
    [Password]  NVARCHAR (MAX)   NOT NULL,
    PRIMARY KEY CLUSTERED ([UserID] ASC)
);
GO
CREATE UNIQUE NONCLUSTERED INDEX [IX_UserInfo_U1]
    ON [dbo].[UserInfo]([LoginCode] ASC);

また、Passwordには、DBのユーザー関数として定義されている独自のアルゴリズムでハッシュ化されていることを想定しています。

 

独自のユーザー情報クラスの作成

IUser の実装として、MyAppUser という名前で作りました。基本的にはDBのテーブルの情報をそのまま読み込むつもりの構成です。ハッシュ化されたパスワードもここで保持するようにしておきます。

/// サンプルプロジェクト:Models/Auth/Auth.csに実装
public class MyAppUser : IUser
{
    /// <summary>
    /// DB上の一意キー
    /// </summary>
    public string Id { get; set; }

    /// <summary>
    /// ログイン認証に使うユーザーID
    /// </summary>
    public string UserName { get; set; }

    public string FirstName { get; set; }

    public string LastName { get; set; }

    public string EMail { get; set; }

    /// <summary>
    /// ハッシュ化されてDBに格納されているパスワード
    /// </summary>
    public string HashedPassword { get; set; }
}

 

独自のユーザーストアクラスの作成

カスタムストアを実装します。今回はログイン認証に必要最低限の機能を実現するため、IUserStore と IUserPasswordStore のみの実装とします。処理を実装するメソッドも、とりあえず必要最低限の2つのみにしています。

/// サンプルプロジェクト:Models/Auth/Auth.csに実装
using Dapper;

~

public class MyAppUserStore : IUserStore<MyAppUser>, IUserPasswordStore<MyAppUser>
{
    public Task<MyAppUser> FindByNameAsync(string userName)
    {
        // DBを検索する。Entitiy Franewrokを使っても良い。今回は諸事情と好みの問題でDapperを利用。
        using (var cn = DbConnectionFactory.Create("DefaultConnection"))
        {
            cn.Open();
            var sql = " SELECT " +
                      "   Convert(nvarchar(MAX), UserID) AS ID , LoginCode AS UserName , FirstName , LastName , EMail , Password AS HashedPassword " +
                      " FROM " +
                      "   UserInfo " +
                      " WHERE " +
                      "   LoginCode = @UserName ";

            var users = cn.Query<MyAppUser>(sql, new { UserName = userName });
            return Task.FromResult(users.FirstOrDefault());
        }
    }

    public Task<string> GetPasswordHashAsync(MyAppUser user)
    {
        // DBから取得されたMyAppUserにはハッシュ化されたパスワードが既に格納されているはず。
        // なのでここではそれをそのまま返す
        return Task.FromResult(user.HashedPassword);
    }

    public Task CreateAsync(MyAppUser user)
    {
        throw new NotImplementedException();
    }

    public Task DeleteAsync(MyAppUser user)
    {
        throw new NotImplementedException();
    }

    public Task<MyAppUser> FindByIdAsync(string userId)
    {
        throw new NotImplementedException();
    }

    public Task UpdateAsync(MyAppUser user)
    {
        throw new NotImplementedException();
    }

    public void Dispose()
    {
        //例外は出ないようにNotImplementedExceptionは消しておく
    }

    public Task<bool> HasPasswordAsync(MyAppUser user)
    {
        throw new NotImplementedException();
    }

    public Task SetPasswordHashAsync(MyAppUser user, string passwordHash)
    {
        throw new NotImplementedException();
    }
}

 

独自のパスワードハッシュアルゴリズムの実装

ASP.NET Identity では 標準のパスワードハッシュアルゴリズムとして PasswordHasher クラスが用意されています。 しかし、今回のように別システムで独自のアルゴリズムによってハッシュ化されている場合は、当然これではうまくいきません。 その場合は、IPasswordHasher の実装としてHasherを作ることで解決できます。以下は、DB側で実装されたユーザー定義関数を利用してハッシュ化されたパスワードを取得するカスタム・ハッシャーの例です。

/// サンプルプロジェクト:Models/Auth/Auth.csに実装
public class MyPasswordHasher : IPasswordHasher
{
    public string HashPassword(string password)
    {
        using (var cn = DbConnectionFactory.Create("DefaultConnection"))
        {
            cn.Open();
            var sql = "SELECT dbo.HashPassword(@RawPassword)";
            return cn.Query<string>(sql, new { RawPassword = password }).Single();
        }
    }

    public PasswordVerificationResult VerifyHashedPassword(string hashedPassword, string providedPassword)
    {
        if (hashedPassword == this.HashPassword(providedPassword))
        {
            return PasswordVerificationResult.Success;
        }
        else
        {
            return PasswordVerificationResult.Failed;
        }
    }
}

 

UserManagerで利用するパスワードハッシュアルゴリズムを設定

作成したカスタム・ハッシャーをUserManagerが利用するように設定します。といっても、UserManagerのPasswordHasher プロパティーにインスタンスを設定してあげるだけです。こんな感じで設定してやれば、あとは勝手に使ってくれます。

/// サンプルプロジェクト: Controllers/AuthController.csに実装
//ログインを処理するコントローラのActionメソッド
[HttpPost]
[AllowAnonymous]
public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
{
    if (ModelState.IsValid)
    {
        // カスタマイズのポイント:
        //   UserManagerのインスタンス作成時、
        //     型引数:カスタマイズしたユーザー情報
        //     コンストラクタの引数:カスタマイズしたユーザストアのインスタンス
        //   をそれぞれ渡す
        var userManager = new UserManager<MyAppUser>(new MyAppUserStore());

        // カスタマイズのポイント:
        //   パスワードのハッシュ化アルゴリズムとして、IPasswordHasherを実装したカスタムクラスのインスタンスを設定
        userManager.PasswordHasher = new MyPasswordHasher();

        var user = await userManager.FindAsync(model.UserName, model.Password);
        if (user != null)
        {
            var authentication = this.HttpContext.GetOwinContext().Authentication;
            var identify = await userManager.CreateIdentityAsync(
                user,
                DefaultAuthenticationTypes.ApplicationCookie);
            authentication.SignOut(DefaultAuthenticationTypes.ExternalCookie);
            authentication.SignIn(new AuthenticationProperties() { IsPersistent = false }, identify);

            return Redirect(returnUrl);
        }
        else
        {
            ModelState.AddModelError("", "ログインIDまたはパスワードが無効です。");
        }
    }
    
    // ここで問題が発生した場合はフォームを再表示します
    return View(model);
}

 

別のカスタマイズ方法も…

今回は UserManager はそのまま利用し、ユーザーストアをカスタマイズする方法でやってみました。

しかし、ユーザー認証処理そのものがDB側でストアドプロシージャによって行われ、アプリ開発者にそのロジックが公開されていないようなケースもあると思います。 その場合はユーザーストアのカスタマイズで対応するのでは無く、、UserManager を継承したカスタムのUserManager を作成し、FindAsyncメソッドをオーバーライドしてやるほうが簡単かもしれません。

 

まとめ

ということで、簡単ですがこんな感じで既存の独自構成のユーザ情報を使っての認証を実装してみました。特にエンタープライズアプリケーションだと、社内で別途管理統一されたユーザーデータを使って個々のアプリケーションで認証を行う…という事も少なくないのではないかと思っています。そういった場合でも、ASP.NET Identityでは比較的簡単なカスタマイズで対応できると思います。 また今回は読み込みのみですが、このテーブルに対してユーザの登録やパスワードの変更処理などをカスタマイズして実装することも容易だと思います。

 

なお、今回は下記の情報を参考にさせていただきました。

オンラインイベント"dotnetConf"が開催されます(ただし英語)

日本時間の2014.06.26-27に、開発者向けオンラインイベント「dotnetConf」が開催されます(※ただし英語。現地太平洋夏時間では2014.06.25-26)。

 

f:id:kiyokura:20140620065740p:plain

 

詳細はこちらのdotnetConfオフィシャルサイトから。

日本時間では深夜の時間帯とまあアレなのですが、Channel 9にて無償で視聴できますので、興味がある方はご覧になってみてはいかがでしょうか*1

特に事前登録の必要はないようです。が、参加者の把握的な意味で助かるので事前登録してね、的なことも書いてあります。視聴予定の方は登録してあげても良いのではないかと思います。

自分のメモの意味で、スケジュールを抜粋しておきます。

※2014.06.20現在の状態です。(まだ未定っぽいところもありますし、今後更新されていくと思います。気が付いたら更新する予定) ※2014.06.23 Day1のセッション順変更と、Day2のタイトルのアップデートを反映。

 

 

day 1 - JST 2014.06.26 01:00 Start

PDT JST Session
09:00 01:00 State of .NET (Keynote)
09:15 01:15 New Innovations in the .NET Runtime
10:15 02:15 The Future of C#
11:15 03:15 Building Universal Windows Apps with XAML and C# in Visual Studio
12:00 04:00 .NET Native Deep Dive
12:45 04:45 Fun with .NET - Windows Phone, LEGO Mindstorms, and Azure
13:15 05:15 Kinect for Windows
14:15 06:15 What's new in XAML Platform & Tooling
15:15 07:15 Developing native iOS, Android, and Windows apps with Xamarin
16:15 08:15 What's new for WPF Developers
17:00 09:00 .NET Micro Framework and IoT

 

day 2 - JST 2014.06.27 01:00 Start

PDT JST Session
09:00 01:00 ASP.NET Today and Tomorrow (Keynote)
09:30 01:30 ASP.NET Web Forms
10:00 02:00 ASP.NET MVC 6 (now with integrated Web API!)
11:00 03:00 Entiy Framework
12:00 04:00 Taking Your ASP.NET Apps to the Cloud with Microsoft Azure Web Sites
13:00 05:00 ASP.NET Publishing Explained
14:00 06:00 ASP.NET Identity
15:00 07:00 Dependency Injection and Testability in .NET
16:00 08:00 SignalR
16:30 08:30 ASP.NET vNext 101

 

個人的には、ASP.NET関係が2日目に固まっているのでそちら注目の予定です。 (27日は仕事はお休みにしようかな…)

*1:リアルタイムで見なくても、多分後で動画が公開されるんじゃないかとは思います

DapperはIN句も自動でマップしてくれるのが便利

本エントリはC# Advent Calendar 2013 3日目のエントリです。

Dapperいいよ、Dapper

最近は完全にDapper派(?)になっています。 ということで、今日はタイトルの通りDapperのIN句へのマップ機能を紹介したいと思います。

 

IN句に配列やリストを自動でマップする

通常、SQLの標準のプロバイダで利用できるプリペアード・ステートメントでもIN句についてはあまり文化的な対応ができないんじゃないかと思います。IN句に含めるリストに値をバインドしないといけない場合、どうしても動的にSQLを組む必要に駆られがちです。リストの要素の数だけバインド変数を作るってそれぞれにバインドする……のが良いのでしょうが、ついつい値込で直接SQL組をんじゃったり……*1

 

ですがこれ、Dapperを使うと簡単に解決できます。Dapperを使うと、こんな風に書けるんです。

便利ですよね?

 

どうSQLが組まれてどうバインドされているのか

便利なのはいいのですが、どのようにSQLが組まれ、実行されているのでしょうか。気になりますよね。

 

GlimpseとアドオンのGlimpse.Adoを使うと、実行したSQLを簡単に確認することができます。ということで、ASP.NETのアプリケーションとして上記のSQLを実行してみました。

 

f:id:kiyokura:20131201204507p:plain

なるほど、リテラルとして文字列でSQLを組み立てるのではなく、リスト要素の数だけ連番でパラメータを作成してバインドしているようです。これなら文字列のリストを渡す場合でも、安全性そうですね。

また上記の例だとジェネリックリストを渡していますが、単純な配列でも問題ないようです。

 

まとめ

いかがだったでしょうか。

実はこれ、偉そうに紹介するようなことではなく、Dapperのサイトでも紹介されている代表的な機能の一つです(Dapper - a simple object mapper for .Net#List Support)。

この他、Dapperにはドキュメンドが少ないこともあり、テストコードを眺めたりしてると新しい発見があるかもしれません。まだ見たことがない方は、ぜひ一度見てみること面白いかと思います。

ということで、ちょっと軽めの内容でしたが、これにて本日のエントリを終わりたいと思います。 次は…週末にOne ASP.NET Advent Calendarでお会いしましょう!

*1:良い子は真似してはいけません