ハロの外部記憶インターフェイス

そろそろ覚える努力が必要かも…

ASP.NET MVCでjQueryが動作しない

SCRIPT5009: '$' is not defined

jQueryをすべて正しく設定してもこのエラーになってしまう。

解決

@Scripts.Render("~/bundles/jquery")をヘッダー部に設定する。

<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>@ViewBag.Title - マイ ASP.NET アプリケーション</title>
    @Styles.Render("~/Content/css")
    @Scripts.Render("~/bundles/modernizr")
    @Scripts.Render("~/bundles/jquery")      <!-- 下部にあったjqueryをヘッダー部へ移動した。  -->

</head>
<body>

    // 省略

    @*Scripts.Render("~/bundles/jquery")*@
    @Scripts.Render("~/bundles/bootstrap")

    @RenderSection("scripts", required:=False)
</body>
</html>

Visual StudioのLocalDb管理

LocalDb

Visual Studioから、ローカル用にLocalDbの追加が可能になっている。

※詳細については

SQL Server 2016 Express LocalDB | Microsoft Docs

Visual Studioから追加する。

サンプルプロジェクトはASP.NET MVC Webアプリケーションのルートから、マウス右クリックメニューから「追加」「SQLデータベース」 f:id:haronoid:20180425160022p:plain 新しいDB名を追加 f:id:haronoid:20180425160207p:plain App_Dataフォルダーに追加するか聞かれるが、素直にApp_Dataにしました。 f:id:haronoid:20180425160235p:plain

ソリューションにMyDbが追加される。 f:id:haronoid:20180425160331p:plain

ログインパスワードを設定する。

mdfファイルをダブルクリックするとVisual Studio内のSQL Serverオブジェクトエクスプローラが開きます。

照合順序の変更

デフォルトは「SQL_Latin1_General_CP1_CI_AS」になっており、日本語が文字化けするので、「Japanese_XJIS_100_CI_AI_WS」へ変更が必要

Visual Studio内のSQL ServerオブジェクトエクスプローラからLocalDbを開いて、新しいクエリを表示し、以下のコマンドを実行する。

SELECT DB_NAME()
GO

-- 上記DB_NAMEをDB名にして実行
USE [master]
GO
ALTER DATABASE [database_name] COLLATE Japanese_XJIS_100_CI_AI_WS
GO

テーブルに既にデータが貼っている場合

ALTER TABLE dbo.Table_1 
ALTER COLUMN NAME varchar(50) COLLATE Japanese_XJIS_100_CI_AI_WS;

接続文字列

ASP.NET MVCのプロジェクト内部に入れた場合はフォルダー名を|DataDirectory|にすることが可能

Server=(LocalDB)\MSSQLLocalDB; Integrated Security=true ;AttachDbFileName=D:\Data\MyDB1.mdf
Server=(LocalDB)\MSSQLLocalDB; Integrated Security=true ;AttachDbFileName=|DataDirectory|MyDB1.mdf

MS SQL Server Management Studioから接続する。

接続文字列に(LocalDB){インスタンス名}で接続出来る。

(LocalDB)\MSSQLLocalDB

インスタンス名がわからない場合、「sqllocaldb.exe」で調べられる。

> sqllocaldb info
MSSQLLocalDB
ProjectsV13

sqlcmdで接続する場合

新しいDB作成及び、ログインの手順…

恐らく、LocalDbをサーバーリリスする場合、サーバーのローカルユーザーからLocalDbを作成しないと権限問題が発生する可能性があり、 以下のコマンドからサーバーでLocalDbを作成するコマンドを実行する必要がありそう。 サーバーにもSQL Server Express LocalDbをインストールする必要あり。

SqlLocalDB.exe create "DeptLocalDB"  
SqlLocalDB.exe share "DeptLocalDB" "DeptSharedLocalDB"  
SqlLocalDB.exe start "DeptLocalDB"  
SqlLocalDB.exe info "DeptLocalDB"  
REM The previous statement outputs the Instance pipe name for the next step  
sqlcmd –S np:\\.\pipe\LOCALDB#<use your pipe name>\tsql\query  
CREATE LOGIN NewLogin WITH PASSWORD = 'Passw0rd!!@52';   
GO  
CREATE USER NewLogin;  
GO  
EXIT  
sqlcmd –S (localdb)\.\DeptSharedLocalDB -U NewLogin -P Passw0rd!!@52  

参考: SqlLocalDB ユーティリティ | Microsoft Docs

ダウンロード: SQL Server Downloads | Microsoft

ASP.NET MVC画面イメージをPDFにする。

ASP.NET MVCの画面をPDF化する方法

ASP.NET MVCでの帳票関連で使えるのがないか調べている内に見つかった。もので、画面イメージをそのままPDF化出来るものがあったので、メモる。

TuesPechkin

wkhtmltopdfをベースにラッピングしている模様。注意としてwkhtmltopdfがAzureでは動作しないみたいで、Azureで使用するなら別の方法を考えた方がいいかも GitHub - tuespetre/TuesPechkin: A .NET wrapper for the wkhtmltopdf library with an object-oriented API.

Nuget

Nugetからインストールする。Win32と64がそれぞれあるけど、AnyCPUで問題なかった。

PM> Install-Package TuesPechkin.Wkhtmltox.AnyCPU

Controller

pdf化したい画面をUrlにし、HtmlToPdfDocumentに渡せば基本簡単に出来ちゃう感じです。 ASP.NET MVCでやっているが、MVCは特に関係なさそう。

using TuesPechkin;

public class HomeController : Controller
{
    public ActionResult ToPdfPage()
    {
        var helper = new UrlHelper(ControllerContext.RequestContext);
        var indexUrl = helper.Action("Index", "Home", null, Request.Url.Scheme);

        var document = new HtmlToPdfDocument()
        {
            GlobalSettings =
            {
                ProduceOutline = true,
                DocumentTitle = "PDF Sample",
                PaperSize = PaperKind.A4,
                Margins =
                {
                    All = 1.375,
                    Unit = Unit.Centimeters
                }
            },
            Objects =
            {
                new ObjectSettings() {
                    PageUrl = indexUrl,
                },
            }
        };

        var converter = new StandardConverter(
                    new PdfToolset(
                        new WinAnyCPUEmbeddedDeployment(
                            new TempFolderDeployment())));

        var pdfData = converter.Convert(document);

        return File(pdfData, "application/pdf", "PdfSample.pdf");
    }
}

ExcelファイルをPdfに変換する。

前提条件

  • 実行するサーバーのExcelアプリを実行するため、サーバーにExcelのインストールが必要
  • バージョンは2007以降である必要
  • .NETのアセンブリを利用するため、Windowsサーバー限定となる。

Visual Studioでプロジェクトを生成し、参照を追加

Nugetで以下のパッケージを追加します。 * Microsoft.Office.Interop.Excel

using

オープンするオブジェクトはすべて「ComObject」になるらしく、いちいち開放してあげないとプロセスが残ってしまう。

using Microsoft.Office.Interop.Excel;  // Excel関連
using System.Runtime.InteropServices;  // ComObjectを開放するため

ExcelファイルのOpen

private Application _application = new Application();
private Workbook _workbook;
_application.Visible = false;   // Excel Window 非表示
_workbook = (Workbook)_application.Workbooks.Open(filepath);

ExcelをPdf出力する

そなえつけの「ExportAsFixedFormat」関数でExcelのPdf出力は簡単に出来た。

_workbook.Worksheets.Select(); // 全シート選択状態

_workbook.ExportAsFixedFormat(
     XlFixedFormatType.xlTypePDF,    //XPSとPDFのどれか
     pdfFilePath,                                     //PDFファイルの出力パス
     XlFixedFormatQuality.xlQualityStandard);   //品質:StandardとMinimumのみ

ComObjectの開放

面倒だったのが、SheetもCellもつくたびComObjectの参照カウントが増えるため、任意に解除する必要があった。

public void SetWorkSheet(int sheetNo)
{
    Marshal.ReleaseComObject(_worksheet);
    _worksheet = _workbook.Worksheets.Item[sheetNo];
}
public string GetCellText(int rowNo, int colNo)
{
    string ret;
    var cell = _worksheet.Cells[rowNo, colNo];
    ret = cell.Text;
    Marshal.ReleaseComObject(cell);
    return ret;
}
public void Close()
{
    if (_worksheet != null)
    {
        Marshal.ReleaseComObject(_worksheet);
        _worksheet = null;
    }
    if (_workbook != null)
    {
        _workbook.Close(false); // 変更分を保存せず終了する。
        Marshal.ReleaseComObject(_workbook);
        _workbook = null;
    }
    if (_application != null)
    {
        _application.Quit();
        Marshal.ReleaseComObject(_application);
        _application = null;
    }
}

作ったソースの中から、一部メモとして残す。

最終的に

ComObject開放がうまくいかなかったり、プロセスが残ったりしたので、Excelの操作系はClosedXMLを利用することにした。

ASP.NET MVC AD認証ログイン

注意

このポストはまだ成功確認が取れていない状態のメモです。 環境が整い次第テストを行う予定で、そのときのための準備になります。

AD認証でのログインについて調べる

Visual StudioのテンプレートではUseOpenIdConnectAuthenticationにより、OpenIdを利用したADログインになるため、 やりたいことの参考が出来なかったため、調べた事をメモする。

MVC Web ApplicationをMVCで認証なしで作成する。

プロジェクトにNugetから追加する。

  • Microsoft.Owin.Host.SystemWeb
    • OwinをIISで有効にするためのライブラリ
  • Microsoft.AspNet.Identity.Owin
    • 認証に必要なライブラリ

参照にPrincipalContextを使うためのライブラリを追加

  • System.DirectoryServices
  • System.DirectoryServices.AccountManagement

User

IUserを継承したユーザを作成する。 ただし、生成にはUserPrincpalから取得するようにする。

using Microsoft.AspNet.Identity;
using System.DirectoryServices.AccountManagement;
using System.Threading.Tasks;

public class AppUser : IUser<string>
{
    private UserPrincipal _adUser;

    public AppUser(UserPrincipal adUser)
    {
        this._adUser = adUser;
    }

    public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<AppUser> manager)
    {
        var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
        return userIdentity;
    }

    #region IUser<string> Members

    public string Id => _adUser.SamAccountName;

    public string UserName
    {
        get => _adUser.SamAccountName;
        set { throw new System.NotImplementedException(); }
    }
    #endregion
}

UserStore

ユーザ関連のCLUD処理を行うStoreクラスを定義する。 PrincipalContextからFindByIdentityにより、ユーザの取得を行う。

using Microsoft.AspNet.Identity;
using System.DirectoryServices.AccountManagement;
using System.Threading.Tasks;

public class AppUserStore :
    IUserStore<AppUser>,
    IUserStore<AppUser, string>
{
    private readonly PrincipalContext _context;
    private AppUserStore(PrincipalContext context)
    {
        _context = context;
    }

    // context.Get<PrincipalContext>()でADのcontextを取得
    public static AppUserStore Create(IdentityFactoryOptions<AppUserStore> options, IOwinContext context)
    {
        //  PrincipalContextをOwinContextから取得するようにする。StartUpでCreatePerOwinContextに追加しておく
        //var principalContext = new PrincipalContext(ContextType.Domain);
        var principalContext = context.Get<PrincipalContext>();
        return new AppUserStore(principalContext);
    }

    #region IUserStore<MyUser, string> Members

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

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

    public void Dispose()
    {
        throw new NotImplementedException();
    }

    // UserPrincipal.FindByIdentityにより、検索する
    public Task<AppUser> FindByIdAsync(string userId)
    {
        var user = UserPrincipal.n(_context, userId);
        return Task.FromResult<AppUser>(new AppUser(user));
    }

    public Task<AppUser> FindByNameAsync(string userName)
    {
        var user = UserPrincipal.FindByIdentity(_context, userName);
        return Task.FromResult<AppUser>(new AppUser(user));
    }

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

    #endregion
}

UserMananger

UserStoreクラスの総裁を行うクラス

using Microsoft.AspNet.Identity;
using System.DirectoryServices.AccountManagement;
using System.Threading.Tasks;

public class AppUserMananger : UserManager<AppUser>
{
    private readonly PrincipalContext _context;

    private AppUserMananger(IUserStore<AppUser> store, PrincipalContext context) : base(store)
    {
        _context = context;
    }
    
    public static AppUserMananger Create(IdentityFactoryOptions<AppUserMananger> options, IOwinContext context)
    {
        var userStore = context.Get<AppUserStore>();
        var principalContext = context.Get<PrincipalContext>();
        return new AppUserMananger(userStore, principalContext);
    }

    public override async Task<bool> CheckPasswordAsync(AppUser user, string password)
    {
        return await Task.FromResult(_context.ValidateCredentials(user.UserName, password, ContextOptions.Negotiate));
    }
}

SignInManager

ログイン状態を管理するクラス

using Microsoft.AspNet.Identity;
using System.DirectoryServices.AccountManagement;
using System.Threading.Tasks;

public class AppSignInManager : SignInManager<AppUser, string>
{
    UserManager<AppUser, string> _manager;

    private AppSignInManager(UserManager<AppUser, string> userManager, IAuthenticationManager authenticationManager) 
        : base(userManager, authenticationManager)
    {
        _manager = UserManager;
    }

    public static AppSignInManager Create(IdentityFactoryOptions<AppSignInManager> options, IOwinContext context)
    {
        // UserManagerはOwinContextから取得する。
        AppUserMananger manager = context.GetUserManager<AppUserMananger>();
        return new AppSignInManager(manager, context.Authentication);
    }

    public override Task<ClaimsIdentity> CreateUserIdentityAsync(AppUser user)
    {
        return user.GenerateUserIdentityAsync((AppUserMananger)UserManager);
    }

    public override async Task<SignInStatus> PasswordSignInAsync(string userName, string password, bool isPersistent, bool shouldLockout)
    {
        var result = SignInStatus.Failure;

        try
        {
            var user = await this.UserManager.FindAsync(userName, password);
            if (user != null)
            {
                await this.SignInAsync(user, isPersistent, true);
                result = SignInStatus.Success;
            }
        } catch
        {
            result = SignInStatus.Failure;
        }

        return result;
    }
}

StartUp

using Owin;
using Microsoft.Owin.Security;
using Microsoft.Owin.Security.Cookies;
using Microsoft.AspNet.Identity;
using System.Threading.Tasks;
using Microsoft.AspNet.Identity.Owin;
using Microsoft.Owin;
using System.DirectoryServices.AccountManagement;
using System.Security.Claims;

[assembly: OwinStartup(typeof(MvcWeb.OwinStartUp))]
public class OwinStartUp
{
    public void Configuration(IAppBuilder app)
    {
        app.CreatePerOwinContext(() => new PrincipalContext(ContextType.Domain));
        app.CreatePerOwinContext<AppUserMananger>(AppUserMananger.Create);
        app.CreatePerOwinContext<AppSignInManager>(AppSignInManager.Create);
        
        app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);

        app.UseCookieAuthentication(new CookieAuthenticationOptions()
        {
            AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
            LoginPath = new PathString("/Account/Login"),
            Provider = new CookieAuthenticationProvider
            {
                OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<AppUserMananger, AppUser>(
                    validateInterval: TimeSpan.FromMinutes(30),
                    regenerateIdentity: (manager,user) => user.GenerateUserIdentityAsync(manager))
            }
        });

    }
}

LoginViewModel

public class LoginViewModel
{
    [Required]
    [Display(Name = "ユーザID")]
    public string UserID { get; set; }

    [Required]
    [DataType(DataType.Password)]
    [Display(Name = "パスワード")]
    public string Password { get; set; }

    [Display(Name = "このアカウントを記憶する")]
    public bool RememberMe { get; set; }
}

AccountController

http://localhost/Acount/LoginからADのユーザ名、パスワードでログインする処理

[Authorize]
public class AccountController : Controller
{
    //
    // GET: /Account/Login
    [AllowAnonymous]
    public ActionResult Login(string returnUrl)
    {
        ViewBag.ReturnUrl = returnUrl;
        return View();
    }

    //
    // POST: /Account/Login
    [HttpPost]
    [AllowAnonymous]
    public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
    {
        if (!ModelState.IsValid)
        {
            return View(model);
        }

       AppSignInManager signInManager = HttpContext.GetOwinContext().Get<AppSignInManager>();
       AppUserMananger userManager = HttpContext.GetOwinContext().GetUserManager<AppUserMananger>();

        // これは、アカウント ロックアウトの基準となるログイン失敗回数を数えません。
        // パスワード入力失敗回数に基づいてアカウントがロックアウトされるように設定するには、shouldLockout: true に変更してください。
        var result = await signInManager.PasswordSignInAsync(model.UserID, model.Password, model.RememberMe, shouldLockout: false);
        switch (result)
        {
            case SignInStatus.Success:
                return RedirectToLocal(returnUrl);
            case SignInStatus.LockedOut:
                return View("Lockout");
            case SignInStatus.RequiresVerification:
                return RedirectToAction("SendCode", new { ReturnUrl = returnUrl, RememberMe = model.RememberMe });
            case SignInStatus.Failure:
            default:
                ModelState.AddModelError("", "無効なログイン試行です。");
                return View(model);
        }
    }
    private ActionResult RedirectToLocal(string returnUrl)
    {
        if (Url.IsLocalUrl(returnUrl))
        {
            return Redirect(returnUrl);
        }
        return RedirectToAction("Index", "Home");
    }
}

Login View

@using MvcWeb.Models
@model LoginViewModel
@{
    ViewBag.Title = "ログイン";
}

<h2>@ViewBag.Title.</h2>
<div class="row">
    <div class="col-md-8">
        <section id="loginForm">
            @using (Html.BeginForm("Login", "Account", new { ReturnUrl = ViewBag.ReturnUrl }, FormMethod.Post, new { @class = "form-horizontal", role = "form" }))
            {
                @Html.AntiForgeryToken()
                <h4>ローカル アカウントを使用してログインします。</h4>
                <hr />
                @Html.ValidationSummary(true, "", new { @class = "text-danger" })
                <div class="form-group">
                    @Html.LabelFor(m => m.UserID, new { @class = "col-md-2 control-label" })
                    <div class="col-md-10">
                        @Html.TextBoxFor(m => m.UserID, new { @class = "form-control" })
                        @Html.ValidationMessageFor(m => m.UserID, "", new { @class = "text-danger" })
                    </div>
                </div>
                <div class="form-group">
                    @Html.LabelFor(m => m.Password, new { @class = "col-md-2 control-label" })
                    <div class="col-md-10">
                        @Html.PasswordFor(m => m.Password, new { @class = "form-control" })
                        @Html.ValidationMessageFor(m => m.Password, "", new { @class = "text-danger" })
                    </div>
                </div>
                <div class="form-group">
                    <div class="col-md-offset-2 col-md-10">
                        <div class="checkbox">
                            @Html.CheckBoxFor(m => m.RememberMe)
                            @Html.LabelFor(m => m.RememberMe)
                        </div>
                    </div>
                </div>
                <div class="form-group">
                    <div class="col-md-offset-2 col-md-10">
                        <input type="submit" value="ログイン" class="btn btn-default" />
                    </div>
                </div>
                <p>
                    @Html.ActionLink("新しいユーザーとして登録する", "Register")
                </p>
                @* これを有効にする前に、パスワード リセット機能に対するアカウント確認を有効にしてください。
                    <p>
                        @Html.ActionLink("パスワードを忘れた場合", "ForgotPassword")
                    </p>*@
            }
        </section>
    </div>
    <div class="col-md-4">
        <section id="socialLoginForm">
            @Html.Partial("_ExternalLoginsListPartial", new ExternalLoginListViewModel { ReturnUrl = ViewBag.ReturnUrl })
        </section>
    </div>
</div>

@section Scripts {
    @Scripts.Render("~/bundles/jqueryval")
}

ASP.Net MVC Identity 認証

ASP.NET MVC プロジェクトを作成

認証を選択するとデフォルトの認証ロジックが貼るため、認証なしで作成する。

プロジェクトにNugetから追加する。

  • Microsoft.Owin.Host.SystemWeb
    • OwinをIISで有効にするためのライブラリ
  • Microsoft.AspNet.Identity.Owin
    • 認証に必要なライブラリ

AppUser

UserNameとPasswordのみの簡単なものにします。 適当なフォルダーに作成します。

public class AppUser : IUser<string>
{
    public string Id { get; set; } = Guid.NewGuid().ToString();

    public string UserName { get; set; }

    public string Password { get; set; }
}

UserManager

UserStoreクラスを制御するラッピングクラス

Create(IdentityFactoryOptions, IOwinContext)を実装して、インスタンス生成時に呼ばれる様にしている。 現状としてはどんな仕組みで呼び出し時にCreateでパラメータを渡さずに動いているのかがさっぱりわからない。

using Microsoft.AspNet.Identity;

public class AppUserManager : UserManager<AppUser>
{
    private AppUserManager(IUserStore<AppUser> store)
        : base(store)
    { }

    public static AppUserMananger Create(IdentityFactoryOptions<AppUserMananger> options, IOwinContext context)
    {
        return new AppUserMananger(new AppUserStore());
    }
}

IRole

public class AppRole : IRole<string>
{
    public string Id { get; set; } = Guid.NewGuid().ToString();

    public string Name { get; set ; }
}

RoleMananager

using Microsoft.AspNet.Identity;

public class AppRoleManager : RoleManager<AppRole>
{
    public AppRoleManager(IRoleStore<AppRole, string> store) 
        : base(store)
    { }
}

SignInManager

using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.Owin;
using Microsoft.Owin;
using Microsoft.Owin.Security;

public class AppSignInManager : SignInManager<AppUser, string>
{
    private AppSignInManager(UserManager<AppUser, string> userManager, IAuthenticationManager authenticationManager)
        : base(userManager, authenticationManager)
    { }
    public static AppSignInManager Create(IdentityFactoryOptions<AppSignInManager> options, IOwinContext context)
    {
        AppUserMananger manager = context.GetUserManager<AppUserMananger>();
       return new AppSignInManager(manager, context.Authentication);
    }
}

UserStore

ユーザー情報を扱うためのクラス(CRUD処理)

public class AppUserStore : 
    IUserStore<AppUser>, 
    IUserStore<AppUser,string>,
    IUserPasswordStore<AppUser,string>,
    IUserRoleStore<AppUser, string>,
    IRoleStore<AppRole, string>
{
    // DBの代わりにダミーデータ設定
    
    // ユーザマスタ
    private static List<AppUser> Users { get; } = new List<AppUser>
    {
        new AppUser { Id = "1", UserName = "user1", Password="abc" },
        new AppUser { Id = "2", UserName = "user2", Password="def" }
    };

    // ロールマスタ
    private static List<AppRole> Roles { get; } = new List<AppRole>
    {
        new AppRole { Id = "1", Name = "emploryee"},
        new AppRole { Id = "999", Name = "admin" }
    };

    // ユーザー・ロールマスタ
    private static List<Tuple<string, string>> UserRoleMap { get; } = new List<Tuple<string, string>>
    {
        Tuple.Create("1", "1"),
        Tuple.Create("2","999")
    };

    public Task AddToRoleAsync(AppUser user, string roleName)
    {
        var role = Roles.FirstOrDefault(x => x.Name == roleName);
        if (role == null) { throw new InvalidOperationException(); }

        var userRoleMap = UserRoleMap.FirstOrDefault(x => x.Item1 == user.Id && x.Item2 == role.Id);
        if (userRoleMap == null)
        {
            UserRoleMap.Add(Tuple.Create(user.Id, role.Id));
        }

        return Task.Delay(0);
    }

    public Task CreateAsync(AppUser user)
    {
        Users.Add(user);
        return Task.Delay(0);
    }

    public Task CreateAsync(AppRole role)
    {
        Roles.Add(role);
        return Task.Delay(0);
    }

    public Task DeleteAsync(AppUser user)
    {
        Users.Remove(Users.First(x => x.Id == user.Id));
        return Task.Delay(0);
    }

    public Task DeleteAsync(AppRole role)
    {
        Roles.Remove(Roles.First(x => x.Id == role.Id));
        return Task.Delay(0);
    }

    public void Dispose()
    {
    }

    public Task<AppUser> FindByIdAsync(string userId)
    {
        return Task.FromResult(Users.FirstOrDefault(u => u.Id == userId));
    }

    public Task<AppUser> FindByNameAsync(string userName)
    {
        return Task.FromResult(Users.FirstOrDefault(u => u.UserName == userName));
    }

    public Task<string> GetPasswordHashAsync(AppUser user)
    {
        return Task.FromResult(new PasswordHasher().HashPassword(user.Password));
    }

    public Task<IList<string>> GetRolesAsync(AppUser user)
    {
        IList<string> roleNames = UserRoleMap.Where(x => x.Item1 == user.Id)
            .Select(x => x.Item2)
            .Select(x => Roles.First(y => y.Id == x))
            .Select(x => x.Name)
            .ToList();
        return Task.FromResult(roleNames);
    }

    public Task<bool> HasPasswordAsync(AppUser user)
    {
        return Task.FromResult(user.Password != null);
    }

    public async Task<bool> IsInRoleAsync(AppUser user, string roleName)
    {
        var roles = await this.GetRolesAsync(user);
        return roles.FirstOrDefault(x => x.ToUpper() == roleName.ToUpper()) != null;
    }

    public Task RemoveFromRoleAsync(AppUser user, string roleName)
    {
        var role = Roles.FirstOrDefault(x => x.Name == roleName);
        if (role == null) { return Task.FromResult(default(object)); }
        var userRoleMap = UserRoleMap.FirstOrDefault(x => x.Item1 == user.Id && x.Item2 == role.Id);
        if (UserRoleMap != null)
        {
            UserRoleMap.Remove(userRoleMap);
        }
        return Task.Delay(0);
    }

    public Task SetPasswordHashAsync(AppUser user, string passwordHash)
    {
        user.Password = passwordHash;
        return Task.Delay(0);
    }

    public async Task UpdateAsync(AppUser user)
    {
        var target = await this.FindByIdAsync(user.Id);
        if (target == null) { return; }
        target.UserName = user.UserName;
        target.Password = user.Password;
        return;
    }

    public Task UpdateAsync(AppRole role)
    {
        var t = Roles.FirstOrDefault(r => r.Id == role.Id);
        if (t == null) { return Task.FromResult(default(object)); }
        t.Name = role.Name;
        return Task.FromResult(default(object));
    }

    Task<AppRole> IRoleStore<AppRole, string>.FindByIdAsync(string roleId)
    {
        return Task.FromResult(Roles.FirstOrDefault(u => u.Id == roleId));
    }

    Task<AppRole> IRoleStore<AppRole, string>.FindByNameAsync(string roleName)
    {
        return Task.FromResult(Roles.FirstOrDefault(u => u.Name == roleName));
    }
}

StartUp

ルートのStartUp.csを作成します。

using System;
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.Owin;
using Microsoft.Owin;
using Microsoft.Owin.Security;
using Microsoft.Owin.Security.Cookies;
using Owin;

[assembly: OwinStartup(typeof(MvcWeb2.OwinStartUp))]
namespace MvcWeb2
{
    public class OwinStartUp
    {
        public void Configuration(IAppBuilder app)
        {
            // 後から「HttpContext.GetOwinContext().Get」で取り出して使える。
            // Visual Studioのテンプレートに書いているコメントですが、だそうです。(DBは実装していないので、DbContextは作成していません。

            app.CreatePerOwinContext<AppUserMananger>(AppUserMananger.Create);
            app.CreatePerOwinContext<AppSignInManager>(AppSignInManager.Create);
            app.CreatePerOwinContext<AppRoleManager>((options, context) 
                => new AppRoleManager(context.Get<AppUserStore>()));
            app.CreatePerOwinContext<AppSignInManager>((options, context) 
                => new AppSignInManager(context.GetUserManager<AppUserManager>(), context.Authentication));

            app.UseCookieAuthentication(new CookieAuthenticationOptions
            {
                AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
                LoginPath = new PathString("/Auth/Login")
            });
        }
    }
}

Controller

using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.Owin;
using System.ComponentModel.DataAnnotations;
using System.Threading.Tasks;
using System.Web;
using System.Web.Mvc;

[Authorize]
public class AuthController : Controller
{
    private AppSignInManager _appSignInManager;
    private AppUserManager _appUserManager;

    public AppSignInManager SignInManager
    {
        get
        {
            // StartUp.csでCreatePerOwinContext登録したオブジェクトを取り出す。
            return _appSignInManager ?? HttpContext.GetOwinContext().Get<AppSignInManager>();
        }
        private set
        {
            _appSignInManager = value;
        }
    }

    public AppUserManager UserManager
    {
        get
        {
            // StartUp.csでCreatePerOwinContext登録したオブジェクトを取り出す。
            return _appUserManager ?? HttpContext.GetOwinContext().Get<AppUserManager>();
        }
        private set
        {
            _appUserManager = value;
        }
    }

    // GET: Auth/Login
    [AllowAnonymous]
    public ActionResult Login(string returnUrl)
    {
        ViewBag.ReturnUrl = returnUrl;
        return View(new LoginViewModel());
    }

    //
    // POST: /Auth/Login
    [AllowAnonymous]
    [HttpPost]
    public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
    {
        if (!this.ModelState.IsValid)
        {
            ViewBag.ReturnUrl = returnUrl;
            return View(model);
        }

        // 認証 (SignInManagerを使用しない場合)
        //var userManager = new UserManager<AppUser>(new AppUserStore());
        //var user = await userManager.FindAsync(model.UserName, model.Password);
        //if (user == null)
        //{
        //    // 認証失敗したらエラーメッセージを設定してログイン画面を表示する
        //    this.ModelState.AddModelError("", "ユーザ名かパスワードが違います");
        //    ViewBag.ReturnUrl = returnUrl;
        //    return View(model);
        //}

        //// クレームベースのIDを作って
        //var identify = await userManager.CreateIdentityAsync(
        //    user,
        //    DefaultAuthenticationTypes.ApplicationCookie);

        //// 認証情報を設定
        //var authentication = this.HttpContext.GetOwinContext().Authentication;
        //authentication.SignIn(identify);

        // SignInManagerを利用する場合
        var user = await UserManager.FindAsync(model.UserName, model.Password);
        if (user == null) {
            this.ModelState.AddModelError("", "ユーザ名かパスワードが違います");
            return View(model);
        }

        await SignInManager.SignInAsync(user, false, false);

        // 元のページへリダイレクト
        return Redirect(returnUrl);
    }
}

Auth/LoginView

@model MvcWeb2.Controllers.LoginViewModel

@{
    ViewBag.Title = "Login";
}

<h2>Login</h2>
@Html.ValidationSummary(true)

@* ReturnUrlをパラメータに渡すようにしたフォームを作る *@
@using (Html.BeginForm(new { ReturnUrl = ViewBag.ReturnUrl }))
{
    <div class="form-group">
        @Html.LabelFor(m => m.UserName)
        @Html.TextBoxFor(m => m.UserName)
        @Html.ValidationMessageFor(m => m.UserName)
    </div>
    <div class="form-group">
        @Html.LabelFor(m => m.Password)
        @Html.PasswordFor(m => m.Password)
        @Html.ValidationMessageFor(m => m.Password)
    </div>

    <input type="submit" value="ログイン" class="btn-default" />
}

Auth/LoginViewModel

public class LoginViewModel
{
    [Required(ErrorMessage = "ユーザー名を入れてください")]
    [Display(Name = "ユーザー名")]
    public string UserName { get; set; }

    [Required(ErrorMessage = "パスワードを入れてください")]
    [Display(Name = "パスワード")]
    public string Password { get; set; }
}

SQL ServerのIdentityカラムの最大値

Identity カラム設定

int型で生成した場合、最大値は「21億(正確には2,147,483,647)」まで採番可能になる。

CREATE TABLE [table1] (
[IDNO] int NOT NULL IDENTITY (1, 1),
CONSTRAINT [PK_T_SYSTEM_MON_NEW_HISTORY] PRIMARY KEY CLUSTERED (
 [IDNO] ASC
 )
)

ただし、最大値を超えた場合はエラーとなる。

Identity 最大値を超えた場合

  • 型をより大きい型(bigintなど)へ変更する。
  • DBCC CHECKIDENTコマンドで番号を初期化する。
-- ID値を0に設定
DBCC CHECKIDENT ('table1', RESEED, 0);
-- ID値を100に設定
DBCC CHECKIDENT ('table1', RESEED, 100);