<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>Feed Name</title>
  <link href="http://timschreiber.com/"/>
  <link type="application/atom+xml" rel="self" href="http://timschreiber.com/atom.xml"/>
  <updated>2022-09-10T02:10:59+00:00</updated>
  <id>http://timschreiber.com/</id>
  <author>
    <name>Timothy P. Schreiber</name>
    <email>http://timschreiber.com/contact/</email>
  </author>

  
  <entry>
    <id>http://timschreiber.com/health/2019/04/13/first-post/</id>
    <link type="text/html" rel="alternate" href="http://timschreiber.com//health/2019/04/13/first-post/"/>
    <title>Weight Loss First Post</title>
    <updated>2019-04-13T00:00:00+00:00</updated>
    <author>
      <name>Timothy P. Schreiber</name>
      <uri>http://timschreiber.com/</uri>
    </author>
    <content type="html">&lt;p&gt;This is the fist post of the new food category, where I will focus on my weight loss journey.&lt;/p&gt;

</content>
  </entry>
  
  <entry>
    <id>http://timschreiber.com/2018/05/08/aspnet-core-identity-with-patterns-3/</id>
    <link type="text/html" rel="alternate" href="http://timschreiber.com//2018/05/08/aspnet-core-identity-with-patterns-3/"/>
    <title>ASP.NET Core Identity with Patterns (Part 3 of 3)</title>
    <updated>2018-05-08T16:04:00+00:00</updated>
    <author>
      <name>Timothy P. Schreiber</name>
      <uri>http://timschreiber.com/</uri>
    </author>
    <content type="html">&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/2018/05/07/aspnet-core-identity-with-patterns/&quot;&gt;Part 1&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2018/05/07/aspnet-core-identity-with-patterns-2/&quot;&gt;Part 2&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Part 3&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The source code for this series of posts is available at on my GitHub: &lt;a href=&quot;https://github.com/timschreiber/ASP.NET-Core-Identity-Example&quot;&gt;https://github.com/timschreiber/ASP.NET-Core-Identity-Example&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;NOTES:&lt;/em&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;em&gt;This series of posts requires a functional understanding of ASP.NET Core Identity If you haven’t had at least some kind of exposure, this you should probably start &lt;a href=&quot;https://docs.microsoft.com/en-us/aspnet/core/security/authentication/identity?view=aspnetcore-2.1&amp;amp;tabs=visual-studio%2Caspnetcore2x&quot;&gt;here&lt;/a&gt;.&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;If you haven’t read &lt;a href=&quot;/2015/01/14/persistence-ignorant-asp-net-identity-with-patterns-part-1/&quot;&gt;my previous posts&lt;/a&gt;, I’d suggest you stop here and read at least the first one to understand the problems I had with ASP.NET Identity 2.0 and how I solved them.&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In Part 1, we started building our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AspNetCoreIdentityExample&lt;/code&gt; solution, beginning with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AspNetCoreIdentityExample.Web project&lt;/code&gt;. First we got it running with the out-of-the-box Entity Framework implementation, then we broke it in preparation for our custom, persistence-ignorant version. In Part 2, we added the Domain and Data Layers, and now in this step, we’re going back to the Web project to build out the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CustomUserStore&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CustomRoleStore&lt;/code&gt; classes and to wire everything up into a working application.&lt;/p&gt;

&lt;h2 id=&quot;back-to-the-web-project&quot;&gt;Back to the Web Project&lt;/h2&gt;

&lt;p&gt;Now that we have our Domain and Data layers in place, we can build out the two stores required by ASP.NET Core Identity. Of these two, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CustomUserStore&lt;/code&gt; class can get really big, depending on the required features. Lucky for you, I already did all the hard work:&lt;/p&gt;

&lt;h3 id=&quot;the-stores&quot;&gt;The Stores&lt;/h3&gt;

&lt;h4 id=&quot;customrolestorecs&quot;&gt;CustomRoleStore.cs&lt;/h4&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;using AspNetCoreIdentityExample.Domain;
using AspNetCoreIdentityExample.Domain.Entities;
using Microsoft.AspNetCore.Identity;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Threading;
using System.Threading.Tasks;

namespace AspNetCoreIdentityExample.Web.Identity
{
    public class CustomRoleStore :
        IRoleStore&amp;lt;IdentityRole&amp;gt;,
        IRoleClaimStore&amp;lt;IdentityRole&amp;gt;
    {
        private readonly IUnitOfWork _unitOfWork;

        public CustomRoleStore(IUnitOfWork unitOfWork)
        {
            _unitOfWork = unitOfWork;
        }

        #region IRoleStore&amp;lt;IdentityRole&amp;gt; Members
        public Task&amp;lt;IdentityResult&amp;gt; CreateAsync(IdentityRole role, CancellationToken cancellationToken)
        {
            try
            {
                if (cancellationToken != null)
                    cancellationToken.ThrowIfCancellationRequested();

                if (role == null)
                    throw new ArgumentNullException(nameof(role));

                var roleEntity = getRoleEntity(role);

                _unitOfWork.RoleRepository.Add(roleEntity);
                _unitOfWork.Commit();

                return Task.FromResult(IdentityResult.Success);
            }
            catch(Exception ex)
            {
                return Task.FromResult(IdentityResult.Failed(new IdentityError { Code = ex.Message, Description = ex.Message }));
            }
        }

        public Task&amp;lt;IdentityResult&amp;gt; DeleteAsync(IdentityRole role, CancellationToken cancellationToken)
        {
            try
            {
                if (cancellationToken != null)
                    cancellationToken.ThrowIfCancellationRequested();

                if (role == null)
                    throw new ArgumentNullException(nameof(role));

                _unitOfWork.RoleRepository.Remove(role.Id);
                _unitOfWork.Commit();

                return Task.FromResult(IdentityResult.Success);
            }
            catch (Exception ex)
            {
                return Task.FromResult(IdentityResult.Failed(new IdentityError { Code = ex.Message, Description = ex.Message }));
            }
        }

        public Task&amp;lt;IdentityRole&amp;gt; FindByIdAsync(string roleId, CancellationToken cancellationToken)
        {
            if (cancellationToken != null)
                cancellationToken.ThrowIfCancellationRequested();

            if (string.IsNullOrWhiteSpace(roleId))
                throw new ArgumentNullException(nameof(roleId));

            if(!Guid.TryParse(roleId, out Guid id))
                throw new ArgumentOutOfRangeException(nameof(roleId), $&quot;{nameof(roleId)} is not a valid GUID&quot;);

            var roleEntity = _unitOfWork.RoleRepository.Find(id.ToString());
            return Task.FromResult(getIdentityRole(roleEntity));
        }

        public Task&amp;lt;IdentityRole&amp;gt; FindByNameAsync(string normalizedRoleName, CancellationToken cancellationToken)
        {
            if (cancellationToken != null)
                cancellationToken.ThrowIfCancellationRequested();

            if (string.IsNullOrWhiteSpace(normalizedRoleName))
                throw new ArgumentNullException(nameof(normalizedRoleName));

            var roleEntity = _unitOfWork.RoleRepository.FindByName(normalizedRoleName);
            return Task.FromResult(getIdentityRole(roleEntity));
        }

        public Task&amp;lt;string&amp;gt; GetNormalizedRoleNameAsync(IdentityRole role, CancellationToken cancellationToken)
        {
            if (cancellationToken != null)
                cancellationToken.ThrowIfCancellationRequested();

            if (role == null)
                throw new ArgumentNullException(nameof(role));

            return Task.FromResult(role.NormalizedName);
        }

        public Task&amp;lt;string&amp;gt; GetRoleIdAsync(IdentityRole role, CancellationToken cancellationToken)
        {
            if (cancellationToken != null)
                cancellationToken.ThrowIfCancellationRequested();

            if (role == null)
                throw new ArgumentNullException(nameof(role));

            return Task.FromResult(role.Id);
        }

        public Task&amp;lt;string&amp;gt; GetRoleNameAsync(IdentityRole role, CancellationToken cancellationToken)
        {
            if (cancellationToken != null)
                cancellationToken.ThrowIfCancellationRequested();

            if (role == null)
                throw new ArgumentNullException(nameof(role));

            return Task.FromResult(role.Name);
        }

        public Task SetNormalizedRoleNameAsync(IdentityRole role, string normalizedName, CancellationToken cancellationToken)
        {
            if (cancellationToken != null)
                cancellationToken.ThrowIfCancellationRequested();

            if (role == null)
                throw new ArgumentNullException(nameof(role));

            role.NormalizedName = normalizedName;

            return Task.CompletedTask;
        }

        public Task SetRoleNameAsync(IdentityRole role, string roleName, CancellationToken cancellationToken)
        {
            if (cancellationToken != null)
                cancellationToken.ThrowIfCancellationRequested();

            if (role == null)
                throw new ArgumentNullException(nameof(role));

            role.Name = roleName;

            return Task.CompletedTask;
        }

        public Task&amp;lt;IdentityResult&amp;gt; UpdateAsync(IdentityRole role, CancellationToken cancellationToken)
        {
            try
            {
                if (cancellationToken != null)
                    cancellationToken.ThrowIfCancellationRequested();

                if (role == null)
                    throw new ArgumentNullException(nameof(role));

                var roleEntity = getRoleEntity(role);

                _unitOfWork.RoleRepository.Update(roleEntity);
                _unitOfWork.Commit();

                return Task.FromResult(IdentityResult.Success);
            }
            catch (Exception ex)
            {
                return Task.FromResult(IdentityResult.Failed(new IdentityError { Code = ex.Message, Description = ex.Message }));
            }
        }

        public void Dispose()
        {
            // Lifetimes of dependencies are managed by the IoC container, so disposal here is unnecessary.
        }
        #endregion

        #region IRoleClaimStore&amp;lt;IdentityRole&amp;gt; Members
        public Task&amp;lt;IList&amp;lt;Claim&amp;gt;&amp;gt; GetClaimsAsync(IdentityRole role, CancellationToken cancellationToken = default(CancellationToken))
        {
            if (cancellationToken != null)
                cancellationToken.ThrowIfCancellationRequested();

            if (role == null)
                throw new ArgumentNullException(nameof(role));

            IList&amp;lt;Claim&amp;gt; result = _unitOfWork.RoleClaimRepository.FindByRoleId(role.Id).Select(x =&amp;gt; new Claim(x.ClaimType, x.ClaimValue)).ToList();

            return Task.FromResult(result);
        }

        public Task AddClaimAsync(IdentityRole role, Claim claim, CancellationToken cancellationToken = default(CancellationToken))
        {
            if (cancellationToken != null)
                cancellationToken.ThrowIfCancellationRequested();

            if (role == null)
                throw new ArgumentNullException(nameof(role));

            if (claim == null)
                throw new ArgumentNullException(nameof(claim));

            var roleClaimEntity = new RoleClaim
            {
                ClaimType = claim.Type,
                ClaimValue = claim.Value,
                RoleId = role.Id
            };

            _unitOfWork.RoleClaimRepository.Add(roleClaimEntity);
            _unitOfWork.Commit();

            return Task.CompletedTask;
        }

        public Task RemoveClaimAsync(IdentityRole role, Claim claim, CancellationToken cancellationToken = default(CancellationToken))
        {
            if (cancellationToken != null)
                cancellationToken.ThrowIfCancellationRequested();

            if (role == null)
                throw new ArgumentNullException(nameof(role));

            if (claim == null)
                throw new ArgumentNullException(nameof(claim));

            var roleClaimEntity = _unitOfWork.RoleClaimRepository.FindByRoleId(role.Id)
                .SingleOrDefault(x =&amp;gt; x.ClaimType == claim.Type &amp;amp;&amp;amp; x.ClaimValue == claim.Value);

            if(roleClaimEntity != null)
            {
                _unitOfWork.RoleClaimRepository.Remove(roleClaimEntity.Id);
                _unitOfWork.Commit();
            }

            return Task.CompletedTask;
        }
        #endregion

        #region Private Methods
        private Role getRoleEntity(IdentityRole value)
        {
            return value == null
                ? default(Role)
                : new Role
                {
                    ConcurrencyStamp = value.ConcurrencyStamp,
                    Id = value.Id,
                    Name = value.Name,
                    NormalizedName = value.NormalizedName
                };
        }

        private IdentityRole getIdentityRole(Role value)
        {
            return value == null
                ? default(IdentityRole)
                : new IdentityRole
                {
                    ConcurrencyStamp = value.ConcurrencyStamp,
                    Id = value.Id,
                    Name = value.Name,
                    NormalizedName = value.NormalizedName
                };
        }
        #endregion
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;customuserstorecs&quot;&gt;CustomUserStore.cs&lt;/h4&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;using AspNetCoreIdentityExample.Domain;
using AspNetCoreIdentityExample.Domain.Entities;
using AspNetCoreIdentityExample.Web.Models;
using Microsoft.AspNetCore.Identity;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Threading;
using System.Threading.Tasks;

namespace AspNetCoreIdentityExample.Web.Identity
{
    public class CustomUserStore :
        IUserStore&amp;lt;ApplicationUser&amp;gt;,
        IUserPasswordStore&amp;lt;ApplicationUser&amp;gt;,
        IUserEmailStore&amp;lt;ApplicationUser&amp;gt;,
        IUserLoginStore&amp;lt;ApplicationUser&amp;gt;,
        IUserRoleStore&amp;lt;ApplicationUser&amp;gt;,
        IUserSecurityStampStore&amp;lt;ApplicationUser&amp;gt;,
        IUserClaimStore&amp;lt;ApplicationUser&amp;gt;,
        IUserAuthenticationTokenStore&amp;lt;ApplicationUser&amp;gt;,
        IUserTwoFactorStore&amp;lt;ApplicationUser&amp;gt;,
        IUserPhoneNumberStore&amp;lt;ApplicationUser&amp;gt;,
        IUserLockoutStore&amp;lt;ApplicationUser&amp;gt;,
        IQueryableUserStore&amp;lt;ApplicationUser&amp;gt;
    {
        private readonly IUnitOfWork _unitOfWork;

        public CustomUserStore(IUnitOfWork unitOfWork)
        {
            _unitOfWork = unitOfWork;
        }

        #region IQueryableUserStore&amp;lt;ApplicationUser&amp;gt; Members
        public IQueryable&amp;lt;ApplicationUser&amp;gt; Users
        {
            get
            {
                return _unitOfWork.UserRepository.All()
                    .Select(x =&amp;gt; getApplicationUser(x))
                    .AsQueryable();
            }
        }
        #endregion

        #region IUserStore&amp;lt;ApplicationUser&amp;gt; Members
        public Task&amp;lt;IdentityResult&amp;gt; CreateAsync(ApplicationUser user, CancellationToken cancellationToken)
        {
            try
            {
                if (cancellationToken != null)
                    cancellationToken.ThrowIfCancellationRequested();

                if (user == null)
                    throw new ArgumentNullException(nameof(user));

                var userEntity = getUserEntity(user);

                _unitOfWork.UserRepository.Add(userEntity);
                _unitOfWork.Commit();

                return Task.FromResult(IdentityResult.Success);
            }
            catch (Exception ex)
            {
                return Task.FromResult(IdentityResult.Failed(new IdentityError { Code = ex.Message, Description = ex.Message }));
            }
        }

        public Task&amp;lt;IdentityResult&amp;gt; DeleteAsync(ApplicationUser user, CancellationToken cancellationToken)
        {
            try
            {
                if (cancellationToken != null)
                    cancellationToken.ThrowIfCancellationRequested();

                if (user == null)
                    throw new ArgumentNullException(nameof(user));

                _unitOfWork.UserRepository.Remove(user.Id);
                _unitOfWork.Commit();

                return Task.FromResult(IdentityResult.Success);
            }
            catch (Exception ex)
            {
                return Task.FromResult(IdentityResult.Failed(new IdentityError { Code = ex.Message, Description = ex.Message }));
            }
        }

        public Task&amp;lt;ApplicationUser&amp;gt; FindByIdAsync(string userId, CancellationToken cancellationToken)
        {
            if (cancellationToken != null)
                cancellationToken.ThrowIfCancellationRequested();

            if (string.IsNullOrWhiteSpace(userId))
                throw new ArgumentNullException(nameof(userId));

            if (!Guid.TryParse(userId, out Guid id))
                throw new ArgumentOutOfRangeException(nameof(userId), $&quot;{nameof(userId)} is not a valid GUID&quot;);

            var userEntity = _unitOfWork.UserRepository.Find(id.ToString());

            return Task.FromResult(getApplicationUser(userEntity));
        }

        public Task&amp;lt;ApplicationUser&amp;gt; FindByNameAsync(string normalizedUserName, CancellationToken cancellationToken)
        {
            if (cancellationToken != null)
                cancellationToken.ThrowIfCancellationRequested();

            var userEntity = _unitOfWork.UserRepository.FindByNormalizedUserName(normalizedUserName);

            return Task.FromResult(getApplicationUser(userEntity));
        }

        public Task&amp;lt;string&amp;gt; GetNormalizedUserNameAsync(ApplicationUser user, CancellationToken cancellationToken)
        {
            if (cancellationToken != null)
                cancellationToken.ThrowIfCancellationRequested();

            if (user == null)
                throw new ArgumentNullException(nameof(user));

            return Task.FromResult(user.NormalizedUserName);
        }

        public Task&amp;lt;string&amp;gt; GetUserIdAsync(ApplicationUser user, CancellationToken cancellationToken)
        {
            if (cancellationToken != null)
                cancellationToken.ThrowIfCancellationRequested();

            if (user == null)
                throw new ArgumentNullException(nameof(user));

            return Task.FromResult(user.Id);
        }

        public Task&amp;lt;string&amp;gt; GetUserNameAsync(ApplicationUser user, CancellationToken cancellationToken)
        {
            if (cancellationToken != null)
                cancellationToken.ThrowIfCancellationRequested();

            if (user == null)
                throw new ArgumentNullException(nameof(user));

            return Task.FromResult(user.UserName);
        }

        public Task SetNormalizedUserNameAsync(ApplicationUser user, string normalizedName, CancellationToken cancellationToken)
        {
            if (cancellationToken != null)
                cancellationToken.ThrowIfCancellationRequested();

            if (user == null)
                throw new ArgumentNullException(nameof(user));

            user.NormalizedUserName = normalizedName;

            return Task.CompletedTask;
        }

        public Task SetUserNameAsync(ApplicationUser user, string userName, CancellationToken cancellationToken)
        {
            if (cancellationToken != null)
                cancellationToken.ThrowIfCancellationRequested();

            if (user == null)
                throw new ArgumentNullException(nameof(user));

            user.UserName = userName;

            return Task.CompletedTask;
        }

        public Task&amp;lt;IdentityResult&amp;gt; UpdateAsync(ApplicationUser user, CancellationToken cancellationToken)
        {
            try
            {
                if (cancellationToken != null)
                    cancellationToken.ThrowIfCancellationRequested();

                if (user == null)
                    throw new ArgumentNullException(nameof(user));

                var userEntity = getUserEntity(user);

                _unitOfWork.UserRepository.Update(userEntity);
                _unitOfWork.Commit();

                return Task.FromResult(IdentityResult.Success);
            }
            catch (Exception ex)
            {
                return Task.FromResult(IdentityResult.Failed(new IdentityError { Code = ex.Message, Description = ex.Message }));
            }
        }

        public void Dispose()
        {
            // Lifetimes of dependencies are managed by the IoC container, so disposal here is unnecessary.
        }
        #endregion

        #region IUserPasswordStore&amp;lt;ApplicationUser&amp;gt; Members
        public Task SetPasswordHashAsync(ApplicationUser user, string passwordHash, CancellationToken cancellationToken)
        {
            if (cancellationToken != null)
                cancellationToken.ThrowIfCancellationRequested();

            if (user == null)
                throw new ArgumentNullException(nameof(user));

            user.PasswordHash = passwordHash;

            return Task.CompletedTask;
        }

        public Task&amp;lt;string&amp;gt; GetPasswordHashAsync(ApplicationUser user, CancellationToken cancellationToken)
        {
            if (cancellationToken != null)
                cancellationToken.ThrowIfCancellationRequested();

            if (user == null)
                throw new ArgumentNullException(nameof(user));

            return Task.FromResult(user.PasswordHash);
        }

        public Task&amp;lt;bool&amp;gt; HasPasswordAsync(ApplicationUser user, CancellationToken cancellationToken)
        {
            if (cancellationToken != null)
                cancellationToken.ThrowIfCancellationRequested();

            if (user == null)
                throw new ArgumentNullException(nameof(user));

            return Task.FromResult(!string.IsNullOrWhiteSpace(user.PasswordHash));
        }
        #endregion

        #region IUserEmailStore&amp;lt;ApplicationUser&amp;gt; Members
        public Task SetEmailAsync(ApplicationUser user, string email, CancellationToken cancellationToken)
        {
            if (cancellationToken != null)
                cancellationToken.ThrowIfCancellationRequested();

            if (user == null)
                throw new ArgumentNullException(nameof(user));

            user.Email = email;

            return Task.CompletedTask;
        }

        public Task&amp;lt;string&amp;gt; GetEmailAsync(ApplicationUser user, CancellationToken cancellationToken)
        {
            if (cancellationToken != null)
                cancellationToken.ThrowIfCancellationRequested();

            if (user == null)
                throw new ArgumentNullException(nameof(user));

            return Task.FromResult(user.Email);
        }

        public Task&amp;lt;bool&amp;gt; GetEmailConfirmedAsync(ApplicationUser user, CancellationToken cancellationToken)
        {
            if (cancellationToken != null)
                cancellationToken.ThrowIfCancellationRequested();

            if (user == null)
                throw new ArgumentNullException(nameof(user));

            return Task.FromResult(user.EmailConfirmed);
        }

        public Task SetEmailConfirmedAsync(ApplicationUser user, bool confirmed, CancellationToken cancellationToken)
        {
            if (cancellationToken != null)
                cancellationToken.ThrowIfCancellationRequested();

            if (user == null)
                throw new ArgumentNullException(nameof(user));

            user.EmailConfirmed = confirmed;

            return Task.CompletedTask;
        }

        public Task&amp;lt;ApplicationUser&amp;gt; FindByEmailAsync(string normalizedEmail, CancellationToken cancellationToken)
        {
            if (string.IsNullOrWhiteSpace(normalizedEmail))
                throw new ArgumentNullException(nameof(normalizedEmail));

            var userEntity = _unitOfWork.UserRepository.FindByNormalizedEmail(normalizedEmail);

            return Task.FromResult(getApplicationUser(userEntity));
        }

        public Task&amp;lt;string&amp;gt; GetNormalizedEmailAsync(ApplicationUser user, CancellationToken cancellationToken)
        {
            if (cancellationToken != null)
                cancellationToken.ThrowIfCancellationRequested();

            if (user == null)
                throw new ArgumentNullException(nameof(user));

            return Task.FromResult(user.NormalizedEmail);
        }

        public Task SetNormalizedEmailAsync(ApplicationUser user, string normalizedEmail, CancellationToken cancellationToken)
        {
            if (cancellationToken != null)
                cancellationToken.ThrowIfCancellationRequested();

            if (user == null)
                throw new ArgumentNullException(nameof(user));

            user.NormalizedEmail = normalizedEmail;

            return Task.CompletedTask;
        }
        #endregion

        #region IUserLoginStore&amp;lt;ApplicationUser&amp;gt; Members
        public Task AddLoginAsync(ApplicationUser user, UserLoginInfo login, CancellationToken cancellationToken)
        {
            if (cancellationToken != null)
                cancellationToken.ThrowIfCancellationRequested();

            if (user == null)
                throw new ArgumentNullException(nameof(user));

            if (login == null)
                throw new ArgumentNullException(nameof(login));

            if (string.IsNullOrWhiteSpace(login.LoginProvider))
                throw new ArgumentNullException(nameof(login.LoginProvider));

            if (string.IsNullOrWhiteSpace(login.ProviderKey))
                throw new ArgumentNullException(nameof(login.ProviderKey));

            var loginEntity = new UserLogin
            {
                LoginProvider = login.LoginProvider,
                ProviderDisplayName = login.ProviderDisplayName,
                ProviderKey = login.ProviderKey,
                UserId = user.Id
            };

            _unitOfWork.UserLoginRepository.Add(loginEntity);
            _unitOfWork.Commit();

            return Task.CompletedTask;
        }

        public Task RemoveLoginAsync(ApplicationUser user, string loginProvider, string providerKey, CancellationToken cancellationToken)
        {
            if (cancellationToken != null)
                cancellationToken.ThrowIfCancellationRequested();

            if (user == null)
                throw new ArgumentNullException(nameof(user));

            if (string.IsNullOrWhiteSpace(loginProvider))
                throw new ArgumentNullException(nameof(loginProvider));

            if (string.IsNullOrWhiteSpace(providerKey))
                throw new ArgumentNullException(nameof(providerKey));

            _unitOfWork.UserLoginRepository.Remove(new UserLoginKey { LoginProvider = loginProvider, ProviderKey = providerKey });
            _unitOfWork.Commit();
            
            return Task.CompletedTask;
        }

        public Task&amp;lt;IList&amp;lt;UserLoginInfo&amp;gt;&amp;gt; GetLoginsAsync(ApplicationUser user, CancellationToken cancellationToken)
        {
            if (cancellationToken != null)
                cancellationToken.ThrowIfCancellationRequested();

            if (user == null)
                throw new ArgumentNullException(nameof(user));

            IList&amp;lt;UserLoginInfo&amp;gt; result = _unitOfWork.UserLoginRepository.FindByUserId(user.Id)
                .Select(x =&amp;gt; new UserLoginInfo(x.LoginProvider, x.ProviderKey, x.ProviderDisplayName))
                .ToList();

            return Task.FromResult(result);
        }

        public Task&amp;lt;ApplicationUser&amp;gt; FindByLoginAsync(string loginProvider, string providerKey, CancellationToken cancellationToken)
        {
            if (cancellationToken != null)
                cancellationToken.ThrowIfCancellationRequested();

            if (string.IsNullOrWhiteSpace(loginProvider))
                throw new ArgumentNullException(nameof(loginProvider));

            if (string.IsNullOrWhiteSpace(providerKey))
                throw new ArgumentNullException(nameof(providerKey));

            var loginEntity = _unitOfWork.UserLoginRepository.Find(new UserLoginKey { LoginProvider = loginProvider, ProviderKey = providerKey });
            if (loginEntity == null)
                return Task.FromResult(default(ApplicationUser));

            var userEntity = _unitOfWork.UserRepository.Find(loginEntity.UserId);

            return Task.FromResult(getApplicationUser(userEntity));
        }
        #endregion

        #region IUserRoleStore&amp;lt;ApplicationUser&amp;gt; Members
        public Task AddToRoleAsync(ApplicationUser user, string roleName, CancellationToken cancellationToken)
        {
            if (cancellationToken != null)
                cancellationToken.ThrowIfCancellationRequested();

            if (user == null)
                throw new ArgumentNullException(nameof(user));

            if (string.IsNullOrWhiteSpace(roleName))
                throw new ArgumentNullException(nameof(roleName));

            _unitOfWork.UserRoleRepository.Add(user.Id, roleName);
            _unitOfWork.Commit();

            return Task.CompletedTask;
        }

        public Task RemoveFromRoleAsync(ApplicationUser user, string roleName, CancellationToken cancellationToken)
        {
            if (cancellationToken != null)
                cancellationToken.ThrowIfCancellationRequested();

            if (user == null)
                throw new ArgumentNullException(nameof(user));

            if (string.IsNullOrWhiteSpace(roleName))
                throw new ArgumentNullException(nameof(roleName));

            _unitOfWork.UserRoleRepository.Remove(user.Id, roleName);

            _unitOfWork.Commit();

            return Task.CompletedTask;
        }

        public Task&amp;lt;IList&amp;lt;string&amp;gt;&amp;gt; GetRolesAsync(ApplicationUser user, CancellationToken cancellationToken)
        {
            if (cancellationToken != null)
                cancellationToken.ThrowIfCancellationRequested();

            if (user == null)
                throw new ArgumentNullException(nameof(user));

            IList&amp;lt;string&amp;gt; result = _unitOfWork.UserRoleRepository.GetRoleNamesByUserId(user.Id)
                .ToList();

            return Task.FromResult(result);
        }

        public Task&amp;lt;bool&amp;gt; IsInRoleAsync(ApplicationUser user, string roleName, CancellationToken cancellationToken)
        {
            if (cancellationToken != null)
                cancellationToken.ThrowIfCancellationRequested();

            if (user == null)
                throw new ArgumentNullException(nameof(user));

            if (string.IsNullOrWhiteSpace(roleName))
                throw new ArgumentNullException(nameof(roleName));

            var result = _unitOfWork.UserRoleRepository.GetRoleNamesByUserId(user.Id).Any(x =&amp;gt; x == roleName);

            return Task.FromResult(result);
        }

        public Task&amp;lt;IList&amp;lt;ApplicationUser&amp;gt;&amp;gt; GetUsersInRoleAsync(string roleName, CancellationToken cancellationToken)
        {
            if (cancellationToken != null)
                cancellationToken.ThrowIfCancellationRequested();

            if (string.IsNullOrWhiteSpace(roleName))
                throw new ArgumentNullException(nameof(roleName));

            IList&amp;lt;ApplicationUser&amp;gt; result = _unitOfWork.UserRoleRepository.GetUsersByRoleName(roleName)
                .Select(x =&amp;gt; getApplicationUser(x))
                .ToList();

            return Task.FromResult(result);
        }
        #endregion

        #region IUserSecurityStampStore&amp;lt;ApplicationUser&amp;gt; Members
        public Task SetSecurityStampAsync(ApplicationUser user, string stamp, CancellationToken cancellationToken)
        {
            if (cancellationToken != null)
                cancellationToken.ThrowIfCancellationRequested();

            if (user == null)
                throw new ArgumentNullException(nameof(user));

            user.SecurityStamp = stamp;

            return Task.CompletedTask;
        }

        public Task&amp;lt;string&amp;gt; GetSecurityStampAsync(ApplicationUser user, CancellationToken cancellationToken)
        {
            if (cancellationToken != null)
                cancellationToken.ThrowIfCancellationRequested();

            if (user == null)
                throw new ArgumentNullException(nameof(user));

            return Task.FromResult(user.SecurityStamp);
        }
        #endregion

        #region IUserClaimStore&amp;lt;ApplicationUser&amp;gt; Members
        public Task&amp;lt;IList&amp;lt;Claim&amp;gt;&amp;gt; GetClaimsAsync(ApplicationUser user, CancellationToken cancellationToken)
        {
            if (cancellationToken != null)
                cancellationToken.ThrowIfCancellationRequested();

            if (user == null)
                throw new ArgumentNullException(nameof(user));

            IList&amp;lt;Claim&amp;gt; result = _unitOfWork.UserClaimRepository.GetByUserId(user.Id)
                .Select(x =&amp;gt; new Claim(x.ClaimType, x.ClaimValue)).ToList();

            return Task.FromResult(result);
        }

        public Task AddClaimsAsync(ApplicationUser user, IEnumerable&amp;lt;Claim&amp;gt; claims, CancellationToken cancellationToken)
        {
            if (cancellationToken != null)
                cancellationToken.ThrowIfCancellationRequested();

            if (user == null)
                throw new ArgumentNullException(nameof(user));

            if (claims == null)
                throw new ArgumentNullException(nameof(claims));

            var claimEntities = claims.Select(x =&amp;gt; getUserClaimEntity(x, user.Id));
            if(claimEntities.Count() &amp;gt; 0)
            {
                claimEntities.ToList().ForEach(claimEntity =&amp;gt;
                {
                    _unitOfWork.UserClaimRepository.Add(claimEntity);
                });

                _unitOfWork.Commit();
            }

            return Task.CompletedTask;
        }

        public Task ReplaceClaimAsync(ApplicationUser user, Claim claim, Claim newClaim, CancellationToken cancellationToken)
        {
            if (cancellationToken != null)
                cancellationToken.ThrowIfCancellationRequested();

            if (user == null)
                throw new ArgumentNullException(nameof(user));

            if (claim == null)
                throw new ArgumentNullException(nameof(claim));

            if (newClaim == null)
                throw new ArgumentNullException(nameof(newClaim));

            var claimEntity = _unitOfWork.UserClaimRepository.GetByUserId(user.Id)
                .SingleOrDefault(x =&amp;gt; x.ClaimType == claim.Type &amp;amp;&amp;amp; x.ClaimValue == claim.Value);

            if(claimEntity != null)
            {
                claimEntity.ClaimType = newClaim.Type;
                claimEntity.ClaimValue = newClaim.Value;

                _unitOfWork.UserClaimRepository.Update(claimEntity);
                _unitOfWork.Commit();
            }

            return Task.CompletedTask;
        }

        public Task RemoveClaimsAsync(ApplicationUser user, IEnumerable&amp;lt;Claim&amp;gt; claims, CancellationToken cancellationToken)
        {
            if (cancellationToken != null)
                cancellationToken.ThrowIfCancellationRequested();

            if (user == null)
                throw new ArgumentNullException(nameof(user));

            if (claims == null)
                throw new ArgumentNullException(nameof(claims));

            var userClaimEntities = _unitOfWork.UserClaimRepository.GetByUserId(user.Id);
            if (claims.Count() &amp;gt; 0)
            {
                claims.ToList().ForEach(claim =&amp;gt;
                {
                    var userClaimEntity = userClaimEntities.SingleOrDefault(x =&amp;gt; x.ClaimType == claim.Type &amp;amp;&amp;amp; x.ClaimValue == claim.Value);
                    _unitOfWork.UserClaimRepository.Remove(userClaimEntity.Id);
                });

                _unitOfWork.Commit();
            }

            return Task.CompletedTask;
        }

        public Task&amp;lt;IList&amp;lt;ApplicationUser&amp;gt;&amp;gt; GetUsersForClaimAsync(Claim claim, CancellationToken cancellationToken)
        {
            if (cancellationToken != null)
                cancellationToken.ThrowIfCancellationRequested();

            if (claim == null)
                throw new ArgumentNullException(nameof(claim));

            IList&amp;lt;ApplicationUser&amp;gt; result = _unitOfWork.UserClaimRepository.GetUsersForClaim(claim.Type, claim.Value).Select(x =&amp;gt; getApplicationUser(x)).ToList();

            return Task.FromResult(result);
        }
        #endregion

        #region IUserAuthenticationTokenStore&amp;lt;ApplicationUser&amp;gt; Members
        public Task SetTokenAsync(ApplicationUser user, string loginProvider, string name, string value, CancellationToken cancellationToken)
        {
            if (cancellationToken != null)
                cancellationToken.ThrowIfCancellationRequested();

            if (user == null)
                throw new ArgumentNullException(nameof(user));

            if (string.IsNullOrWhiteSpace(loginProvider))
                throw new ArgumentNullException(nameof(loginProvider));

            if(string.IsNullOrWhiteSpace(name))
                throw new ArgumentNullException(nameof(name));

            var userTokenEntity = new UserToken
            {
                LoginProvider = loginProvider,
                Name = name,
                Value = value,
                UserId = user.Id
            };

            _unitOfWork.UserTokenRepository.Add(userTokenEntity);
            _unitOfWork.Commit();

            return Task.CompletedTask;
        }

        public Task RemoveTokenAsync(ApplicationUser user, string loginProvider, string name, CancellationToken cancellationToken)
        {
            if (cancellationToken != null)
                cancellationToken.ThrowIfCancellationRequested();

            if (user == null)
                throw new ArgumentNullException(nameof(user));

            if (string.IsNullOrWhiteSpace(loginProvider))
                throw new ArgumentNullException(nameof(loginProvider));

            if (string.IsNullOrWhiteSpace(name))
                throw new ArgumentNullException(nameof(name));

            var userTokenEntity = _unitOfWork.UserTokenRepository.Find(new UserTokenKey { UserId = user.Id, LoginProvider = loginProvider, Name = name });
            if(userTokenEntity != null)
            {
                _unitOfWork.UserTokenRepository.Remove(userTokenEntity);
                _unitOfWork.Commit();
            }

            return Task.CompletedTask;
        }

        public Task&amp;lt;string&amp;gt; GetTokenAsync(ApplicationUser user, string loginProvider, string name, CancellationToken cancellationToken)
        {
            if (cancellationToken != null)
                cancellationToken.ThrowIfCancellationRequested();

            if (user == null)
                throw new ArgumentNullException(nameof(user));

            if (string.IsNullOrWhiteSpace(loginProvider))
                throw new ArgumentNullException(nameof(loginProvider));

            if (string.IsNullOrWhiteSpace(name))
                throw new ArgumentNullException(nameof(name));

            var userTokenEntity = _unitOfWork.UserTokenRepository.Find(new UserTokenKey { UserId = user.Id, LoginProvider = loginProvider, Name = name });

            return Task.FromResult(userTokenEntity?.Name);
        }
        #endregion

        #region IUserTwoFactorStore&amp;lt;ApplicationUser&amp;gt; Members
        public Task SetTwoFactorEnabledAsync(ApplicationUser user, bool enabled, CancellationToken cancellationToken)
        {
            if (cancellationToken != null)
                cancellationToken.ThrowIfCancellationRequested();

            if (user == null)
                throw new ArgumentNullException(nameof(user));

            user.TwoFactorEnabled = enabled;

            return Task.CompletedTask;
        }

        public Task&amp;lt;bool&amp;gt; GetTwoFactorEnabledAsync(ApplicationUser user, CancellationToken cancellationToken)
        {
            if (cancellationToken != null)
                cancellationToken.ThrowIfCancellationRequested();

            if (user == null)
                throw new ArgumentNullException(nameof(user));

            return Task.FromResult(user.TwoFactorEnabled);
        }
        #endregion

        #region IUserPhoneNumberStore&amp;lt;ApplicationUser&amp;gt; Members
        public Task SetPhoneNumberAsync(ApplicationUser user, string phoneNumber, CancellationToken cancellationToken)
        {
            if (cancellationToken != null)
                cancellationToken.ThrowIfCancellationRequested();

            if (user == null)
                throw new ArgumentNullException(nameof(user));

            user.PhoneNumber = phoneNumber;

            return Task.CompletedTask;
        }

        public Task&amp;lt;string&amp;gt; GetPhoneNumberAsync(ApplicationUser user, CancellationToken cancellationToken)
        {
            if (cancellationToken != null)
                cancellationToken.ThrowIfCancellationRequested();

            if (user == null)
                throw new ArgumentNullException(nameof(user));

            return Task.FromResult(user.PhoneNumber);
        }

        public Task&amp;lt;bool&amp;gt; GetPhoneNumberConfirmedAsync(ApplicationUser user, CancellationToken cancellationToken)
        {
            if (cancellationToken != null)
                cancellationToken.ThrowIfCancellationRequested();

            if (user == null)
                throw new ArgumentNullException(nameof(user));

            return Task.FromResult(user.PhoneNumberConfirmed);
        }

        public Task SetPhoneNumberConfirmedAsync(ApplicationUser user, bool confirmed, CancellationToken cancellationToken)
        {
            if (cancellationToken != null)
                cancellationToken.ThrowIfCancellationRequested();

            if (user == null)
                throw new ArgumentNullException(nameof(user));

            user.PhoneNumberConfirmed = confirmed;

            return Task.CompletedTask;
        }
        #endregion

        #region IUserLockoutStore&amp;lt;ApplicationUser&amp;gt; Members
        public Task&amp;lt;DateTimeOffset?&amp;gt; GetLockoutEndDateAsync(ApplicationUser user, CancellationToken cancellationToken)
        {
            if (cancellationToken != null)
                cancellationToken.ThrowIfCancellationRequested();

            if (user == null)
                throw new ArgumentNullException(nameof(user));

            return Task.FromResult(user.LockoutEnd);
        }

        public Task SetLockoutEndDateAsync(ApplicationUser user, DateTimeOffset? lockoutEnd, CancellationToken cancellationToken)
        {
            if (cancellationToken != null)
                cancellationToken.ThrowIfCancellationRequested();

            if (user == null)
                throw new ArgumentNullException(nameof(user));

            user.LockoutEnd = lockoutEnd;

            return Task.CompletedTask;
        }

        public Task&amp;lt;int&amp;gt; IncrementAccessFailedCountAsync(ApplicationUser user, CancellationToken cancellationToken)
        {
            if (cancellationToken != null)
                cancellationToken.ThrowIfCancellationRequested();

            if (user == null)
                throw new ArgumentNullException(nameof(user));

            return Task.FromResult(++user.AccessFailedCount);
        }

        public Task ResetAccessFailedCountAsync(ApplicationUser user, CancellationToken cancellationToken)
        {
            if (cancellationToken != null)
                cancellationToken.ThrowIfCancellationRequested();

            if (user == null)
                throw new ArgumentNullException(nameof(user));

            user.AccessFailedCount = 0;

            return Task.CompletedTask;
        }

        public Task&amp;lt;int&amp;gt; GetAccessFailedCountAsync(ApplicationUser user, CancellationToken cancellationToken)
        {
            if (cancellationToken != null)
                cancellationToken.ThrowIfCancellationRequested();

            if (user == null)
                throw new ArgumentNullException(nameof(user));

            return Task.FromResult(user.AccessFailedCount);
        }

        public Task&amp;lt;bool&amp;gt; GetLockoutEnabledAsync(ApplicationUser user, CancellationToken cancellationToken)
        {
            if (cancellationToken != null)
                cancellationToken.ThrowIfCancellationRequested();

            if (user == null)
                throw new ArgumentNullException(nameof(user));

            return Task.FromResult(user.LockoutEnabled);
        }

        public Task SetLockoutEnabledAsync(ApplicationUser user, bool enabled, CancellationToken cancellationToken)
        {
            if (cancellationToken != null)
                cancellationToken.ThrowIfCancellationRequested();

            if (user == null)
                throw new ArgumentNullException(nameof(user));

            user.LockoutEnabled = enabled;

            return Task.CompletedTask;
        }
        #endregion

        #region Private Methods
        private User getUserEntity(ApplicationUser ApplicationUser)
        {
            if (ApplicationUser == null)
                return null;

            var result = new User();
            populateUserEntity(result, ApplicationUser);

            return result;
        }

        private void populateUserEntity(User entity, ApplicationUser ApplicationUser)
        {
            entity.AccessFailedCount = ApplicationUser.AccessFailedCount;
            entity.ConcurrencyStamp = ApplicationUser.ConcurrencyStamp;
            entity.Email = ApplicationUser.Email;
            entity.EmailConfirmed = ApplicationUser.EmailConfirmed;
            entity.Id = ApplicationUser.Id;
            entity.LockoutEnabled = ApplicationUser.LockoutEnabled;
            entity.LockoutEnd = ApplicationUser.LockoutEnd;
            entity.NormalizedEmail = ApplicationUser.NormalizedEmail;
            entity.NormalizedUserName = ApplicationUser.NormalizedUserName;
            entity.PasswordHash = ApplicationUser.PasswordHash;
            entity.PhoneNumber = ApplicationUser.PhoneNumber;
            entity.PhoneNumberConfirmed = ApplicationUser.PhoneNumberConfirmed;
            entity.SecurityStamp = ApplicationUser.SecurityStamp;
            entity.TwoFactorEnabled = ApplicationUser.TwoFactorEnabled;
            entity.UserName = ApplicationUser.UserName;
        }

        private ApplicationUser getApplicationUser(User entity)
        {
            if (entity == null)
                return null;

            var result = new ApplicationUser();
            populateApplicationUser(result, entity);

            return result;
        }

        private void populateApplicationUser(ApplicationUser ApplicationUser, User entity)
        {
            ApplicationUser.AccessFailedCount = entity.AccessFailedCount;
            ApplicationUser.ConcurrencyStamp = entity.ConcurrencyStamp;
            ApplicationUser.Email = entity.Email;
            ApplicationUser.EmailConfirmed = entity.EmailConfirmed;
            ApplicationUser.Id = entity.Id;
            ApplicationUser.LockoutEnabled = entity.LockoutEnabled;
            ApplicationUser.LockoutEnd = entity.LockoutEnd;
            ApplicationUser.NormalizedEmail = entity.NormalizedEmail;
            ApplicationUser.NormalizedUserName = entity.NormalizedUserName;
            ApplicationUser.PasswordHash = entity.PasswordHash;
            ApplicationUser.PhoneNumber = entity.PhoneNumber;
            ApplicationUser.PhoneNumberConfirmed = entity.PhoneNumberConfirmed;
            ApplicationUser.SecurityStamp = entity.SecurityStamp;
            ApplicationUser.TwoFactorEnabled = entity.TwoFactorEnabled;
            ApplicationUser.UserName = entity.UserName;
        }

        private UserClaim getUserClaimEntity(Claim value, string userId)
        {
            return value == null
                ? default(UserClaim)
                : new UserClaim
                {
                    ClaimType = value.Type,
                    ClaimValue = value.Value,
                    UserId = userId
                };
        }
        #endregion
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;configuring-the-dependencies&quot;&gt;Configuring the Dependencies&lt;/h3&gt;

&lt;p&gt;Out of the box, ASP.NET Core encourages dependency injection with a simple inversion of control container. So, instead of using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Unity&lt;/code&gt; or some other container like I did in my previous example, we’re going to use what’s built in. Once we get the dependencies configured, we should have a working application!&lt;/p&gt;

&lt;h4 id=&quot;the-identitybuilder&quot;&gt;The IdentityBuilder&lt;/h4&gt;

&lt;p&gt;The out-of-the-box Entity Framework implementation of ASP.NET Core Identity has an extension method on the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IdentityBuilder&lt;/code&gt; class called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AddEntityFrameworkStores&lt;/code&gt; which just added the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UserStore&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RoleStore&lt;/code&gt; implementations to the inversion of control container. So we’re going to create our own extension method called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AddCustomStores&lt;/code&gt; that will — you guessed it — add our custom stores:&lt;/p&gt;

&lt;h5 id=&quot;identitybuilderextensionscs&quot;&gt;IdentityBuilderExtensions.cs&lt;/h5&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;using AspNetCoreIdentityExample.Web.Models;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.DependencyInjection;

namespace AspNetCoreIdentityExample.Web.Identity
{
    public static class IdentityBuilderExtensions
    {
        public static IdentityBuilder AddCustomStores(this IdentityBuilder builder)
        {
            builder.Services.AddTransient&amp;lt;IUserStore&amp;lt;ApplicationUser&amp;gt;, CustomUserStore&amp;gt;();
            builder.Services.AddTransient&amp;lt;IRoleStore&amp;lt;IdentityRole&amp;gt;, CustomRoleStore&amp;gt;();
            return builder;
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;edit-startupcs&quot;&gt;Edit Startup.cs&lt;/h4&gt;

&lt;p&gt;Now all that’s left to do is make a few changes to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Startup.cs&lt;/code&gt; class, and we’ll be done. So, open it up, and do the following:&lt;/p&gt;

&lt;p&gt;Add the following &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;using&lt;/code&gt; statements:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;using WebApplication1.Identity;
using WebApplication1.Domain;
using WebApplication1.Data;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then, in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ConfigureServices&lt;/code&gt; method, change this line:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;services.AddIdentity&amp;lt;ApplicationUser, IdentityRole&amp;gt;()
    .AddDefaultTokenProviders();
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;to this:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;services.AddIdentity&amp;lt;ApplicationUser, IdentityRole&amp;gt;()
    .AddCustomStores()
    .AddDefaultTokenProviders();
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And add the following line right after &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;// Add application services.&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;services.AddScoped&amp;lt;IUnitOfWork, DapperUnitOfWork&amp;gt;(provider =&amp;gt; new DapperUnitOfWork(Configuration.GetConnectionString(&quot;DefaultConnection&quot;)));
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;em&gt;NOTE: Using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AddScoped&lt;/code&gt; for the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IUnitOfWork&lt;/code&gt; allows a single instance of the underlying &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IDbCconnection&lt;/code&gt; to be used per request, as opposed to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AddTransient&lt;/code&gt; which would create a new instance each time it is resolved. This is what allows the UnitOfWork pattern to work with multiple repositories.&lt;/em&gt;&lt;/p&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;Now, go ahead and run it! You should have a working application that has all of the same functionality as you get out-of-the-box with the default template! If not, then you might need to go back and double-check your work along the way. Or (and I would probably just do this), you could fork my repository for this tutorial: &lt;a href=&quot;https://github.com/timschreiber/ASP.NET-Core-Identity-Example&quot;&gt;https://github.com/timschreiber/ASP.NET-Core-Identity-Example&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It’s been a fun exercise to put this project together. Microsoft fixed a lot of things that were wrong with ASP.NET Identity, and doing a custom implementation is easier than ever. Now that you’ve got your feet wet, you can adapt this project to your specific use case. I have a “day job” project where I’m implementing the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CustomRoleStore&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CustomUserStore&lt;/code&gt; to use a REST service instead of a database connection. So, let me know what you think in the comments, and as always, happy coding!&lt;/p&gt;

</content>
  </entry>
  
  <entry>
    <id>http://timschreiber.com/2018/05/07/aspnet-core-identity-with-patterns-2/</id>
    <link type="text/html" rel="alternate" href="http://timschreiber.com//2018/05/07/aspnet-core-identity-with-patterns-2/"/>
    <title>ASP.NET Core Identity with Patterns (Part 2 of 3)</title>
    <updated>2018-05-07T12:56:00+00:00</updated>
    <author>
      <name>Timothy P. Schreiber</name>
      <uri>http://timschreiber.com/</uri>
    </author>
    <content type="html">&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/2018/05/07/aspnet-core-identity-with-patterns/&quot;&gt;Part 1&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Part 2&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2018/05/08/aspnet-core-identity-with-patterns-3/&quot;&gt;Part 3&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The source code for this series of posts is available at on my GitHub: &lt;a href=&quot;https://github.com/timschreiber/ASP.NET-Core-Identity-Example&quot;&gt;https://github.com/timschreiber/ASP.NET-Core-Identity-Example&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;NOTES:&lt;/em&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;em&gt;This series of posts requires a functional understanding of ASP.NET Core Identity If you haven’t had at least some kind of exposure, this you should probably start &lt;a href=&quot;https://docs.microsoft.com/en-us/aspnet/core/security/authentication/identity?view=aspnetcore-2.1&amp;amp;tabs=visual-studio%2Caspnetcore2x&quot;&gt;here&lt;/a&gt;.&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;If you haven’t read my &lt;a href=&quot;/2015/01/14/persistence-ignorant-asp-net-identity-with-patterns-part-1/&quot;&gt;my previous posts&lt;/a&gt;, I’d suggest you stop here and read at least the first one to understand the problems I had with ASP.NET Identity 2.0 and how I solved them.&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In Part 1, we started building our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AspNetCoreIdentityExample&lt;/code&gt; solution, beginning with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AspNetCoreIdentityExample.Web project&lt;/code&gt;. First we got it running with the out-of-the-box Entity Framework implementation, then we broke it in preparation for our custom, persistence-ignorant version. In this step, we’ll be adding the Domain and Data Layers.&lt;/p&gt;

&lt;h2 id=&quot;the-domain-layer&quot;&gt;The Domain Layer&lt;/h2&gt;

&lt;p&gt;Now, let’s create a class library for our Domain Layer. Our application domain is made up of our entity classes, interfaces for our repositories, and our Unit of Work interface. Our Data Layer (which we’ll code later) will implement these interfaces to allow our entities to be persisted to the database. The Domain Layer forms the core of our entire, well-layered, loosely-coupled application architecture.&lt;/p&gt;

&lt;p&gt;So let’s add a class library project to the solution. I called mine &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AspNetCoreIdentityExample.Domain&lt;/code&gt;. Once the project has been created, let’s add two folders: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Entities&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Repositories&lt;/code&gt;.&lt;/p&gt;

&lt;h3 id=&quot;entities&quot;&gt;Entities&lt;/h3&gt;

&lt;p&gt;In keeping with our persistence ignorant design, our entities are just Plain Old CLR Objects (POCOs), and in order to get the same ASP.NET Identity functionality from our application as we would from the out-of-the-box Entity Framework implementation, we’ll create the following classes in the Entities folder:&lt;/p&gt;

&lt;p&gt;Because &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RoleClaim&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UserClaim&lt;/code&gt; are essentially the same class, differing only in the objects they’re related to, I’ve chosen to define a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ClaimBase&lt;/code&gt; class that the other two classes will inherit from:&lt;/p&gt;

&lt;h4 id=&quot;claimbasecs&quot;&gt;ClaimBase.cs&lt;/h4&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;namespace AspNetCoreIdentityExample.Domain.Entities
{
    public abstract class ClaimBase
    {
        public int Id { get; set; }
        public string ClaimType { get; set; }
        public string ClaimValue { get; set; }
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;roleclaimcs&quot;&gt;RoleClaim.cs&lt;/h4&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;namespace AspNetCoreIdentityExample.Domain.Entities
{
    public class RoleClaim : ClaimBase
    {
        public string RoleId { get; set; }
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;userclaimcs&quot;&gt;UserClaim.cs&lt;/h4&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;namespace AspNetCoreIdentityExample.Domain.Entities
{
    public class UserClaim : ClaimBase
    {
        public string UserId { get; set; }
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UserLogin&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UserToken&lt;/code&gt; entities use composite keys, and it’s difficult to use a generic repsotitory interface without representing those keys as a class that can be used in a generic type parameter. So, another OO design decision I made was to make composite keys their own classes, from which the corresponding entities would inherit. This also simplifies things in the data layer (when we get there).&lt;/p&gt;

&lt;h4 id=&quot;userlogincs&quot;&gt;UserLogin.cs&lt;/h4&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;namespace AspNetCoreIdentityExample.Domain.Entities
{
    public class UserLogin : UserLoginKey
    {
        public string ProviderDisplayName { get; set; }
        public string UserId { get; set; }
    }

    public class UserLoginKey
    {
        public string LoginProvider;
        public string ProviderKey;
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;usertokencs&quot;&gt;UserToken.cs&lt;/h4&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;namespace AspNetCoreIdentityExample.Domain.Entities
{
    public class UserToken : UserTokenKey
    {
        public string Value { get; set; }
    }

    public class UserTokenKey
    {
        public string UserId { get; set; }
        public string LoginProvider { get; set; }
        public string Name { get; set; }
    }
}    
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The remaining &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Role&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;User&lt;/code&gt; entities are just plain old classes:&lt;/p&gt;

&lt;h4 id=&quot;rolecs&quot;&gt;Role.cs&lt;/h4&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;namespace AspNetCoreIdentityExample.Domain.Entities
{
    public class Role
    {
        public string Id { get; set; }
        public string ConcurrencyStamp { get; set; }
        public string Name { get; set; }
        public string NormalizedName { get; set; }
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;usercs&quot;&gt;User.cs&lt;/h4&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;using System;

namespace AspNetCoreIdentityExample.Domain.Entities
{
    public class User
    {
        public string Id { get; set; }
        public int AccessFailedCount { get; set; }
        public string ConcurrencyStamp { get; set; }
        public string Email { get; set; }
        public bool EmailConfirmed { get; set; }
        public bool LockoutEnabled { get; set; }
        public DateTimeOffset? LockoutEnd { get; set; }
        public string NormalizedEmail { get; set; }
        public string NormalizedUserName { get; set; }
        public string PasswordHash { get; set; }
        public string PhoneNumber { get; set; }
        public bool PhoneNumberConfirmed { get; set; }
        public string SecurityStamp { get; set; }
        public bool TwoFactorEnabled { get; set; }
        public string UserName { get; set; }
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;repositories&quot;&gt;Repositories&lt;/h3&gt;

&lt;p&gt;Next up are the repositories. We’re not implementing anything here, just creating the interfaces that our future Data Layer will implement. Each repository can be expected to have basic CRUD methods, so we can use a generic repository interface like this:&lt;/p&gt;

&lt;h4 id=&quot;irepositorycs&quot;&gt;IRepository.cs&lt;/h4&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;

namespace AspNetCoreIdentityExample.Domain.Repositories
{
    public interface IRepository&amp;lt;TEntity, TKey&amp;gt; where TEntity : class
    {
        IEnumerable&amp;lt;TEntity&amp;gt; All();

        TEntity Find(TKey key);

        void Add(TEntity entity);

        void Update(TEntity entity);

        void Remove(TKey key);
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;However, some repositories will need additional methods. So for them, we can create specialized interfaces that inherit from the generic repository:&lt;/p&gt;

&lt;h4 id=&quot;iroleclaimrepositorycs&quot;&gt;IRoleClaimRepository.cs&lt;/h4&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;using AspNetCoreIdentityExample.Domain.Entities;
using System.Collections.Generic;

namespace AspNetCoreIdentityExample.Domain.Repositories
{
    public interface IRoleClaimRepository : IRepository&amp;lt;RoleClaim, int&amp;gt;
    {
        IEnumerable&amp;lt;RoleClaim&amp;gt; FindByRoleId(string roleId);
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;irolerepositorycs&quot;&gt;IRoleRepository.cs&lt;/h4&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;using AspNetCoreIdentityExample.Domain.Entities;
using System.Threading;
using System.Threading.Tasks;

namespace AspNetCoreIdentityExample.Domain.Repositories
{
    public interface IRoleRepository : IRepository&amp;lt;Role, string&amp;gt;
    {
        Role FindByName(string roleName);
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;iuserclaimrepositorycs&quot;&gt;IUserClaimRepository.cs&lt;/h4&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;using AspNetCoreIdentityExample.Domain.Entities;
using System;
using System.Collections.Generic;
using System.Text;

namespace AspNetCoreIdentityExample.Domain.Repositories
{
    public interface IUserClaimRepository : IRepository&amp;lt;UserClaim, int&amp;gt;
    {
        IEnumerable&amp;lt;UserClaim&amp;gt; GetByUserId(string userId);
        IEnumerable&amp;lt;User&amp;gt; GetUsersForClaim(string claimType, string claimValue);
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;iuserloginrepositorycs&quot;&gt;IUserLoginRepository.cs&lt;/h4&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;using AspNetCoreIdentityExample.Domain.Entities;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;

namespace AspNetCoreIdentityExample.Domain.Repositories
{
    public interface IUserLoginRepository : IRepository&amp;lt;UserLogin, UserLoginKey&amp;gt;
    {
        IEnumerable&amp;lt;UserLogin&amp;gt; FindByUserId(string userId);
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;iuserrepositorycs&quot;&gt;IUserRepository.cs&lt;/h4&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;using AspNetCoreIdentityExample.Domain.Entities;
using System.Threading;
using System.Threading.Tasks;

namespace AspNetCoreIdentityExample.Domain.Repositories
{
    public interface IUserRepository : IRepository&amp;lt;User, string&amp;gt;
    {
        User FindByNormalizedUserName(string normalizedUserName);

        User FindByNormalizedEmail(string normalizedEmail);
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;iuserrolerepositorycs&quot;&gt;IUserRoleRepository.cs&lt;/h4&gt;

&lt;p&gt;The one specialized repository interface that won’t inherit from the base repository interface is the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IUserRoleRepository&lt;/code&gt; interface which only deals with the many-to-many relationship between &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;User&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Role&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;using AspNetCoreIdentityExample.Domain.Entities;
using System;
using System.Collections.Generic;
using System.Text;

namespace AspNetCoreIdentityExample.Domain.Repositories
{
    public interface IUserRoleRepository
    {
        void Add(string UserId, string roleName);
        void Remove(string userId, string roleName);
        IEnumerable&amp;lt;string&amp;gt; GetRoleNamesByUserId(string userId);
        IEnumerable&amp;lt;User&amp;gt; GetUsersByRoleName(string roleName);
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You might notice we don’t have an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IUserTokenRepository&lt;/code&gt; interface. That’s simply because we don’t have a need beyond that which the generic repository interface already provides.&lt;/p&gt;

&lt;h3 id=&quot;unit-of-work&quot;&gt;Unit of Work&lt;/h3&gt;

&lt;p&gt;Another important design pattern we’re following is the Unit of Work pattern, which ensures that all changes are sent as a single transaction to the data store.&lt;/p&gt;

&lt;p&gt;Our IUnitOfWork interface defines the methods that we’ll implement later in the data layer. Because we may need to use more than one repository in a single Unit of Work transaction, it’s important that we design our IUnitOfWork interface to ensure all of our repositories are using the same transaction during any given transaction scope. So we’re putting the getters for our repositories in there as well.&lt;/p&gt;

&lt;p&gt;Once again, we’re not including anything that might couple this interface to any specific persistence mechanism. Those are implementation details that we’ll tackle later in the Data Layer.&lt;/p&gt;

&lt;h4 id=&quot;iunitofworkcs&quot;&gt;IUnitOfWork.cs&lt;/h4&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;using AspNetCoreIdentityExample.Domain.Entities;
using AspNetCoreIdentityExample.Domain.Repositories;
using System;

namespace AspNetCoreIdentityExample.Domain
{
    public interface IUnitOfWork : IDisposable
    {
        IRoleRepository RoleRepository { get; }
        IRoleClaimRepository RoleClaimRepository { get; }
        IUserRepository UserRepository { get; }
        IUserClaimRepository UserClaimRepository { get; }
        IUserLoginRepository UserLoginRepository { get; }
        IRepository&amp;lt;UserToken, UserTokenKey&amp;gt; UserTokenRepository { get; }
        IUserRoleRepository UserRoleRepository { get; }

        void Commit();
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;the-data-layer&quot;&gt;The Data Layer&lt;/h2&gt;

&lt;p&gt;Whereas ASP.NET Core Identity uses Entity Framework out-of-the-box, for this tutorial, I’ve chosen to use Dapper, which is a fast, lightweight object mapper for SQL queries. Since we’re using a persistence-ignorant approach, we could just as easily use another ORM or even plain SQL with little or no modification to our Domain layer.&lt;/p&gt;

&lt;p&gt;The first thing we need to do is to add another class library to the solution. I called mine &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AspNetCoreIdentityExample.Data&lt;/code&gt;. Once, the project has been created, we’ll need to add a folder called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Repositories&lt;/code&gt; and add a project reference to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AspNetCoreIdentityExample.Domain&lt;/code&gt; project.&lt;/p&gt;

&lt;p&gt;Next, we’ll need to add a reference to Dapper. To do this, launch the Package Manager Console and run the following command:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;PM&amp;gt; Install-Package Dapper
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;repositories-1&quot;&gt;Repositories&lt;/h3&gt;

&lt;p&gt;With Dapper installed, now we can move on to implementing our repositories. As we dive into the code, I want you to notice a couple of things about the classes:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;There is no default constructor. That’s because we’re following the Dependency Injection pattern by providing the repository with the IDbTransaction it needs in the constructor. This ensures that all of our repositories will use the same transaction, which is a main objective of the Unit of Work pattern.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;The repository implementations are marked with the internal access modifier. That’s because these repository classes are implementation details of the Data Layer that don’t need to be exposed beyond the Unit of Work. Making them public could allow you to couple them to other layers, which is exactly what we’re trying to avoid.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;All of our repositories have some common boilerplate code that we can abstract into a base class. So let’s start by creating the following class in the Repositories folder:&lt;/p&gt;

&lt;h4 id=&quot;repositorybasecs&quot;&gt;RepositoryBase.cs&lt;/h4&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;using System.Collections.Generic;
using System.Data;
using Dapper;

namespace AspNetCoreIdentityExample.Data.Repositories
{
    internal abstract class RepositoryBase
    {
        private IDbTransaction _transaction;
        private IDbConnection Connection { get { return _transaction.Connection; } }

        public RepositoryBase(IDbTransaction transaction)
        {
            _transaction = transaction;
        }

        protected T ExecuteScalar&amp;lt;T&amp;gt;(string sql, object param)
        {
            return Connection.ExecuteScalar&amp;lt;T&amp;gt;(sql, param, _transaction);
        }

        protected T QuerySingleOrDefault&amp;lt;T&amp;gt;(string sql, object param)
        {
            return Connection.QuerySingleOrDefault&amp;lt;T&amp;gt;(sql, param, _transaction);
        }

        protected IEnumerable&amp;lt;T&amp;gt; Query&amp;lt;T&amp;gt;(string sql, object param = null)
        {
            return Connection.Query&amp;lt;T&amp;gt;(sql, param, _transaction);
        }

        protected void Execute(string sql, object param)
        {
            Connection.Execute(sql, param, _transaction);
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The entity-specific repository classes extend the base repository class and implement the applicable interfaces from the Domain Layer. Let’s add the following classes to the Repositories folder:&lt;/p&gt;

&lt;h4 id=&quot;roleclaimrepositorycs&quot;&gt;RoleClaimRepository.cs&lt;/h4&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;using AspNetCoreIdentityExample.Domain.Entities;
using AspNetCoreIdentityExample.Domain.Repositories;
using System.Collections.Generic;
using System.Data;

namespace AspNetCoreIdentityExample.Data.Repositories
{
    internal class RoleClaimRepository : RepositoryBase, IRoleClaimRepository
    {
        public RoleClaimRepository(IDbTransaction transaction)
            : base(transaction)
        { }

        public void Add(RoleClaim entity)
        {
            entity.Id = ExecuteScalar&amp;lt;int&amp;gt;(
                sql: @&quot;
                    INSERT INTO AspNetRoleClaims(ClaimType, ClaimValue, RoldId)
                    VALUES(@ClaimType, @ClaimValue, @RoldId);
                    SELECT SCOPE_IDENTITY()&quot;,
                param: entity
            );
        }

        public RoleClaim Find(int key)
        {
            return QuerySingleOrDefault&amp;lt;RoleClaim&amp;gt;(
                sql: &quot;SELECT * FROM AspNetRoleClaims WHERE Id = @key&quot;,
                param: new { key }
            );
        }

        public IEnumerable&amp;lt;RoleClaim&amp;gt; FindByRoleId(string roleId)
        {
            return Query&amp;lt;RoleClaim&amp;gt;(
                sql: &quot;SELECT * FROM AspNetRoleClaims WHERE RoleId = @roleId&quot;,
                param: new { roleId }
            );
        }

        public IEnumerable&amp;lt;RoleClaim&amp;gt; All()
        {
            return Query&amp;lt;RoleClaim&amp;gt;(
                sql: &quot;SELECT * FROM AspNetRoleClaims&quot;
            );
        }

        public void Remove(int key)
        {
            Execute(
                sql: &quot;DELETE FROM AspNetRoleClaims WHERE Id = @key&quot;,
                param: new { key } 
            );
        }

        public void Update(RoleClaim entity)
        {
            Execute(
                sql: @&quot;
                    UPDATE AspNetRoleClaims SET ClaimType = @ClaimType,
                        ClaimValue = @ClaimValue, RoleId = @RoleId
                    WHERE Id = @Id&quot;,
                param: entity
            );
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;rolerepositorycs&quot;&gt;RoleRepository.cs&lt;/h4&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;using AspNetCoreIdentityExample.Domain.Entities;
using AspNetCoreIdentityExample.Domain.Repositories;
using System;
using System.Collections.Generic;
using System.Data;

namespace AspNetCoreIdentityExample.Data.Repositories
{
    internal class RoleRepository : RepositoryBase, IRoleRepository
    {
        public RoleRepository(IDbTransaction transaction)
            : base(transaction)
        { }

        public void Add(Role entity)
        {
            Execute(
                sql: @&quot;
                    INSERT INTO AspNetRoles(Id, ConcurrencyStamp, [Name], NormalizedName)
                    VALUES(@Id, @ConcurrencyStamp, @Name, @NormalizedName)&quot;,
                param: entity
            );
        }

        public IEnumerable&amp;lt;Role&amp;gt; All()
        {
            return Query&amp;lt;Role&amp;gt;(
                sql: &quot;SELECT * FROM AspNetRoles&quot;
            );
        }

        public Role Find(string key)
        {
            return QuerySingleOrDefault&amp;lt;Role&amp;gt;(
                sql: &quot;SELECT * FROM AspNetRoles WHERE Id = @key&quot;,
                param: new { key }
            );
        }

        public Role FindByName(string roleName)
        {
            return QuerySingleOrDefault&amp;lt;Role&amp;gt;(
                sql: &quot;SELECT * FROM AspNetRoles WHERE [Name] = @roleName&quot;,
                param: new { roleName }
            );
        }


        public void Remove(string key)
        {
            Execute(
                sql: &quot;DELETE FROM AspNetRoles WHERE Id = @key&quot;,
                param: new { key }
            );

            throw new NotImplementedException();
        }

        public void Update(Role entity)
        {
            Execute(
                sql: @&quot;
                    UPDATE AspNetRoles SET ConcurrencyStamp = @ConcurrencyStamp,
                        [Name] = @Name, NormalizedName = @NormalizedName
                    WHERE Id = @Id&quot;,
                param: entity
            );
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;userclaimrepositorycs&quot;&gt;UserClaimRepository.cs&lt;/h4&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;using AspNetCoreIdentityExample.Domain.Entities;
using AspNetCoreIdentityExample.Domain.Repositories;
using System;
using System.Collections.Generic;
using System.Data;
using System.Text;

namespace AspNetCoreIdentityExample.Data.Repositories
{
    internal class UserClaimRepository : RepositoryBase, IUserClaimRepository
    {
        public UserClaimRepository(IDbTransaction transaction)
            : base(transaction)
        {
        }

        public void Add(UserClaim entity)
        {
            entity.Id = ExecuteScalar&amp;lt;int&amp;gt;(
                sql: @&quot;
                    INSERT INTO AspNetUserClaims(ClaimType, ClaimValue, UserId)
                    VALUES(@ClaimType, @ClaimValue, @UserId);
                    SELECT SCOPE_IDENTITY()&quot;,
                param: entity
            );
        }

        public IEnumerable&amp;lt;UserClaim&amp;gt; All()
        {
            return Query&amp;lt;UserClaim&amp;gt;(
                sql: @&quot;
                    SELECT Id, ClaimType, ClaimValue, UserId
                    FROM AspNetUserClaims&quot;
            );
        }

        public UserClaim Find(int key)
        {
            return QuerySingleOrDefault&amp;lt;UserClaim&amp;gt;(
                sql: @&quot;
                    SELECT Id, ClaimType, ClaimValue, UserId
                    FROM AspNetUserClaims WHERE Id = @key&quot;,
                param: new { key }
            );
        }

        public IEnumerable&amp;lt;UserClaim&amp;gt; GetByUserId(string userId)
        {
            return Query&amp;lt;UserClaim&amp;gt;(
                sql: @&quot;
                    SELECT Id, ClaimType, ClaimValue, UserId
                    FROM AspNetUserClaims
                    WHERE UserId = @userId&quot;,
                param: new { userId }
            );
        }

        public IEnumerable&amp;lt;User&amp;gt; GetUsersForClaim(string claimType, string claimValue)
        {
            return Query&amp;lt;User&amp;gt;(
                sql: @&quot;
                    SELECT
                        u.Id, u.AccessFailedCount, u.ConcurrencyStamp, u.Email,
                        u.EmailConfirmed, u.LockoutEnabled, u.LockoutEnd,
                        u.NormalizedEmail, u.NormalizedUserName, u.PasswordHash,
                        u.PhoneNumber, u.PhoneNumberConfirmed, u.SecurityStamp,
                        u.TwoFactorEnabled, u.UserName
                    FROM
                        AspNetUserClaims c INNER JOIN
                        AspNetUsers u ON c.UserId = u.Id
                    WHERE
                        c.ClaimType = @claimType AND
                        c.ClaimValue = @claimValue
                &quot;,
                param: new { claimType, claimValue }
            );
        }

        public void Remove(int key)
        {
            Execute(
                sql: @&quot;
                    DELETE FROM AspNetUserClaims
                    WHERE Id = @key&quot;,
                param: new { key }
            );
        }

        public void Update(UserClaim entity)
        {
            Execute(
                sql: @&quot;
                    UPDATE AspNetUserClaims SET ClaimType = @ClaimType,
                        ClaimValue = @ClaimValue, UserId = @UserId
                    WHERE Id = @Id&quot;,
                param: entity
            );
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;userloginrepositorycs&quot;&gt;UserLoginRepository.cs&lt;/h4&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;using AspNetCoreIdentityExample.Domain.Entities;
using AspNetCoreIdentityExample.Domain.Repositories;
using System;
using System.Collections.Generic;
using System.Data;
using System.Text;

namespace AspNetCoreIdentityExample.Data.Repositories
{
    internal class UserLoginRepository : RepositoryBase, IUserLoginRepository
    {
        public UserLoginRepository(IDbTransaction transaction)
            : base(transaction)
        { }

        public void Add(UserLogin entity)
        {
            Execute(
                sql: @&quot;
                    INSERT INTO AspNetUserLogins(LoginProvider, ProviderKey, ProviderDisplayName, UserId)
                    VALUES(@LoginProvider, @ProviderKey, @ProviderDisplayName, @UserId)&quot;,
                param: entity
            );
        }

        public IEnumerable&amp;lt;UserLogin&amp;gt; All()
        {
            return Query&amp;lt;UserLogin&amp;gt;(
                sql: &quot;SELECT * FROM AspNetUserLogins&quot;
            );
        }

        public UserLogin Find(UserLoginKey id)
        {
            return QuerySingleOrDefault&amp;lt;UserLogin&amp;gt;(
                sql: @&quot;
                    SELECT * FROM AspNetUserLogins
                    WHERE LoginProvider = @LoginProvider AND ProviderKey = @ProviderKey&quot;,
                param: id
            );
        }

        public IEnumerable&amp;lt;UserLogin&amp;gt; FindByUserId(string userId)
        {
            return Query&amp;lt;UserLogin&amp;gt;(
                sql: &quot;SELECT * FROM AspNetUserLogins WHERE UserId = @userId&quot;,
                param: new { userId }
            );
        }

        public void Remove(UserLoginKey key)
        {
            Execute(
                sql: @&quot;
                    DELETE FROM AspNetUserLogins
                    WHERE LoginProvider = @LoginProvider AND ProviderKey = @ProviderKey&quot;,
                param: key
            );
        }

        public void Update(UserLogin entity)
        {
            Execute(
                sql: @&quot;
                    UPDATE AspNetUserLogins SET ProviderDisplayName = @ProviderDisplayName,
                        UserId = @UserId
                    WHERE LoginProvider = @LoginProvider AND ProviderKey = @ProviderKey&quot;,
                param: entity
            );
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;userrepositorycs&quot;&gt;UserRepository.cs&lt;/h4&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;using AspNetCoreIdentityExample.Domain.Entities;
using AspNetCoreIdentityExample.Domain.Repositories;
using System;
using System.Collections.Generic;
using System.Data;
using System.Text;

namespace AspNetCoreIdentityExample.Data.Repositories
{
    internal class UserRepository : RepositoryBase, IUserRepository
    {
        public UserRepository(IDbTransaction transaction)
            : base(transaction)
        { }

        public void Add(User entity)
        {
            Execute(
                sql: @&quot;
                    INSERT INTO AspNetUsers(Id, AccessFailedCount, ConcurrencyStamp, Email,
                        EmailConfirmed, LockoutEnabled, LockoutEnd, NormalizedEmail,
                        NormalizedUserName, PasswordHash, PhoneNumber, PhoneNumberConfirmed,
                        SecurityStamp, TwoFactorEnabled, UserName)
                    VALUES(@Id, @AccessFailedCount, @ConcurrencyStamp, @Email, @EmailConfirmed,
                        @LockoutEnabled, @LockoutEnd, @NormalizedEmail, @NormalizedUserName,
                        @PasswordHash, @PhoneNumber, @PhoneNumberConfirmed, @SecurityStamp,
                        @TwoFactorEnabled, @UserName)&quot;,
                param: entity
            );
        }

        public IEnumerable&amp;lt;User&amp;gt; All()
        {
            return Query&amp;lt;User&amp;gt;(
                sql: &quot;SELECT * FROM AspNetUsers&quot;
            );
        }

        public User Find(string key)
        {
            return QuerySingleOrDefault&amp;lt;User&amp;gt;(
                sql: &quot;SELECT * FROM AspNetUsers WHERE Id = @key&quot;,
                param: new { key }
            );
        }

        public User FindByNormalizedEmail(string normalizedEmail)
        {
            return QuerySingleOrDefault&amp;lt;User&amp;gt;(
                sql: &quot;SELECT * FROM AspNetUsers WHERE NormalizedEmail = @normalizedEmail&quot;,
                param: new { normalizedEmail }
            );
        }

        public User FindByNormalizedUserName(string normalizedUserName)
        {
            return QuerySingleOrDefault&amp;lt;User&amp;gt;(
                sql: &quot;SELECT * FROM AspNetUsers WHERE NormalizedUserName = @normalizedUserName&quot;,
                param: new { normalizedUserName }
            );
        }

        public void Remove(string key)
        {
            Execute(
                sql: &quot;DELETE FROM AspNetUsers WHERE Id = @key&quot;,
                param: new { key }
            );
        }

        public void Update(User entity)
        {
            Execute(
                sql: @&quot;
                    UPDATE AspNetUsers SET AccessFailedCount = @AccessFailedCount,
                        ConcurrencyStamp = @ConcurrencyStamp, Email = @Email,
                        EmailConfirmed = @EmailConfirmed, LockoutEnabled = @LockoutEnabled,
                        LockoutEnd = @LockoutEnd, NormalizedEmail = @NormalizedEmail,
                        NormalizedUserName = @NormalizedUserName, PasswordHash = @PasswordHash,
                        PhoneNumber = @PhoneNumber, PhoneNumberConfirmed = @PhoneNumberConfirmed,
                        SecurityStamp = @SecurityStamp, TwoFactorEnabled = @TwoFactorEnabled,
                        UserName = @UserName
                    WHERE Id = @Id&quot;,
                param: entity);
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;userrolerepositorycs&quot;&gt;UserRoleRepository.cs&lt;/h4&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;using AspNetCoreIdentityExample.Domain.Entities;
using AspNetCoreIdentityExample.Domain.Repositories;
using System;
using System.Collections.Generic;
using System.Data;
using System.Text;

namespace AspNetCoreIdentityExample.Data.Repositories
{
    internal class UserRoleRepository : RepositoryBase, IUserRoleRepository
    {
        public UserRoleRepository(IDbTransaction transaction) : base(transaction)
        {
        }

        public void Add(string userId, string roleName)
        {
            Execute(
                sql: @&quot;
                    INSERT INTO AspNetUserRoles(UserId, RoleId)
                    SELECT TOP 1 @userId, Id FROM AspNetRoles
                    WHERE NormalizedName = @roleName&quot;,
                param: new { userId, roleName }
            );
        }

        public IEnumerable&amp;lt;string&amp;gt; GetRoleNamesByUserId(string userId)
        {
            return Query&amp;lt;string&amp;gt;(
                sql: @&quot;
                    SELECT r.[Name]
                    FROM AspNetUserRoles ur INNER JOIN
                        AspNetRoles r ON ur.RoleId = r.Id
                    WHERE ur.UserId = @userId
                &quot;,
                param: new { userId }
            );
        }

        public IEnumerable&amp;lt;User&amp;gt; GetUsersByRoleName(string roleName)
        {
            return Query&amp;lt;User&amp;gt;(
                sql: @&quot;
                    SELECT u.*
                    FROM AspNetUserRoles ur INNER JOIN
                        AspNetRoles r ON ur.RoleId = r.Id INNER JOIN
                        AspNetUsers u ON ur.UserId = u.Id
                    WHERE r.NormalizedName = @roleName
                &quot;,
                param: new { roleName });
        }

        public void Remove(string userId, string roleName)
        {
            Execute(
                sql: @&quot;
                    DELETE ur
                    FROM AspNetUserRoles ur INNER JOIN
                        AspNetRoles r ON ur.RoleId = r.Id
                    WHERE r.NormalizedName = @roleName
                &quot;,
                param: new { userId, roleName }
            );
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;usertokenrepositorycs&quot;&gt;UserTokenRepository.cs&lt;/h4&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;using AspNetCoreIdentityExample.Domain.Entities;
using AspNetCoreIdentityExample.Domain.Repositories;
using System;
using System.Collections.Generic;
using System.Data;
using System.Text;

namespace AspNetCoreIdentityExample.Data.Repositories
{
    internal class UserTokenRepository : RepositoryBase, IRepository&amp;lt;UserToken, UserTokenKey&amp;gt;
    {
        public UserTokenRepository(IDbTransaction transaction)
            : base(transaction)
        { }

        public void Add(UserToken entity)
        {
            Execute(
                sql: @&quot;
                    INSERT INTO AspNetUserTokens(UserId, LoginProvider, [Name], Value)
                    VALUES(@UserId, @LoginProvider, @Name, @Value)&quot;,
                param: entity
            );
        }

        public IEnumerable&amp;lt;UserToken&amp;gt; All()
        {
            return Query&amp;lt;UserToken&amp;gt;(
                sql: &quot;SELECT * FROM AspNetUserTokens&quot;
            );
        }

        public UserToken Find(UserTokenKey key)
        {
            return QuerySingleOrDefault&amp;lt;UserToken&amp;gt;(
                sql: @&quot;
                    SELECT * FROM AspNetUserTokens
                    WHERE UserId = @UserId AND LoginProvider = @LoginProvider
                        AND [Name] = @Name&quot;,
                param: key
            );
        }

        public void Remove(UserTokenKey key)
        {
            Execute(
                sql: @&quot;
                    DELETE FROM AspNetUserTokens
                    WHERE UserId = @UserId AND LoginProvider = @LoginProvider
                        AND [Name] = @Name&quot;,
                param: key
            );
        }

        public void Update(UserToken entity)
        {
            Execute(
                sql: @&quot;
                    UPDATE AspNetUserTokens SET Value = @Value
                    WHERE UserId = @UserId
                        AND LoginProvider = @LoginProvider
                        AND [Name] = @Name&quot;,
                param: entity
            );
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;unit-of-work-1&quot;&gt;Unit of Work&lt;/h3&gt;

&lt;p&gt;The last piece of the Data Layer is the Unit of Work implementation. As I pointed out before, the Unit of Work pattern ensures that all changes are sent as a single transaction to the data store where they will either all succeed or all fail and get rolled back.&lt;/p&gt;

&lt;p&gt;The Unit of Work implementation I’ve chosen to use for this tutorial comes from my popular &lt;a href=&quot;https://github.com/timschreiber/DapperUnitOfWork&quot;&gt;Dapper Unit of Work&lt;/a&gt; repository on GitHub.&lt;/p&gt;

&lt;h4 id=&quot;dapperunitofworkcs&quot;&gt;DapperUnitOfWork.cs&lt;/h4&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;using AspNetCoreIdentityExample.Data.Repositories;
using AspNetCoreIdentityExample.Domain;
using AspNetCoreIdentityExample.Domain.Entities;
using AspNetCoreIdentityExample.Domain.Repositories;
using System;
using System.Data;
using System.Data.SqlClient;

namespace AspNetCoreIdentityExample.Data
{
    public class DapperUnitOfWork : IUnitOfWork
    {
        #region Fields
        private IDbConnection _connection;
        private IDbTransaction _transaction;
        private IRoleRepository _roleRepository;
        private IRoleClaimRepository _roleClaimRepository;
        private IUserRepository _userRepository;
        private IUserClaimRepository _userClaimRepository;
        private IUserLoginRepository _userLoginRepository;
        private IRepository&amp;lt;UserToken, UserTokenKey&amp;gt; _userTokenRepository;
        private IUserRoleRepository _userRoleRepository;
        private bool _disposed;
        #endregion

        public DapperUnitOfWork(string connectionString)
        {
            _connection = new SqlConnection(connectionString);
            _connection.Open();
            _transaction = _connection.BeginTransaction();
        }

        #region IUnitOfWork Members
        public IRoleRepository RoleRepository
        {
            get
            {
                return _roleRepository
                    ?? (_roleRepository = new RoleRepository(_transaction));
            }
        }

        public IRoleClaimRepository RoleClaimRepository
        {
            get
            {
                return _roleClaimRepository
                    ?? (_roleClaimRepository = new RoleClaimRepository(_transaction));
            }
        }

        public IUserRepository UserRepository
        {
            get
            {
                return _userRepository
                    ?? (_userRepository = new UserRepository(_transaction));
            }
        }

        public IUserClaimRepository UserClaimRepository
        {
            get
            {
                return _userClaimRepository
                    ?? (_userClaimRepository = new UserClaimRepository(_transaction));
            }
        }
    
        public IUserLoginRepository UserLoginRepository
        {
            get
            {
                return _userLoginRepository
                    ?? (_userLoginRepository = new UserLoginRepository(_transaction));
            }
        }

        public IRepository&amp;lt;UserToken, UserTokenKey&amp;gt; UserTokenRepository
        {
            get
            {
                return _userTokenRepository
                    ?? (_userTokenRepository = new UserTokenRepository(_transaction));
            }
        }

        public IUserRoleRepository UserRoleRepository
        {
            get
            {
                return _userRoleRepository
                    ?? (_userRoleRepository = new UserRoleRepository(_transaction));
            }
        }

        public void Commit()
        {
            try
            {
                _transaction.Commit();
            }
            catch
            {
                _transaction.Rollback();
            }
            finally
            {
                _transaction.Dispose();
                resetRepositories();
                _transaction = _connection.BeginTransaction();
            }
        }

        public void Dispose()
        {
            dispose(true);
            GC.SuppressFinalize(this);
        }
        #endregion

        #region Private Methods
        private void resetRepositories()
        {
            _roleRepository = null;
            _roleClaimRepository = null;
            _userRepository = null;
            _userClaimRepository = null;
            _userLoginRepository = null;
            _userTokenRepository = null;
            _userRoleRepository = null;
        }

        private void dispose(bool disposing)
        {
            if(!_disposed)
            {
                if(disposing)
                {
                    if(_transaction != null)
                    {
                        _transaction.Dispose();
                        _transaction = null;
                    }
                    if(_connection != null)
                    {
                        _connection.Dispose();
                        _connection = null;
                    }
                }
                _disposed = true;
            }
        }

        ~DapperUnitOfWork()
        {
            dispose(false);
        }
        #endregion
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;next-steps&quot;&gt;Next Steps&lt;/h2&gt;

&lt;p&gt;And that’s the Data Layer. We are &lt;em&gt;SO&lt;/em&gt; close to having a working application again! All we have left to do in Part 3 is to revisit the Web Layer so we can finish coding the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CustomUserStore&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CustomRoleStore&lt;/code&gt; classes and do the last couple of steps to wire everything together.&lt;/p&gt;

&lt;p&gt;Until next time, happy coding!&lt;/p&gt;

</content>
  </entry>
  
  <entry>
    <id>http://timschreiber.com/2018/05/07/aspnet-core-identity-with-patterns/</id>
    <link type="text/html" rel="alternate" href="http://timschreiber.com//2018/05/07/aspnet-core-identity-with-patterns/"/>
    <title>ASP.NET Core Identity with Patterns (Part 1 of 3)</title>
    <updated>2018-05-07T12:01:00+00:00</updated>
    <author>
      <name>Timothy P. Schreiber</name>
      <uri>http://timschreiber.com/</uri>
    </author>
    <content type="html">&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Part 1&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2018/05/07/aspnet-core-identity-with-patterns-2/&quot;&gt;Part 2&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2018/05/08/aspnet-core-identity-with-patterns-3/&quot;&gt;Part 3&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The source code for this series of posts is available at on my GitHub: &lt;a href=&quot;https://github.com/timschreiber/ASP.NET-Core-Identity-Example&quot;&gt;https://github.com/timschreiber/ASP.NET-Core-Identity-Example&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;NOTES:&lt;/em&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;em&gt;This series of posts requires a functional understanding of ASP.NET Core Identity If you haven’t had at least some kind of exposure, this you should probably start &lt;a href=&quot;https://docs.microsoft.com/en-us/aspnet/core/security/authentication/identity-custom-storage-providers?view=aspnetcore-2.1&quot;&gt;here&lt;/a&gt;.&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;If you haven’t read my &lt;a href=&quot;/2015/01/14/persistence-ignorant-asp-net-identity-with-patterns-part-1/&quot;&gt;my previous posts&lt;/a&gt;, I’d suggest you stop here and read at least the first one to understand the problems I had with ASP.NET Identity 2.0 and how I solved them.&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;This article was written for .NET Core 2.0. The current version is 2.2, and there have been a number of changes to ASP.NET Core Identity. While implementation details may have changed, the concepts are largely the same.&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;Even though I authored this blog post, I have a demanding day job as well, and as such may not be able to address individual questions or concerns about this article or its associated GitHub repository.&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;still-has-problems-but-not-as-many&quot;&gt;Still Has Problems, but not as Many&lt;/h2&gt;

&lt;p&gt;As I started putting an example project together, I realized that there are still some problems with the out-of-the-box &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Individual User Accounts&lt;/code&gt; template, but there aren’t as many, and the ones remain are easier to solve.&lt;/p&gt;

&lt;h3 id=&quot;whats-fixed&quot;&gt;What’s Fixed&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;Your application’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;User&lt;/code&gt; class is no longer required to implement an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IUser&lt;/code&gt; interface to work with the Identity framework and is therefore no longer tightly coupled to it.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Built-in dependency injection allows you to write cleaner, loosely-coupled, more testable code.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;whats-still-a-problem&quot;&gt;What’s Still a Problem&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;The default template with the Individual User Accounts authentication option still generates a single-layered application that is more or less tightly coupled to Entity Framework.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UserManager&lt;/code&gt; class still has a bunch of hidden, feature-specific dependencies that you may not discover until runtime.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UserStore&lt;/code&gt; class can still grow into a huge god class that violates the Single Responsibility Principle.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;how-were-going-to-fix-it&quot;&gt;How We’re Going to Fix It&lt;/h2&gt;

&lt;p&gt;For this tutorial, we’re going to be building a &lt;strong&gt;.NET Core 2.0&lt;/strong&gt;, &lt;strong&gt;ASP.NET Core Web Application (Model View Controller)&lt;/strong&gt; with the &lt;strong&gt;Individual User Accounts&lt;/strong&gt; authentication option. Like before, we will focus on &lt;strong&gt;persistence ignorance&lt;/strong&gt; and design patterns.&lt;/p&gt;

&lt;h3 id=&quot;persistence-ignorance&quot;&gt;Persistence Ignorance&lt;/h3&gt;

&lt;p&gt;Fundamentally, persistence ignorance means that your entities shouldn’t care about how they’re stored, created, retrieved, updated, or deleted. Instead you just focus on modeling the business domain. The purpose of this post is not to explain persistence ignorance or Domain-Driven Design or try to convince you why you should use them, but if you’d like to know more, this is a good article: &lt;a href=&quot;http://www.codeproject.com/Articles/339725/Domain-Driven-Design-Clear-Your-Concepts-Before-Yo&quot;&gt;Domain Driven Design — Clear Your Concepts Before You Start&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;design-patterns&quot;&gt;Design Patterns&lt;/h3&gt;

&lt;p&gt;This application will follow some important architectural patterns. Because ASP.NET Core already supports inversion of control out of the box, we’re going touch on Dependency Injection but focus on the Repository and Unit of Work patterns.&lt;/p&gt;

&lt;h2 id=&quot;the-web-project&quot;&gt;The Web Project&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;NOTE: I used Visual Studio 2017 to create the Solution. You should be able accomplish everything I’m doing here with Visual Studio Code, but the steps will be different if you choose to go that route.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The first thing to do is to make sure your Visual Studio is up to date. If you have a yellow flag in the upper-right corner, and it tells you there’s an update available, then you need to update before continuing.&lt;/p&gt;

&lt;p&gt;Once you’re up to date, launch Visual Studio and create a new &lt;strong&gt;.NET Core 2.0&lt;/strong&gt;, &lt;strong&gt;ASP.NET Core Web Application (Model View Controller)&lt;/strong&gt; with the &lt;strong&gt;Individual User Accounts&lt;/strong&gt; authentication option. I called mine &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AspNetCoreIdentityExample.Web&lt;/code&gt;, and I named the solution &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AspNetCoreIdentityExample&lt;/code&gt; (without the .Web part). Visual Studio will work on it for a few seconds, and then your new project will be ready. Don’t run it quite yet, as there’s still a little more to do.&lt;/p&gt;

&lt;h3 id=&quot;optional-switch-from-localdb-to-sql-server-express-and-ssms&quot;&gt;Optional: Switch from LocalDB to SQL Server Express and SSMS&lt;/h3&gt;

&lt;p&gt;I’m not particularly fond of LocalDB. I prefer using a SQL Server Express database and SQL Server Management Studio (SSMS) because and it is closer to how it would be in a real-life production environment and is what I’m used to. You can use whichever you prefer. You don’t have to use SQL Server, a relational database, or even any kind of database at all (you could use a flat file if you wanted to). But this tutorial assumes some flavor of SQL Server. Anyway, to make the change to SQL Server Express (assuming it’s installed), edit the connection string in yoru appsettings.json as follows:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;{
  &quot;ConnectionStrings&quot;: {
    &quot;DefaultConnection&quot;: &quot;Server=Server=(local);Database=AspNetCoreIdentityExample;Trusted_Connection=True;MultipleActiveResultSets=true&quot;
  },
  &quot;Logging&quot;: {
    &quot;IncludeScopes&quot;: false,
    &quot;LogLevel&quot;: {
      &quot;Default&quot;: &quot;Warning&quot;
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;apply-migrations&quot;&gt;Apply Migrations&lt;/h3&gt;

&lt;p&gt;Apply the migrations by running the following command in the Package Manager Console:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;PM&amp;gt; Update-Database
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This step will create the database and tables and allows us to avoid scripting out the tables later (like I did in my previous ASP.NET Identity tutorial). Once the migrations have finished, you can verify the tables exist if you’d like:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/aspnet-core-identity-with-patterns/ssms-1.png&quot; alt=&quot;Using SQL Server Management Studio (SSMS) to verify that the ASP.NET Core Identity Tables have been created.&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;run-the-project&quot;&gt;Run the Project&lt;/h3&gt;

&lt;p&gt;At this point, you should have a bare bones ASP.NET Core Identity website. You should be able to register, login, etc. at this point. Go ahead, give it a shot: register a user, logout, login, change the password, etc.&lt;/p&gt;

&lt;h3 id=&quot;now-lets-break-it&quot;&gt;Now Let’s Break It&lt;/h3&gt;

&lt;p&gt;Now that we have a working application, let’s break it. Let’s do the following:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;Delete the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Data&lt;/code&gt; folder and everything in it.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Remove the dependency on Entity Framework from the NuGet packages and the project file.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Congratulations! Your application is now broken. Now, because I hate lieaving an application in an uncompilable state, let’s at least get it compiling (although not actually working at runtime).&lt;/p&gt;

&lt;h4 id=&quot;stub-out-the-userstore-and-rolestore-classes&quot;&gt;Stub out the UserStore and RoleStore classes&lt;/h4&gt;

&lt;p&gt;Like ASP.NET Identity 2.0, ASP.NET Core Identity uses two Store classes to persist Identity data: the UserStore and the Role Store. Because we’re doing a custom implementation of ASP.NET Core Identity, we need to roll our own. So create a new folder called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Identity&lt;/code&gt; and add two classes:&lt;/p&gt;

&lt;h5 id=&quot;customrolestorecs&quot;&gt;CustomRoleStore.cs&lt;/h5&gt;

&lt;p&gt;We’ll start with the easy one. Edit the class so it implements two interfaces: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IRoleStore&amp;lt;IdentityRole&amp;gt;&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IRoleClaimStore&amp;lt;IdentityRole&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;using System.Security.Claims;
using System.Threading;
using System.Threading.Tasks;

namespace AspNetCoreIdentityExample.Web.Identity
{
    public class CustomRoleStore :
        IRoleStore&amp;lt;IdentityRole&amp;gt;,
        IRoleClaimStore&amp;lt;IdentityRole&amp;gt;
    {
        // Implementations will go here
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Notice the red squiggly lines under the interfaces. Visual Studio can stub out the implementation for you. All you have to do is put your cursor over the interface with the squiggly red underline, press [CTRL]-[.] (period), and select &lt;strong&gt;Implement Interface&lt;/strong&gt; from the context menu.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/aspnet-core-identity-with-patterns/visual-studio-implement-interface.png&quot; alt=&quot;Let Visual Studio stub out your interface implementation for you&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Visual Studio will automatically generate your methods. They’ll all throw a NotImplementedException, but they’ll be there, and your code will compile. Repeat for each interface that needs to be implemented.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;NOTE: I honestly don’t know if Visual Studio Code will do this for you. If not, then I’m sorry, but you will have to stub out the methods yourself. It’s really going to suck for the CustomUserStore.cs class. Sorry.&lt;/em&gt;&lt;/p&gt;

&lt;h5 id=&quot;customuserstorecs&quot;&gt;CustomUserStore.cs&lt;/h5&gt;

&lt;p&gt;The CustomUserStore is a little more complicated. To get the same functionality as we would with the out-of-the-box EntityFramework implementation, we need to implement a lot of interfaces. More information on these interfaces can be found &lt;a href=&quot;https://docs.microsoft.com/en-us/aspnet/core/security/authentication/identity-custom-storage-providers?view=aspnetcore-2.1&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;using AspNetCoreIdentityExample.Domain;
using AspNetCoreIdentityExample.Domain.Entities;
using AspNetCoreIdentityExample.Web.Models;
using Microsoft.AspNetCore.Identity;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Threading;
using System.Threading.Tasks;

namespace AspNetCoreIdentityExample.Web.Identity
{
    public class CustomUserStore :
        IUserStore&amp;lt;ApplicationUser&amp;gt;,
        IUserPasswordStore&amp;lt;ApplicationUser&amp;gt;,
        IUserEmailStore&amp;lt;ApplicationUser&amp;gt;,
        IUserLoginStore&amp;lt;ApplicationUser&amp;gt;,
        IUserRoleStore&amp;lt;ApplicationUser&amp;gt;,
        IUserSecurityStampStore&amp;lt;ApplicationUser&amp;gt;,
        IUserClaimStore&amp;lt;ApplicationUser&amp;gt;,
        IUserAuthenticationTokenStore&amp;lt;ApplicationUser&amp;gt;,
        IUserTwoFactorStore&amp;lt;ApplicationUser&amp;gt;,
        IUserPhoneNumberStore&amp;lt;ApplicationUser&amp;gt;,
        IUserLockoutStore&amp;lt;ApplicationUser&amp;gt;,
        IQueryableUserStore&amp;lt;ApplicationUser&amp;gt;
    {
        // Implementations will go here
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Again, you will need to have Visual Studio generate the implementations of each of those interfaces. Tedious, I know.&lt;/p&gt;

&lt;h4 id=&quot;edit-startupcs&quot;&gt;Edit Startup.cs&lt;/h4&gt;

&lt;p&gt;The last thing we need to do to get the project to compile is to remove all references to Entity Framework and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ApplicationDbContext &lt;/code&gt;from the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ConfigureServices&lt;/code&gt; method of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Startup.cs&lt;/code&gt; class.&lt;/p&gt;

&lt;p&gt;Delete the following line from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Startup.cs&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;using Microsoft.EntityFrameworkCore;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Delete the following line from the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ConfigureServices&lt;/code&gt; method in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Startup.cs&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;services.AddDbContext&amp;lt;ApplicationDbContext&amp;gt;(options =&amp;gt;
    options.UseSqlServer(Configuration.GetConnectionString(&quot;DefaultConnection&quot;)));
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then, change the following line in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ConfigureServices&lt;/code&gt; method in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Startup.cs&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;services.AddIdentity&amp;lt;ApplicationUser, IdentityRole&amp;gt;()
    .AddEntityFrameworkStores&amp;lt;ApplicationDbContext&amp;gt;()
    .AddDefaultTokenProviders();
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;to this:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;services.AddIdentity&amp;lt;ApplicationUser, IdentityRole&amp;gt;()
.AddDefaultTokenProviders();
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Your application should now compile. If you tried to run it, it will throw exceptions everywhere, but if it doesn’t at least compile, then go back over the steps and make sure you did everything correctly.&lt;/p&gt;

&lt;h2 id=&quot;next-steps&quot;&gt;Next Steps&lt;/h2&gt;

&lt;p&gt;This is where we will leave it for now. So far, so good. In Part 2, we’ll put the Web Project on the back burner and focus on the Domain and Data layers; and then in Part 3, we’ll come back to the Web Project to flesh out the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CustomRoleStore&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CustomUserStore&lt;/code&gt; classes, and tie everything together into a working application.&lt;/p&gt;

&lt;p&gt;Until next time, happy coding!&lt;/p&gt;

</content>
  </entry>
  
  <entry>
    <id>http://timschreiber.com/2018/04/27/stackoverflow-finally-admits-it-has-an-asshole-problem/</id>
    <link type="text/html" rel="alternate" href="http://timschreiber.com//2018/04/27/stackoverflow-finally-admits-it-has-an-asshole-problem/"/>
    <title>StackOverlow Finally Admits It Has an Asshole Problem</title>
    <updated>2018-04-27T00:00:00+00:00</updated>
    <author>
      <name>Timothy P. Schreiber</name>
      <uri>http://timschreiber.com/</uri>
    </author>
    <content type="html">&lt;p&gt;Back in October 2013, I wrote a blog post titled, &lt;a href=&quot;/2013/10/30/beware-the-stackoverlords/&quot;&gt;Beware the StackOverlords!&lt;/a&gt; that expressed my frustration with the culture of assholery in the StackOverflow community. Since then, it’s become one of my most popular posts — certainly the most commented. Many agreed with my thoughts, and many denied there was a problem.&lt;/p&gt;

&lt;p&gt;As it turns out, there is a problem, and StackOverflow is finally ready to talk about it. So, stop now and read: &lt;a href=&quot;https://stackoverflow.blog/2018/04/26/stack-overflow-isnt-very-welcoming-its-time-for-that-to-change/&quot;&gt;Stack Overflow Isn’t Very Welcoming. It’s Time for That to Change&lt;/a&gt;, on the official StackOverflow blog if you haven’t already. I’ll wait. Also if you haven’t read &lt;a href=&quot;/2013/10/30/beware-the-stackoverlords/&quot;&gt;my original StackOverlords post&lt;/a&gt;, I’d suggest you read it, too.&lt;/p&gt;

&lt;h4 id=&quot;all-good-lets-proceed&quot;&gt;All good? Let’s proceed…&lt;/h4&gt;

&lt;p&gt;At the beginning of my original StackOverlords post, I stated (maybe a little too dramatically) that long-time users wield their elevated privileges against newbie scum. In other words, the community is not welcoming to new users. In their blog post, Stack Overflow finally admits it:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Too many people experience Stack Overflow as a hostile or elitist place, especially newer coders, women, people of color, and others in marginalized groups.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I went on to say that it was something they were refusing to address, and again, they agreed:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Our employees and community have cared about this for a long time, but we’ve struggled to talk about it publicly or to sufficiently prioritize it in recent years.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It’s good to know that I wasn’t completely off-base with my original analysis, and I’m happy to see they are finally acknowledging it. After all, the first step toward positive change is admitting there’s a problem.&lt;/p&gt;

&lt;h4 id=&quot;so-what-are-they-going-to-do-about-it&quot;&gt;So, what are they going to do about it?&lt;/h4&gt;

&lt;p&gt;Over the years, StackOverflow has made some half-assed attempts at making their community kinder and more inclusive:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Initiatives like the Summer of Love, the closing overhaul, revising the “Be Nice” Policy, and our coaching experiment all came from our desire to build a place where everyone feels welcome. But we never felt comfortable acknowledging that we had a serious problem, and we under-resourced it. Badly. … Inclusion efforts (and other public Q&amp;amp;A work) have consistently been “fairly important, like… roughly #3 on our list of priorities?” Which meant they got allocated roughly zero resources.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;But now they say all of that is changing. They’re prioritizing the issue and staffing it with talent from across the company. They’re paying more attention to the community, conducting user research, and revisiting old policies. Some of the specific areas on which they plan to focus first include:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Shifting from “don’t be an asshole” to “be welcoming.”&lt;/li&gt;
  &lt;li&gt;Flagging and deleting unkind comments&lt;/li&gt;
  &lt;li&gt;Making it easier for new users to succeed&lt;/li&gt;
  &lt;li&gt;Stopping to judge others for not knowing things&lt;/li&gt;
  &lt;li&gt;Rejecting the false dichotomy between quality and kindness.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Of these, I am most excited to see their plans for helping new users succeed:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;We’re planning to test a new “beginner” ask page that breaks the question box into multiple fields – one for each of the key things answerers need to help:&lt;/p&gt;
  &lt;ul&gt;
    &lt;li&gt;“What did you want to happen?”&lt;/li&gt;
    &lt;li&gt;“What actually happened?” (Include any error details)&lt;/li&gt;
    &lt;li&gt;“Paste the shortest block of code that reproduces the problem.” (We’ll format it!)&lt;/li&gt;
    &lt;li&gt;“Describe what you’ve tried so far” (including searches, etc.)&lt;/li&gt;
  &lt;/ul&gt;
&lt;/blockquote&gt;

&lt;h4 id=&quot;is-it-enough-i-hope-so-but&quot;&gt;Is it enough? I hope so, but…&lt;/h4&gt;

&lt;p&gt;In the game of poker, there’s a thing called a tell, which is a subtle, involuntary difference in a player’s behavior that can disclose when they are bluffing. It can manifest as behaviors, changes in breathing, tone of voice, facial expressions, etc. But the important thing to know is that players don’t realize they’re doing it. Tells aren’t just for poker, either. Sometimes, when someone tries to craft a message that doesn’t really match their culture and values, the resulting cognitive dissonance can be manifest in subtle, odd word choices.&lt;/p&gt;

&lt;p&gt;Here’s how I think that applies to StackOverflow: right after they admit they are perceived to be hostile or elitist toward, “newer coders, women, people of color, and others in marginalized groups,” they refer to those groups as “outsiders” in the very next paragraph.&lt;/p&gt;

&lt;p&gt;I don’t want to read too much into it, but I do think that word choice is interesting, to say the least. I really do hope it’s nothing and that there aren’t any unaddressed cultural skeletons still lurking in their closet. Believe it or not, I really do want StackOverflow to implement and continuously improve these new initiatives over the long term. I want them to be kind, inclusive, and successful.&lt;/p&gt;

&lt;p&gt;So for now, I’m cautiously optimistic about what the future holds for the StackOverflow community; and who knows, maybe I’ll even start participating there again.&lt;/p&gt;

</content>
  </entry>
  
  <entry>
    <id>http://timschreiber.com/music/2017/05/13/demo-track-sampler/</id>
    <link type="text/html" rel="alternate" href="http://timschreiber.com//music/2017/05/13/demo-track-sampler/"/>
    <title>Demo Track Sampler</title>
    <updated>2017-05-13T00:00:00+00:00</updated>
    <author>
      <name>Timothy P. Schreiber</name>
      <uri>http://timschreiber.com/</uri>
    </author>
    <content type="html">&lt;p&gt;I came close in 1999 when I recorded a couple of tracks with a Provo, UT band called Mile 5.&lt;/p&gt;

&lt;p&gt;About a year ago, I started devoting more time to music, and about six months ago, I started writing songs and envisioning an album. Recently, I decided to get it all done by the end of the year.&lt;/p&gt;

&lt;p&gt;Today, I reached the milestone of having half the album written and demos recorded. There’s still a lot to do, but I wanted to get it out there and give the world a sneak peak!&lt;/p&gt;

&lt;p&gt;So have a listen:&lt;/p&gt;

&lt;iframe width=&quot;100%&quot; height=&quot;450&quot; scrolling=&quot;no&quot; frameborder=&quot;no&quot; src=&quot;https://w.soundcloud.com/player/?url=https%3A//api.soundcloud.com/playlists/321516329&amp;amp;auto_play=false&amp;amp;hide_related=false&amp;amp;show_comments=true&amp;amp;show_user=true&amp;amp;show_reposts=false&amp;amp;visual=true&quot;&gt;&lt;/iframe&gt;
</content>
  </entry>
  
  <entry>
    <id>http://timschreiber.com/2016/07/29/im-an-all-around-nic-guy/</id>
    <link type="text/html" rel="alternate" href="http://timschreiber.com//2016/07/29/im-an-all-around-nic-guy/"/>
    <title>I'm an All Around NIC Guy</title>
    <updated>2016-07-29T00:00:00+00:00</updated>
    <author>
      <name>Timothy P. Schreiber</name>
      <uri>http://timschreiber.com/</uri>
    </author>
    <content type="html">&lt;p&gt;I was with ZirMed for only seven short months and was not actively seeking other employment, but this opportunity with Kentucky Interactive just kind of fell in my lap, and I would have been crazy not to pursue it.&lt;/p&gt;

&lt;p&gt;The Kentucky Interactive office is only 20 minutes from my house. Compared to my horrendous 80 to 90 minute commute each way to ZirMed, I’m getting the equivalent of more than an entire workday back each week. What to do, what to do?&lt;/p&gt;

&lt;p&gt;The savings in time, gas, and wear and tear on my vehicle alone is well worth switching, but my new job offers me more variety and opportunities to better utilize the depth and breadth of my skills and experience and gain experience with newer technologies. I originally had high hopes for a long career with ZirMed, and it was not an easy decision to leave after such a short time. I had expected that after six months I would have seen more variety of work, used more current technologies, and integrated more successfully with my team and the company culture as a whole. But as it turned out, it just wasn’t a good fit on multiple levels.&lt;/p&gt;

&lt;p&gt;My compensation with Kentucky Interactive is excellent, and the benefits package is well above industry norms. The local office is small, but they have the backing of a publicly-traded corporation. The people are friendly and genuine, and I’m very comfortable with them. Most importantly, it just feels “right.”&lt;/p&gt;

&lt;p&gt;I’m looking forward to a long and prosperous career with Kentucky Interactive and NIC!&lt;/p&gt;

&lt;h3 id=&quot;about-kentucky-interactive&quot;&gt;About Kentucky Interactive&lt;/h3&gt;

&lt;p&gt;Kentucky Interactive LLC operates Kentucky.gov as a collaborative effort with the Commonwealth of Kentucky. Based in Frankfort, Kentucky Interactive provides a secure, mobile-friendly platform that allows government agencies of any size to conduct business online and improve public access to information. Kentucky Interactive is part of NIC’s (NASDAQ: EGOV) family of companies.&lt;/p&gt;

&lt;h3 id=&quot;about-nic&quot;&gt;About NIC&lt;/h3&gt;

&lt;p&gt;Founded in 1992, NIC (NASDAQ: EGOV) is the nation’s leading provider of innovative digital government solutions and secure payment processing, which help make government more accessible to everyone through technology. The family of NIC companies provides digital government solutions for more than 4,500 federal, state, and local agencies in the United States. Forbes has named NIC as one of the “100 Best Small Companies in America” six times and the company has been included four times on the Barron’s 400 Index. Additional information is available at &lt;a href=&quot;http://www.egov.com&quot;&gt;http://www.egov.com&lt;/a&gt;.&lt;/p&gt;

</content>
  </entry>
  
  <entry>
    <id>http://timschreiber.com/2016/07/19/profanity-and-professionalism/</id>
    <link type="text/html" rel="alternate" href="http://timschreiber.com//2016/07/19/profanity-and-professionalism/"/>
    <title>Profanity and Professionalism</title>
    <updated>2016-07-19T00:00:00+00:00</updated>
    <author>
      <name>Timothy P. Schreiber</name>
      <uri>http://timschreiber.com/</uri>
    </author>
    <content type="html">&lt;p&gt;Then I began to seriously consider the topic and its relevance to professionalism and software craftsmanship.&lt;/p&gt;

&lt;p&gt;I grew up in a “different” time when profanity was less accepted, when one could still raise more than a few eyebrows with a well-aimed f-bomb. But that doesn’t mean I didn’t embrace profanity at some point in my life. During my college years, even though I was living in Utah surrounded by conversationally-pure Mormons, profanity became so tightly integrated into my vernacular that a roommate once described me as having a “poetic” mastery of it.&lt;/p&gt;

&lt;p&gt;Nowadays, perhaps coming full circle, I no longer have much use for profanity. On rare occasions when I deem appropriate, I may casually drop a curse word among carefully selected company. I’m never terribly offended by occasional curse word in entertainment or casual conversation as long as it doesn’t come across as gratuitous or forced, but in the workplace among assumedly professional co-workers, hearing more than the rarest of slips of the tongue is enough to make me cringe a little. I have been known to politely ask regular offenders to kindly refrain from swearing around me, and for the most part, they at least try. But there are those who respond with a disgusted, offended expression or an outright “f*** you,” as if their constitutionally protected right to free speech had just been trampled under the boots of my moral fascism.&lt;/p&gt;

&lt;p&gt;Some have pointed out that some very successful people – rock stars of the workplace if you will – have used profanity to craft their individual personae, citing &lt;a href=&quot;https://zachholman.com/posts/swearing/&quot;&gt;Zach Holman&lt;/a&gt; (GitHub), &lt;a href=&quot;https://www.garyvaynerchuk.com/&quot;&gt;Gary Vaynerchuk&lt;/a&gt; (the veritable poster boy of profanity in business), and others as examples. To that argument, I respond with some wisdom that was imparted to me many years ago: For every one rock star, there are a hundred or a thousand or ten thousand unknown musicians. Seriously, work your butt off, and if you do end up a rock star, then by all means, act like one. But until you get there, your co-workers and employers (especially your employers) are going to expect you to conduct yourself a certain way.&lt;/p&gt;

&lt;p&gt;Others argue that profanity is fine as long as you’re aware of your audience. To that, I ask: Who do you think your audience is when you’re at work? Your co-workers? Your employer? Your customers? The correct answer is all of the above. It’s a tough audience that has the power to make or break you. As &lt;a href=&quot;http://www.hanselman.com/blog/ProfanityDoesntWork.aspx&quot;&gt;Scott Hanselman&lt;/a&gt; put it:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;You take no chances of offending by not swearing, but you guarantee to offend someone if you do.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Like it or not, swearing at work has a negative impact on how others perceive you. A &lt;a href=&quot;http://www.careerbuilder.com/share/aboutus/pressreleasesdetail.aspx?ed=12%2F31%2F2012&amp;amp;id=pr709&amp;amp;sd=7%2F25%2F2012&quot;&gt;2012 survey conducted by CareerBuilder&lt;/a&gt; found the following:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Employers are inclined to think less of an employee who swears at work for a variety of reasons. Most (81 percent) believe that the use of curse words brings the employee’s professionalism into question. Others are concerned with the lack of control (71 percent) and lack of maturity (68 percent) demonstrated by swearing at work, while 54 percent said swearing at work makes an employee appear less intelligent.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Furthermore, the results of the survey state:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Sixty-four percent of employers said that they’d think less of an employee who repeatedly uses curse words, and 57 percent said they’d be less likely to promote someone who swears in the office.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Having been a manager, I wholeheartedly agree with the results of the survey. As far as my software development teams are concerned, I look for senior level professionals who aren’t afraid to mentor others and who can stay focused and productive under pressure. Using profanity at work shows me you have none of that.&lt;/p&gt;

&lt;p&gt;There are companies out there where on-the-job profanity has become an integral part of the culture. Managers and employees alike seem quite uninhibited with regard to profanity. Sure, there’s an unspoken rule of common courtesy in which race and gender-derogatory words are never used, and swear words are never directed at anyone directly. But profanity is regularly used in cubicles, hallways, meetings, and electronic communication. I recently left a job due, in part, to their acceptance and even encouragement of profanity in the office.&lt;/p&gt;

&lt;p&gt;In cases of profanity work culture, I think the most important question to consider is this: What benefit do you expect to receive from your use of profanity? If the answer is to fit into the company culture, then one must further consider whether it sufficiently offsets the virtual guarantee of alienating someone – perhaps someone with the power to make or break you. Indeed, Scott Hanselman agrees:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;I believe that swearing decreases your reach and offers little benefit in return… Being generally pleasant and helpful isn’t sugarcoating, it’s being pleasant and helpful.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;As a manager, I was less concerned with how employees embraced particular aspects of the culture than I was with how easy the employee was to work with – ie. pleasant, helpful, and (might I add) dependable.&lt;/p&gt;

&lt;p&gt;Considering the lack of benefit and the risk of alienating coworkers and customers, one must ask whether one can use profanity on the job and still consider themselves a professional. As evidenced by the CareerBuilder survey, I believe the answer is a resounding “no.” Additionally, companies that embrace profanity as part of their culture may unwittingly sending the wrong message to their investors, customers, and everyone else, that they are unprofessional, immature, and lacking credibility. Whether or not it’s true, that is the perception.&lt;/p&gt;

&lt;p&gt;But then again, I’m probably just old and stodgy and out of touch with reality.&lt;/p&gt;

&lt;p&gt;Get off my lawn.&lt;/p&gt;

</content>
  </entry>
  
  <entry>
    <id>http://timschreiber.com/2016/02/15/how-to-save-windows-spotlight-lockscreen-images/</id>
    <link type="text/html" rel="alternate" href="http://timschreiber.com//2016/02/15/how-to-save-windows-spotlight-lockscreen-images/"/>
    <title>How to Save Windows Spotlight Lockscreen Images</title>
    <updated>2016-02-15T00:00:00+00:00</updated>
    <author>
      <name>Timothy P. Schreiber</name>
      <uri>http://timschreiber.com/</uri>
    </author>
    <content type="html">&lt;p&gt;It’s silly and trivial, but one of my favorite features of Windows 10 is all the cool, new pictures that appear on my lockscreen from time to time. But I always thought it was an oversight on the part of Microsoft that they didn’t let us use them as our desktop backgrounds.&lt;/p&gt;

&lt;p&gt;I used to try to get the images by hitting &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PrtScn&lt;/code&gt; on the lockscreen, pasting it into Paint, saving it as a JPG, and then dragging it into Google Image Search to search by image. Only once in a while could I find a matching image without all the overlays and at a high enough resolution I could use as my desktop background. Each time I slogged through countless Google Images results, disappointed that I couldn’t find the right pictures, I thought to myself, “there has got to be a better way.”&lt;/p&gt;

&lt;p&gt;Sure enough, there is. It’s not only a better way — it’s kinda failproof. It just took a little digging around. So, let’s get started:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Open the Run dialog prompt by holding down your windows key plus R (&lt;span class=&quot;fa fa-windows&quot;&gt;&lt;/span&gt; + R).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;img src=&quot;/img/how-to-save-windows-spotlight-lockscreen-images/run-dialog-2.png&quot; alt=&quot;Run Dialog Prompt&quot; /&gt;&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Copy this &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;%localappdata%\Packages\Microsoft.Windows.ContentDeliveryManager_cw5n1h2txyewy\LocalState\Assets&lt;/code&gt; and paste it into the Run dialog prompt. Click &lt;strong&gt;OK&lt;/strong&gt;, and then a folder will open in File Explorer, with a bunch of cryptic filenames.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;img src=&quot;/img/how-to-save-windows-spotlight-lockscreen-images/assets-folder.png&quot; alt=&quot;Assets Folder&quot; /&gt;&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;Copy all of those files to another folder (I created one on my Desktop called &lt;em&gt;Spotlight Images&lt;/em&gt;. If you see a warning prompt when you try to copy the files, just click &lt;strong&gt;OK&lt;/strong&gt;.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Close the Window you opened in Step 2. It’s best to not accidentally mess up any of those files.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Back to the files you just copied… Now you need to rename all those files with a JPG extension. You could do them one at a time, but I think it’s easier to do them all at once. Just &lt;strong&gt;Shift + Right-click&lt;/strong&gt; with your mouse in the directory you copied all the images to, and then select the &lt;strong&gt;Open command window here&lt;/strong&gt; option from the menu.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;img src=&quot;/img/how-to-save-windows-spotlight-lockscreen-images/open-command-window.png&quot; alt=&quot;Open Command Window&quot; /&gt;&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Now you need to rename all those weird files with a JPG file extension. So, in the command prompt window, type &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ren *.* *.JPG&lt;/code&gt; and hit &lt;strong&gt;ENTER&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;img src=&quot;/img/how-to-save-windows-spotlight-lockscreen-images/command-window-rename.png&quot; alt=&quot;Rename Files with a JPG extension&quot; /&gt;&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;Close the command prompt window.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Back in the window containing the files you copied over, make sure you’re viewing them in thumbnail mode. It’s pretty easy at this point to figure out which ones are Spotlight pictures and which ones aren’t. You can hold down &lt;strong&gt;CTRL&lt;/strong&gt; and left-click any files you don’t want to keep and delete them all at once.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;img src=&quot;/img/how-to-save-windows-spotlight-lockscreen-images/delete-non-backgrounds.png&quot; alt=&quot;Delete the Non Spotlight Images&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Congratulations! Now you have usable copies of the Spotlight lockscreen images that you can use for your desktop background or any other purpose! Windows 10 downloads new Spotlight pictures regularly, so it wouldn’t be a bad idea to repeat these steps every couple of weeks or so to make sure you get all the ones you like.&lt;/p&gt;

</content>
  </entry>
  
  <entry>
    <id>http://timschreiber.com/2016/01/06/und-zen-i-vas-a-zirman/</id>
    <link type="text/html" rel="alternate" href="http://timschreiber.com//2016/01/06/und-zen-i-vas-a-zirman/"/>
    <title>Und Zen I Vas a ZirMan</title>
    <updated>2016-01-06T00:00:00+00:00</updated>
    <author>
      <name>Timothy P. Schreiber</name>
      <uri>http://timschreiber.com/</uri>
    </author>
    <content type="html">&lt;p&gt;My job with SIS is what enabled me to move my wife and kids closer to our extended family, and for that, I will forever be grateful. But at some point after I was hired, business priorities changed, and I found myself continually wondering what my future in the company would be. I never lacked for work, but I didn’t have the variety or degree of career growth I had hoped for. Then, when my compensation was flat for too long, I knew it was time to move on. There were other reasons as well, but those are the big ones.&lt;/p&gt;

&lt;p&gt;I began a passive job search in mid-2015 and pursued a few leads that didn’t go anywhere. Then in November, I was presented with an opportunity at ZirMed in Louisville. At the time, I wasn’t seriously considering anything in Louisville, since the commute would be longer than I wanted. The recruiter persisted, and I agreed to a phone interview mostly just to get him off my back. Well, the phone interview went really well and piqued my interest in the company. I rocked my in-person interview shortly after Thanksgiving, and by mid-December had negotiated and accepted an offer with an excellent compensation package.&lt;/p&gt;

&lt;p&gt;What interested me most in ZirMed is that it is, at its heart, a software company. It has a progressive, tech company culture with a bunch of perks. The people are friendly and intelligent, the work is interesting, and their methodologies and standards are largely in line with my own development philosophy. The company is profitable and growing like crazy, and there is plenty of opportunity for variety, personal improvement, and career growth.&lt;/p&gt;

&lt;p&gt;As of 4-Jan-2015, I am now a Senior .NET Engineer at ZirMed – a ZirMan as it were. I’m still getting through the onboarding process and training, but I am excited to see what the future holds.&lt;/p&gt;

&lt;h4 id=&quot;about-zirmed&quot;&gt;About ZirMed&lt;/h4&gt;

&lt;p&gt;ZirMed’s comprehensive end-to-end platform of cloud-based revenue cycle management solutions—including patient access, charge integrity, claims management, AR management, patient responsibility, and population health management–empowers healthcare organizations of all sizes and types to optimize value-driven and fee-for-service reimbursements. By combining breakthrough predictive analytics technology with innovative software development and the industry’s most advanced transactional network, ZirMed solutions extract actionable insights that improve our clients’ revenue cycles while streamlining workflows, increasing operating efficiencies, and driving bottom-line results. ZirMed’s technology and client support continue to be honored with top industry awards, including KLAS®, Healthcare Informatics, Best of SaaS Showplace (BoSS), and Black Book Rankings. To learn how ZirMed can help your healthcare organization boost its financial performance in an era of changing reimbursement models and rising operating costs, visit &lt;a href=&quot;http://www.zirmed.com&quot;&gt;www.ZirMed.com&lt;/a&gt;.&lt;/p&gt;

</content>
  </entry>
  
  <entry>
    <id>http://timschreiber.com/2015/01/28/persistence-ignorant-asp-net-identity-with-patterns-part-4/</id>
    <link type="text/html" rel="alternate" href="http://timschreiber.com//2015/01/28/persistence-ignorant-asp-net-identity-with-patterns-part-4/"/>
    <title>Persistence-Ignorant ASP.NET Identity with Patterns (Part 4)</title>
    <updated>2015-01-28T00:00:00+00:00</updated>
    <author>
      <name>Timothy P. Schreiber</name>
      <uri>http://timschreiber.com/</uri>
    </author>
    <content type="html">&lt;h4 style=&quot;color:#e53935;font-style:italic;&quot;&gt;See my updated tutorial for ASP.NET Core Identity &lt;a href=&quot;/2018/05/07/aspnet-core-identity-with-patterns/&quot;&gt;here&lt;/a&gt;.&lt;/h4&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/2015/01/14/persistence-ignorant-asp-net-identity-with-patterns-part-1/&quot;&gt;Part 1&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2015/01/25/persistence-ignorant-asp-net-identity-with-patterns-part-2/&quot;&gt;Part 2&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2015/01/26/persistence-ignorant-asp-net-identity-with-patterns-part-3/&quot;&gt;Part 3&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Part 4&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The source code for this series of posts is available at on my GitHub: &lt;a href=&quot;https://github.com/timschreiber/Mvc5IdentityExample&quot;&gt;https://github.com/timschreiber/Mvc5IdentityExample&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;h6 id=&quot;this-series-of-posts-requires-a-functional-understanding-of-aspnet-identity-2x-if-you-havent-had-at-least-some-kind-of-exposure-this-is-a-good-place-to-start-httpwwwaspnetidentity&quot;&gt;&lt;em&gt;This series of posts requires a functional understanding of ASP.NET Identity 2.x. If you haven’t had at least some kind of exposure, this is a good place to start: &lt;a href=&quot;http://www.asp.net/identity&quot;&gt;http://www.asp.net/identity&lt;/a&gt;.&lt;/em&gt;&lt;/h6&gt;

&lt;p&gt;In Part 1, I identified some of the shortcomings in the default template for ASP.NET MVC 5 web applications using ASP.NET Identity for “Individual User Accounts” authentication, and then laid out the requirements for a better implementation. In Part 2, we created the Visual Studio Solution for our ASP.NET Identity Example, broke the out-of-the-box dependencies on Entity Framework, and coded our Domain Layer. In Part 3, we defined our entity mappings, coded the DbContext, and implemented the interfaces for the Repositories and Unit of Work that we defined in the Domain Layer. In this part, we’re going to switch our focus to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Mvc5IdentityExample.Web&lt;/code&gt; project and dive straight into the guts of ASP.NET Identity. Then, we’re going to hook everything up with Unity and finish with a fully functional MVC5 web application with ASP.NET Identity done the “right way.”&lt;/p&gt;

&lt;!--more--&gt;

&lt;h3 id=&quot;aspnet-identity-classes&quot;&gt;ASP.NET Identity Classes&lt;/h3&gt;

&lt;p&gt;When we broke the coupling between our Presentation Layer and Entity Framework, we lost the references to the implementations of four classes that make ASP.NET Identity work (that’s a good thing). So now we have to replace them with classes that work with our Entities, Repositories, and Unit of Work. There are two model classes: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IdentityUser&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IdentityRole&lt;/code&gt;, and two data store classes: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UserStore&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RoleStore&lt;/code&gt;. You might remember &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UserStore&lt;/code&gt; from my rants in &lt;a href=&quot;/2015/01/14/persistence-ignorant-asp-net-identity-with-patterns-part-1/&quot;&gt;Part 1&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To get started, let’s add project references to our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Mvc5IdentityExample.Domain&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Mvc5IdentityExample.Data.EntityFramework&lt;/code&gt; projects. Then, create a folder In the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MvcIdentityExample.Web&lt;/code&gt; project and call it &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Identity&lt;/code&gt;. This is where we’re going to put our classes.&lt;/p&gt;

&lt;h4 id=&quot;model-classes&quot;&gt;Model Classes&lt;/h4&gt;

&lt;p&gt;First, we’re going to get started on our model classes:&lt;/p&gt;

&lt;h6 id=&quot;identityusercs&quot;&gt;IdentityUser.cs&lt;/h6&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;using Microsoft.AspNet.Identity;
using System;

namespace Mvc5IdentityExample.Web.Identity
{
    public class IdentityUser : IUser&amp;lt;Guid&amp;gt;
    {
        public IdentityUser()
        {
            this.Id = Guid.NewGuid();
        }

        public IdentityUser(string userName)
            : this()
        {
            this.UserName = userName;
        }

        public Guid Id { get; set; }
        public string UserName { get; set; }
        public virtual string PasswordHash { get; set; }
        public virtual string SecurityStamp { get; set; }
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h6 id=&quot;identityrolecs&quot;&gt;IdentityRole.cs&lt;/h6&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;using Microsoft.AspNet.Identity;
using System;

namespace Mvc5IdentityExample.Web.Identity
{
    public class IdentityRole : IRole&amp;lt;Guid&amp;gt;
    {
        public IdentityRole()
        {
            this.Id = Guid.NewGuid();
        }

        public IdentityRole(string name)
            : this()
        {
            this.Name = name;
        }

        public IdentityRole(string name, Guid id)
        {
            this.Name = name;
            this.Id = id;
        }

        public Guid Id { get; set; }
        public string Name { get; set; }
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;data-store-classes&quot;&gt;Data Store Classes&lt;/h4&gt;

&lt;p&gt;In the ASP.NET Identity &lt;a href=&quot;http://www.asp.net/identity/overview/extensibility/overview-of-custom-storage-providers-for-aspnet-identity#architecture&quot;&gt;documentation&lt;/a&gt;, Microsoft author Tom FitzMacken acknowledges their data store classes are “closely coupled with the persistence mechanism,” which they then closely couple to their Presentation Layer in the default Entity Framework implementation. Done correctly in a persistence-ignorant kind of way, these classes take the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IdentityUser&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IdentityRole&lt;/code&gt; models that ASP.NET Identity likes, turn them into the Entities that our Repositories and Unit of Work like, and make sure everything gets written out to some persistence mechanism, whatever that may be.&lt;/p&gt;

&lt;p&gt;You might notice we have an empty &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Dispose&lt;/code&gt; method in our data store classes. That’s because ASP.NET Identity requires us to implement &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IDisposable&lt;/code&gt;, but we’re letting Unity manage the lifetime of our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IUnitOfWork&lt;/code&gt;, which is the only injected dependency these classes have.&lt;/p&gt;

&lt;h6 id=&quot;userstorecs&quot;&gt;UserStore.cs&lt;/h6&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;﻿using Microsoft.AspNet.Identity;
using Mvc5IdentityExample.Domain;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using Entities = Mvc5IdentityExample.Domain.Entities;

namespace Mvc5IdentityExample.Web.Identity
{
    public class UserStore : IUserLoginStore&amp;lt;IdentityUser, Guid&amp;gt;, IUserClaimStore&amp;lt;IdentityUser, Guid&amp;gt;, IUserRoleStore&amp;lt;IdentityUser, Guid&amp;gt;, IUserPasswordStore&amp;lt;IdentityUser, Guid&amp;gt;, IUserSecurityStampStore&amp;lt;IdentityUser, Guid&amp;gt;, IUserStore&amp;lt;IdentityUser, Guid&amp;gt;, IDisposable
    {
        private readonly IUnitOfWork _unitOfWork;

        public UserStore(IUnitOfWork unitOfWork)
        {
            _unitOfWork = unitOfWork;
        }

        #region IUserStore&amp;lt;IdentityUser, Guid&amp;gt; Members
        public Task CreateAsync(IdentityUser user)
        {
            if (user == null)
                throw new ArgumentNullException(&quot;user&quot;);

            var u = getUser(user);

            _unitOfWork.UserRepository.Add(u);
            return _unitOfWork.SaveChangesAsync();
        }

        public Task DeleteAsync(IdentityUser user)
        {
            if (user == null)
                throw new ArgumentNullException(&quot;user&quot;);

            var u = getUser(user);

            _unitOfWork.UserRepository.Remove(u);
            return _unitOfWork.SaveChangesAsync();
        }

        public Task&amp;lt;IdentityUser&amp;gt; FindByIdAsync(Guid userId)
        {
            var user = _unitOfWork.UserRepository.FindById(userId);
            return Task.FromResult&amp;lt;IdentityUser&amp;gt;(getIdentityUser(user));
        }

        public Task&amp;lt;IdentityUser&amp;gt; FindByNameAsync(string userName)
        {
            var user = _unitOfWork.UserRepository.FindByUserName(userName);
            return Task.FromResult&amp;lt;IdentityUser&amp;gt;(getIdentityUser(user));
        }

        public Task UpdateAsync(IdentityUser user)
        {
            if (user == null)
                throw new ArgumentException(&quot;user&quot;);

            var u = _unitOfWork.UserRepository.FindById(user.Id);
            if (u == null)
                throw new ArgumentException(&quot;IdentityUser does not correspond to a User entity.&quot;, &quot;user&quot;);

            populateUser(u, user);

            _unitOfWork.UserRepository.Update(u);
            return _unitOfWork.SaveChangesAsync();
        }
        #endregion

        #region IDisposable Members
        public void Dispose()
        {
            // Dispose does nothing since we want Unity to manage the lifecycle of our Unit of Work
        }
        #endregion

        #region IUserClaimStore&amp;lt;IdentityUser, Guid&amp;gt; Members
        public Task AddClaimAsync(IdentityUser user, Claim claim)
        {
            if (user == null)
                throw new ArgumentNullException(&quot;user&quot;);
            if (claim == null)
                throw new ArgumentNullException(&quot;claim&quot;);

            var u = _unitOfWork.UserRepository.FindById(user.Id);
            if (u == null)
                throw new ArgumentException(&quot;IdentityUser does not correspond to a User entity.&quot;, &quot;user&quot;);

            var c = new Entities.Claim
            {
                ClaimType = claim.Type,
                ClaimValue = claim.Value,
                User = u
            };
            u.Claims.Add(c);

            _unitOfWork.UserRepository.Update(u);
            return _unitOfWork.SaveChangesAsync();
        }

        public Task&amp;lt;IList&amp;lt;Claim&amp;gt;&amp;gt; GetClaimsAsync(IdentityUser user)
        {
            if (user == null)
                throw new ArgumentNullException(&quot;user&quot;);

            var u = _unitOfWork.UserRepository.FindById(user.Id);
            if (u == null)
                throw new ArgumentException(&quot;IdentityUser does not correspond to a User entity.&quot;, &quot;user&quot;);

            return Task.FromResult&amp;lt;IList&amp;lt;Claim&amp;gt;&amp;gt;(u.Claims.Select(x =&amp;gt; new Claim(x.ClaimType, x.ClaimValue)).ToList());
        }

        public Task RemoveClaimAsync(IdentityUser user, Claim claim)
        {
            if (user == null)
                throw new ArgumentNullException(&quot;user&quot;);
            if (claim == null)
                throw new ArgumentNullException(&quot;claim&quot;);

            var u = _unitOfWork.UserRepository.FindById(user.Id);
            if (u == null)
                throw new ArgumentException(&quot;IdentityUser does not correspond to a User entity.&quot;, &quot;user&quot;);

            var c = u.Claims.FirstOrDefault(x =&amp;gt; x.ClaimType == claim.Type &amp;amp;&amp;amp; x.ClaimValue == claim.Value);
            u.Claims.Remove(c);

            _unitOfWork.UserRepository.Update(u);
            return _unitOfWork.SaveChangesAsync();
        }
        #endregion

        #region IUserLoginStore&amp;lt;IdentityUser, Guid&amp;gt; Members
        public Task AddLoginAsync(IdentityUser user, UserLoginInfo login)
        {
            if (user == null)
                throw new ArgumentNullException(&quot;user&quot;);
            if (login == null)
                throw new ArgumentNullException(&quot;login&quot;);

            var u = _unitOfWork.UserRepository.FindById(user.Id);
            if (u == null)
                throw new ArgumentException(&quot;IdentityUser does not correspond to a User entity.&quot;, &quot;user&quot;);

            var l = new Entities.ExternalLogin
            {
                LoginProvider = login.LoginProvider,
                ProviderKey = login.ProviderKey,
                User = u
            };
            u.Logins.Add(l);

            _unitOfWork.UserRepository.Update(u);
            return _unitOfWork.SaveChangesAsync();
        }

        public Task&amp;lt;IdentityUser&amp;gt; FindAsync(UserLoginInfo login)
        {
            if (login == null)
                throw new ArgumentNullException(&quot;login&quot;);

            var identityUser = default(IdentityUser);

            var l = _unitOfWork.ExternalLoginRepository.GetByProviderAndKey(login.LoginProvider, login.ProviderKey);
            if (l != null)
                identityUser = getIdentityUser(l.User);

            return Task.FromResult&amp;lt;IdentityUser&amp;gt;(identityUser);
        }

        public Task&amp;lt;IList&amp;lt;UserLoginInfo&amp;gt;&amp;gt; GetLoginsAsync(IdentityUser user)
        {
            if (user == null)
                throw new ArgumentNullException(&quot;user&quot;);

            var u = _unitOfWork.UserRepository.FindById(user.Id);
            if (u == null)
                throw new ArgumentException(&quot;IdentityUser does not correspond to a User entity.&quot;, &quot;user&quot;);

            return Task.FromResult&amp;lt;IList&amp;lt;UserLoginInfo&amp;gt;&amp;gt;(u.Logins.Select(x =&amp;gt; new UserLoginInfo(x.LoginProvider, x.ProviderKey)).ToList());
        }

        public Task RemoveLoginAsync(IdentityUser user, UserLoginInfo login)
        {
            if (user == null)
                throw new ArgumentNullException(&quot;user&quot;);
            if (login == null)
                throw new ArgumentNullException(&quot;login&quot;);

            var u = _unitOfWork.UserRepository.FindById(user.Id);
            if (u == null)
                throw new ArgumentException(&quot;IdentityUser does not correspond to a User entity.&quot;, &quot;user&quot;);

            var l = u.Logins.FirstOrDefault(x =&amp;gt; x.LoginProvider == login.LoginProvider &amp;amp;&amp;amp; x.ProviderKey == login.ProviderKey);
            u.Logins.Remove(l);

            _unitOfWork.UserRepository.Update(u);
            return _unitOfWork.SaveChangesAsync();
        }
        #endregion

        #region IUserRoleStore&amp;lt;IdentityUser, Guid&amp;gt; Members
        public Task AddToRoleAsync(IdentityUser user, string roleName)
        {
            if (user == null)
                throw new ArgumentNullException(&quot;user&quot;);
            if (string.IsNullOrWhiteSpace(roleName))
                throw new ArgumentException(&quot;Argument cannot be null, empty, or whitespace: roleName.&quot;);

            var u = _unitOfWork.UserRepository.FindById(user.Id);
            if (u == null)
                throw new ArgumentException(&quot;IdentityUser does not correspond to a User entity.&quot;, &quot;user&quot;);
            var r = _unitOfWork.RoleRepository.FindByName(roleName);
            if (r == null)
                throw new ArgumentException(&quot;roleName does not correspond to a Role entity.&quot;, &quot;roleName&quot;);

            u.Roles.Add(r);
            _unitOfWork.UserRepository.Update(u);

            return _unitOfWork.SaveChangesAsync();
        }

        public Task&amp;lt;IList&amp;lt;string&amp;gt;&amp;gt; GetRolesAsync(IdentityUser user)
        {
            if (user == null)
                throw new ArgumentNullException(&quot;user&quot;);

            var u = _unitOfWork.UserRepository.FindById(user.Id);
            if (u == null)
                throw new ArgumentException(&quot;IdentityUser does not correspond to a User entity.&quot;, &quot;user&quot;);

            return Task.FromResult&amp;lt;IList&amp;lt;string&amp;gt;&amp;gt;(u.Roles.Select(x =&amp;gt; x.Name).ToList());
        }

        public Task&amp;lt;bool&amp;gt; IsInRoleAsync(IdentityUser user, string roleName)
        {
            if (user == null)
                throw new ArgumentNullException(&quot;user&quot;);
            if (string.IsNullOrWhiteSpace(roleName))
                throw new ArgumentException(&quot;Argument cannot be null, empty, or whitespace: role.&quot;);

            var u = _unitOfWork.UserRepository.FindById(user.Id);
            if (u == null)
                throw new ArgumentException(&quot;IdentityUser does not correspond to a User entity.&quot;, &quot;user&quot;);

            return Task.FromResult&amp;lt;bool&amp;gt;(u.Roles.Any(x =&amp;gt; x.Name == roleName));
        }

        public Task RemoveFromRoleAsync(IdentityUser user, string roleName)
        {
            if (user == null)
                throw new ArgumentNullException(&quot;user&quot;);
            if (string.IsNullOrWhiteSpace(roleName))
                throw new ArgumentException(&quot;Argument cannot be null, empty, or whitespace: role.&quot;);

            var u = _unitOfWork.UserRepository.FindById(user.Id);
            if (u == null)
                throw new ArgumentException(&quot;IdentityUser does not correspond to a User entity.&quot;, &quot;user&quot;);

            var r = u.Roles.FirstOrDefault(x =&amp;gt; x.Name == roleName);
            u.Roles.Remove(r);

            _unitOfWork.UserRepository.Update(u);
            return _unitOfWork.SaveChangesAsync();
        }
        #endregion

        #region IUserPasswordStore&amp;lt;IdentityUser, Guid&amp;gt; Members
        public Task&amp;lt;string&amp;gt; GetPasswordHashAsync(IdentityUser user)
        {
            if (user == null)
                throw new ArgumentNullException(&quot;user&quot;);
            return Task.FromResult&amp;lt;string&amp;gt;(user.PasswordHash);
        }

        public Task&amp;lt;bool&amp;gt; HasPasswordAsync(IdentityUser user)
        {
            if (user == null)
                throw new ArgumentNullException(&quot;user&quot;);
            return Task.FromResult&amp;lt;bool&amp;gt;(!string.IsNullOrWhiteSpace(user.PasswordHash));
        }

        public Task SetPasswordHashAsync(IdentityUser user, string passwordHash)
        {
            user.PasswordHash = passwordHash;
            return Task.FromResult(0);
        }
        #endregion

        #region IUserSecurityStampStore&amp;lt;IdentityUser, Guid&amp;gt; Members
        public Task&amp;lt;string&amp;gt; GetSecurityStampAsync(IdentityUser user)
        {
            if (user == null)
                throw new ArgumentNullException(&quot;user&quot;);
            return Task.FromResult&amp;lt;string&amp;gt;(user.SecurityStamp);
        }

        public Task SetSecurityStampAsync(IdentityUser user, string stamp)
        {
            user.SecurityStamp = stamp;
            return Task.FromResult(0);
        }
        #endregion

        #region Private Methods
        private Entities.User getUser(IdentityUser identityUser)
        {
            if (identityUser == null)
                return null;

            var user = new Entities.User();
            populateUser(user, identityUser);

            return user;
        }

        private void populateUser(Entities.User user, IdentityUser identityUser)
        {
            user.UserId = identityUser.Id;
            user.UserName = identityUser.UserName;
            user.PasswordHash = identityUser.PasswordHash;
            user.SecurityStamp = identityUser.SecurityStamp;
        }

        private IdentityUser getIdentityUser(Entities.User user)
        {
            if (user == null)
                return null;

            var identityUser = new IdentityUser();
            populateIdentityUser(identityUser, user);

            return identityUser;
        }

        private void populateIdentityUser(IdentityUser identityUser, Entities.User user)
        {
            identityUser.Id = user.UserId;
            identityUser.UserName = user.UserName;
            identityUser.PasswordHash = user.PasswordHash;
            identityUser.SecurityStamp = user.SecurityStamp;
        }
        #endregion
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h6 id=&quot;rolestorecs&quot;&gt;RoleStore.cs&lt;/h6&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;using Microsoft.AspNet.Identity;
using Mvc5IdentityExample.Domain;
using Mvc5IdentityExample.Domain.Entities;
using System;
using System.Linq;
using System.Threading.Tasks;

namespace Mvc5IdentityExample.Web.Identity
{
    public class RoleStore : IRoleStore&amp;lt;IdentityRole, Guid&amp;gt;, IQueryableRoleStore&amp;lt;IdentityRole, Guid&amp;gt;, IDisposable
    {
        private readonly IUnitOfWork _unitOfWork;

        public RoleStore(IUnitOfWork unitOfWork)
        {
            _unitOfWork = unitOfWork;
        }

        #region IRoleStore&amp;lt;IdentityRole, Guid&amp;gt; Members
        public System.Threading.Tasks.Task CreateAsync(IdentityRole role)
        {
            if (role == null)
                throw new ArgumentNullException(&quot;role&quot;);

            var r = getRole(role);

            _unitOfWork.RoleRepository.Add(r);
            return _unitOfWork.SaveChangesAsync();
        }

        public System.Threading.Tasks.Task DeleteAsync(IdentityRole role)
        {
            if (role == null)
                throw new ArgumentNullException(&quot;role&quot;);

            var r = getRole(role);

            _unitOfWork.RoleRepository.Remove(r);
            return _unitOfWork.SaveChangesAsync();
        }

        public System.Threading.Tasks.Task&amp;lt;IdentityRole&amp;gt; FindByIdAsync(Guid roleId)
        {
            var role = _unitOfWork.RoleRepository.FindById(roleId);
            return Task.FromResult&amp;lt;IdentityRole&amp;gt;(getIdentityRole(role));
        }

        public System.Threading.Tasks.Task&amp;lt;IdentityRole&amp;gt; FindByNameAsync(string roleName)
        {
            var role = _unitOfWork.RoleRepository.FindByName(roleName);
            return Task.FromResult&amp;lt;IdentityRole&amp;gt;(getIdentityRole(role));
        }

        public System.Threading.Tasks.Task UpdateAsync(IdentityRole role)
        {
            if (role == null)
                throw new ArgumentNullException(&quot;role&quot;);
            var r = getRole(role);
            _unitOfWork.RoleRepository.Update(r);
            return _unitOfWork.SaveChangesAsync();
        }
        #endregion

        #region IDisposable Members
        public void Dispose()
        {
            // Dispose does nothing since we want Unity to manage the lifecycle of our Unit of Work
        }
        #endregion

        #region IQueryableRoleStore&amp;lt;IdentityRole, Guid&amp;gt; Members
        public IQueryable&amp;lt;IdentityRole&amp;gt; Roles
        {
            get
            {
                return _unitOfWork.RoleRepository
                    .GetAll()
                    .Select(x =&amp;gt; getIdentityRole(x))
                    .AsQueryable();
            }
        }
        #endregion

        #region Private Methods
        private Role getRole(IdentityRole identityRole)
        {
            if (identityRole == null)
                return null;
            return new Role
            {
                RoleId = identityRole.Id,
                Name = identityRole.Name
            };
        }

        private IdentityRole getIdentityRole(Role role)
        {
            if (role == null)
                return null;
            return new IdentityRole
            {
                Id = role.RoleId,
                Name = role.Name
            };
        }
        #endregion
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;account-controller&quot;&gt;Account Controller&lt;/h3&gt;

&lt;p&gt;Next, we’re going to move on to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AccountController&lt;/code&gt; that got generated when we created the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MvcIdentityExample.Web&lt;/code&gt; project. There are several changes that we’re going to have to make to get it to work with our new ASP.NET Identity classes. First of all, we need to change the way it gets its &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UserManager&lt;/code&gt; dependency. Since we’ll be letting Unity take care of all of that for us, we’ll just use some simple constructor injection:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;private readonly UserManager&amp;lt;IdentityUser, Guid&amp;gt; _userManager;

public AccountController(UserManager&amp;lt;IdentityUser, Guid&amp;gt; userManager)
{
    _userManager = userManager;
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Next, since 1) I’ve chosen to use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Guid&lt;/code&gt;s for my &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;User&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Role&lt;/code&gt; Ids, and 2) the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AccountController&lt;/code&gt; class generated expecting a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;string&lt;/code&gt;, we need to write a new private method to get a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Guid&lt;/code&gt; from the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;string&lt;/code&gt;s in a safe way:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;private Guid getGuid(string value)
{
    var result = default(Guid);
    Guid.TryParse(value, out result);
    return result;
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Finally, we need to replace some not-working code with our new working code. Let’s go through the methods and do the following:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Replace all occurrences of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;User.Identity.GetUserId()&lt;/code&gt; with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;getGuid(User.Identity.GetUserId())&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;Replace all occurrences of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ApplicationUser&lt;/code&gt; with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IdentityUser&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Replace all occurrences of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UserManager&lt;/code&gt; with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_userManager&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In the end, your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AccountController&lt;/code&gt; class should look like this:&lt;/p&gt;

&lt;h6 id=&quot;accountcontrollercs&quot;&gt;AccountController.cs&lt;/h6&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;using Microsoft.AspNet.Identity;
using Microsoft.Owin.Security;
using Mvc5IdentityExample.Web.Identity;
using Mvc5IdentityExample.Web.Models;
using System;
using System.Threading.Tasks;
using System.Web;
using System.Web.Mvc;

namespace Mvc5IdentityExample.Web.Controllers
{
    [Authorize]
    public class AccountController : Controller
    {
        private readonly UserManager&amp;lt;IdentityUser, Guid&amp;gt; _userManager;
        
        public AccountController(UserManager&amp;lt;IdentityUser, Guid&amp;gt; userManager)
        {
            _userManager = userManager;
        }

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

        //
        // POST: /Account/Login
        [HttpPost]
        [AllowAnonymous]
        [ValidateAntiForgeryToken]
        public async Task&amp;lt;ActionResult&amp;gt; Login(LoginViewModel model, string returnUrl)
        {
            if (ModelState.IsValid)
            {
                var user = await _userManager.FindAsync(model.UserName, model.Password);
                if (user != null)
                {
                    await SignInAsync(user, model.RememberMe);
                    return RedirectToLocal(returnUrl);
                }
                else
                {
                    ModelState.AddModelError(&quot;&quot;, &quot;Invalid username or password.&quot;);
                }
            }

            // If we got this far, something failed, redisplay form
            return View(model);
        }

        //
        // GET: /Account/Register
        [AllowAnonymous]
        public ActionResult Register()
        {
            return View();
        }

        //
        // POST: /Account/Register
        [HttpPost]
        [AllowAnonymous]
        [ValidateAntiForgeryToken]
        public async Task&amp;lt;ActionResult&amp;gt; Register(RegisterViewModel model)
        {
            if (ModelState.IsValid)
            {
                var user = new IdentityUser() { UserName = model.UserName };
                var result = await _userManager.CreateAsync(user, model.Password);
                if (result.Succeeded)
                {
                    await SignInAsync(user, isPersistent: false);
                    return RedirectToAction(&quot;Index&quot;, &quot;Home&quot;);
                }
                else
                {
                    AddErrors(result);
                }
            }

            // If we got this far, something failed, redisplay form
            return View(model);
        }

        //
        // POST: /Account/Disassociate
        [HttpPost]
        [ValidateAntiForgeryToken]
        public async Task&amp;lt;ActionResult&amp;gt; Disassociate(string loginProvider, string providerKey)
        {
            ManageMessageId? message = null;
            IdentityResult result = await _userManager.RemoveLoginAsync(getGuid(User.Identity.GetUserId()), new UserLoginInfo(loginProvider, providerKey));
            if (result.Succeeded)
            {
                message = ManageMessageId.RemoveLoginSuccess;
            }
            else
            {
                message = ManageMessageId.Error;
            }
            return RedirectToAction(&quot;Manage&quot;, new { Message = message });
        }

        //
        // GET: /Account/Manage
        public ActionResult Manage(ManageMessageId? message)
        {
            ViewBag.StatusMessage =
                message == ManageMessageId.ChangePasswordSuccess ? &quot;Your password has been changed.&quot;
                : message == ManageMessageId.SetPasswordSuccess ? &quot;Your password has been set.&quot;
                : message == ManageMessageId.RemoveLoginSuccess ? &quot;The external login was removed.&quot;
                : message == ManageMessageId.Error ? &quot;An error has occurred.&quot;
                : &quot;&quot;;
            ViewBag.HasLocalPassword = HasPassword();
            ViewBag.ReturnUrl = Url.Action(&quot;Manage&quot;);
            return View();
        }

        //
        // POST: /Account/Manage
        [HttpPost]
        [ValidateAntiForgeryToken]
        public async Task&amp;lt;ActionResult&amp;gt; Manage(ManageUserViewModel model)
        {
            bool hasPassword = HasPassword();
            ViewBag.HasLocalPassword = hasPassword;
            ViewBag.ReturnUrl = Url.Action(&quot;Manage&quot;);
            if (hasPassword)
            {
                if (ModelState.IsValid)
                {
                    IdentityResult result = await _userManager.ChangePasswordAsync(getGuid(User.Identity.GetUserId()), model.OldPassword, model.NewPassword);
                    if (result.Succeeded)
                    {
                        return RedirectToAction(&quot;Manage&quot;, new { Message = ManageMessageId.ChangePasswordSuccess });
                    }
                    else
                    {
                        AddErrors(result);
                    }
                }
            }
            else
            {
                // User does not have a password so remove any validation errors caused by a missing OldPassword field
                ModelState state = ModelState[&quot;OldPassword&quot;];
                if (state != null)
                {
                    state.Errors.Clear();
                }

                if (ModelState.IsValid)
                {
                    IdentityResult result = await _userManager.AddPasswordAsync(getGuid(User.Identity.GetUserId()), model.NewPassword);
                    if (result.Succeeded)
                    {
                        return RedirectToAction(&quot;Manage&quot;, new { Message = ManageMessageId.SetPasswordSuccess });
                    }
                    else
                    {
                        AddErrors(result);
                    }
                }
            }

            // If we got this far, something failed, redisplay form
            return View(model);
        }

        //
        // POST: /Account/ExternalLogin
        [HttpPost]
        [AllowAnonymous]
        [ValidateAntiForgeryToken]
        public ActionResult ExternalLogin(string provider, string returnUrl)
        {
            // Request a redirect to the external login provider
            return new ChallengeResult(provider, Url.Action(&quot;ExternalLoginCallback&quot;, &quot;Account&quot;, new { ReturnUrl = returnUrl }));
        }

        //
        // GET: /Account/ExternalLoginCallback
        [AllowAnonymous]
        public async Task&amp;lt;ActionResult&amp;gt; ExternalLoginCallback(string returnUrl)
        {
            var loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync();
            if (loginInfo == null)
            {
                return RedirectToAction(&quot;Login&quot;);
            }

            // Sign in the user with this external login provider if the user already has a login
            var user = await _userManager.FindAsync(loginInfo.Login);
            if (user != null)
            {
                await SignInAsync(user, isPersistent: false);
                return RedirectToLocal(returnUrl);
            }
            else
            {
                // If the user does not have an account, then prompt the user to create an account
                ViewBag.ReturnUrl = returnUrl;
                ViewBag.LoginProvider = loginInfo.Login.LoginProvider;
                return View(&quot;ExternalLoginConfirmation&quot;, new ExternalLoginConfirmationViewModel { UserName = loginInfo.DefaultUserName });
            }
        }

        //
        // POST: /Account/LinkLogin
        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult LinkLogin(string provider)
        {
            // Request a redirect to the external login provider to link a login for the current user
            return new ChallengeResult(provider, Url.Action(&quot;LinkLoginCallback&quot;, &quot;Account&quot;), User.Identity.GetUserId());
        }

        //
        // GET: /Account/LinkLoginCallback
        public async Task&amp;lt;ActionResult&amp;gt; LinkLoginCallback()
        {
            var loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync(XsrfKey, User.Identity.GetUserId());
            if (loginInfo == null)
            {
                return RedirectToAction(&quot;Manage&quot;, new { Message = ManageMessageId.Error });
            }
            var result = await _userManager.AddLoginAsync(getGuid(User.Identity.GetUserId()), loginInfo.Login);
            if (result.Succeeded)
            {
                return RedirectToAction(&quot;Manage&quot;);
            }
            return RedirectToAction(&quot;Manage&quot;, new { Message = ManageMessageId.Error });
        }

        //
        // POST: /Account/ExternalLoginConfirmation
        [HttpPost]
        [AllowAnonymous]
        [ValidateAntiForgeryToken]
        public async Task&amp;lt;ActionResult&amp;gt; ExternalLoginConfirmation(ExternalLoginConfirmationViewModel model, string returnUrl)
        {
            if (User.Identity.IsAuthenticated)
            {
                return RedirectToAction(&quot;Manage&quot;);
            }

            if (ModelState.IsValid)
            {
                // Get the information about the user from the external login provider
                var info = await AuthenticationManager.GetExternalLoginInfoAsync();
                if (info == null)
                {
                    return View(&quot;ExternalLoginFailure&quot;);
                }
                var user = new IdentityUser() { UserName = model.UserName };
                var result = await _userManager.CreateAsync(user);
                if (result.Succeeded)
                {
                    result = await _userManager.AddLoginAsync(user.Id, info.Login);
                    if (result.Succeeded)
                    {
                        await SignInAsync(user, isPersistent: false);
                        return RedirectToLocal(returnUrl);
                    }
                }
                AddErrors(result);
            }

            ViewBag.ReturnUrl = returnUrl;
            return View(model);
        }

        //
        // POST: /Account/LogOff
        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult LogOff()
        {
            AuthenticationManager.SignOut();
            return RedirectToAction(&quot;Index&quot;, &quot;Home&quot;);
        }

        //
        // GET: /Account/ExternalLoginFailure
        [AllowAnonymous]
        public ActionResult ExternalLoginFailure()
        {
            return View();
        }

        [ChildActionOnly]
        public ActionResult RemoveAccountList()
        {
            var linkedAccounts = _userManager.GetLogins(getGuid(User.Identity.GetUserId()));
            ViewBag.ShowRemoveButton = HasPassword() || linkedAccounts.Count &amp;gt; 1;
            return (ActionResult)PartialView(&quot;_RemoveAccountPartial&quot;, linkedAccounts);
        }

        protected override void Dispose(bool disposing)
        {
            if (disposing &amp;amp;&amp;amp; _userManager != null)
            {
                _userManager.Dispose();
            }
            base.Dispose(disposing);
        }

        #region Helpers
        // Used for XSRF protection when adding external logins
        private const string XsrfKey = &quot;XsrfId&quot;;

        private IAuthenticationManager AuthenticationManager
        {
            get
            {
                return HttpContext.GetOwinContext().Authentication;
            }
        }

        private async Task SignInAsync(IdentityUser user, bool isPersistent)
        {
            AuthenticationManager.SignOut(DefaultAuthenticationTypes.ExternalCookie);
            var identity = await _userManager.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie);
            AuthenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = isPersistent }, identity);
        }

        private void AddErrors(IdentityResult result)
        {
            foreach (var error in result.Errors)
            {
                ModelState.AddModelError(&quot;&quot;, error);
            }
        }

        private bool HasPassword()
        {
            var user = _userManager.FindById(getGuid(User.Identity.GetUserId()));
            if (user != null)
            {
                return user.PasswordHash != null;
            }
            return false;
        }

        public enum ManageMessageId
        {
            ChangePasswordSuccess,
            SetPasswordSuccess,
            RemoveLoginSuccess,
            Error
        }

        private ActionResult RedirectToLocal(string returnUrl)
        {
            if (Url.IsLocalUrl(returnUrl))
            {
                return Redirect(returnUrl);
            }
            else
            {
                return RedirectToAction(&quot;Index&quot;, &quot;Home&quot;);
            }
        }

        private class ChallengeResult : HttpUnauthorizedResult
        {
            public ChallengeResult(string provider, string redirectUri) : this(provider, redirectUri, null)
            {
            }

            public ChallengeResult(string provider, string redirectUri, string userId)
            {
                LoginProvider = provider;
                RedirectUri = redirectUri;
                UserId = userId;
            }

            public string LoginProvider { get; set; }
            public string RedirectUri { get; set; }
            public string UserId { get; set; }

            public override void ExecuteResult(ControllerContext context)
            {
                var properties = new AuthenticationProperties() { RedirectUri = RedirectUri };
                if (UserId != null)
                {
                    properties.Dictionary[XsrfKey] = UserId;
                }
                context.HttpContext.GetOwinContext().Authentication.Challenge(properties, LoginProvider);
            }
        }

        private Guid getGuid(string value)
        {
            var result = default(Guid);
            Guid.TryParse(value, out result);
            return result;
        }
        #endregion
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;unity&quot;&gt;Unity&lt;/h3&gt;

&lt;p&gt;I mentioned that we’ll be letting Unity handle the lifetimes for our dependencies. So we need to add Unity to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MvcIdentityExample.Web&lt;/code&gt; project. The easiest way to do this is to install the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Unity.MVC5&lt;/code&gt; package by DevTrends. It adds all the code necessary to integrate Unity with MVC5. So, open up the Package Manager Console and run the following command:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Install-Package Unity.Mvc5
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;After it’s done installing, you’ll notice it added a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UnityConfig.cs&lt;/code&gt; file in your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;App_Start&lt;/code&gt; folder. This is where we are going to register the dependencies we want Unity to inject (and manage the lifetimes of). Open it up, and make sure it looks like this:&lt;/p&gt;

&lt;h6 id=&quot;unityconfigcs&quot;&gt;UnityConfig.cs&lt;/h6&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;using Microsoft.AspNet.Identity;
using Microsoft.Practices.Unity;
using Mvc5IdentityExample.Data.EntityFramework;
using Mvc5IdentityExample.Domain;
using Mvc5IdentityExample.Web.Identity;
using System;
using System.Web.Mvc;
using Unity.Mvc5;

namespace Mvc5IdentityExample.Web
{
    public static class UnityConfig
    {
        public static void RegisterComponents()
        {
            var container = new UnityContainer();

            container.RegisterType&amp;lt;IUnitOfWork, UnitOfWork&amp;gt;(new HierarchicalLifetimeManager(), new InjectionConstructor(&quot;Mvc5IdentityExample&quot;));
            container.RegisterType&amp;lt;IUserStore&amp;lt;IdentityUser, Guid&amp;gt;, UserStore&amp;gt;(new TransientLifetimeManager());
            container.RegisterType&amp;lt;RoleStore&amp;gt;(new TransientLifetimeManager());
            
            DependencyResolver.SetResolver(new UnityDependencyResolver(container));
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then, just add the following line to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Application_Start&lt;/code&gt; method in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Global.asax.cs&lt;/code&gt; (right after the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;BundleConfig&lt;/code&gt; is fine):&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;UnityConfig.RegisterComponents();
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;webconfig&quot;&gt;Web.Config&lt;/h3&gt;

&lt;p&gt;The last thing we need to do is change our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;web.config&lt;/code&gt; so that it uses the connection string for the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Mvc5IdentityExample&lt;/code&gt; database. Replace this line:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;lt;add name=&quot;DefaultConnection&quot; connectionString=&quot;Data Source=(LocalDb)\v11.0;AttachDbFilename=|DataDirectory|\aspnet-Mvc5IdentityExample.Web-20150128011936.mdf;Initial Catalog=aspnet-Mvc5IdentityExample.Web-20150128011936;Integrated Security=True&quot; providerName=&quot;System.Data.SqlClient&quot; /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;with this line:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;lt;add name=&quot;Mvc5IdentityExample&quot; connectionString=&quot;Server=(local);Database=Mvc5IdentityExample;User Id=Mvc5IdentityExampleUser;Password=Password123&quot; providerName=&quot;System.Data.SqlClient&quot; /&amp;gt;    
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;and-awaaaaaay-we-go&quot;&gt;And Awaaaaaay We Go!&lt;/h3&gt;

&lt;p&gt;And now, if we’ve done everything right, we should be able to run it, and it should let you do everything that the out-of-the-box Entity Framework ASP.NET Identity would. Go ahead, register. Log in, change your password, etc. I’ll wait.&lt;/p&gt;

&lt;p&gt;If it worked right away, good for you. If not, well I understand that following along with tutorials has its difficulties. That’s why all my source code for this series of posts is available at on my GitHub: &lt;a href=&quot;https://github.com/timschreiber/Mvc5IdentityExample&quot;&gt;https://github.com/timschreiber/Mvc5IdentityExample&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;putting-it-all-together&quot;&gt;Putting It All Together&lt;/h3&gt;

&lt;p&gt;So here’s what we’ve accomplished throughout this series of posts:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;I’ve shown you how the out-of-the-box Entity Framework implementation of ASP.NET Identity forces you into some bad practices and anti-patterns and presented a plan to do it the “right” way with good design principles, true persistence-ignorance, and some important design patterns including: the Repository pattern (with generic Repositories), the Unit of Work pattern, Dependency Injection.&lt;/li&gt;
  &lt;li&gt;Together, we set up the Visual Studio solution and coded the Domain Layer, including: the Entities, the Repository interfaces, and the Unit of Work interface.&lt;/li&gt;
  &lt;li&gt;We coded an Entity Framework/SQL Server Data Layer that implements the Repository and Unit of Work interfaces we defined in the Domain Layer.&lt;/li&gt;
  &lt;li&gt;We added the required ASP.NET Identity classes to our MVC5 project, hooked them in to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AccountController&lt;/code&gt;, wired them together with Unity, and did some configuration until we had a working MVC5 web application with all the functionality it would have had if we’d used the default Entity Framework crap.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I hope that, as you’ve taken this journey with me, you8’ve gained a deeper understanding of how ASP.NET Identity works and an appreciation for some of the principles and design patterns we’ve used.&lt;/p&gt;

&lt;h3 id=&quot;whats-next&quot;&gt;What’s Next?&lt;/h3&gt;

&lt;p&gt;Technically, we’re not &lt;em&gt;quite&lt;/em&gt; done yet. I still have to prove this design is truly persistence-ignorant. In a future bonus part, I’ll show you how to write a new Data Layer using NHibernate instead of Entity Framework, and how easy it will be to plug in.&lt;/p&gt;

&lt;p&gt;Until then, happy coding!&lt;/p&gt;

</content>
  </entry>
  
  <entry>
    <id>http://timschreiber.com/2015/01/26/persistence-ignorant-asp-net-identity-with-patterns-part-3/</id>
    <link type="text/html" rel="alternate" href="http://timschreiber.com//2015/01/26/persistence-ignorant-asp-net-identity-with-patterns-part-3/"/>
    <title>Persistence-Ignorant ASP.NET Identity with Patterns (Part 3)</title>
    <updated>2015-01-26T00:00:00+00:00</updated>
    <author>
      <name>Timothy P. Schreiber</name>
      <uri>http://timschreiber.com/</uri>
    </author>
    <content type="html">&lt;h4 style=&quot;color:#e53935;font-style:italic;&quot;&gt;See my updated tutorial for ASP.NET Core Identity &lt;a href=&quot;/2018/05/07/aspnet-core-identity-with-patterns/&quot;&gt;here&lt;/a&gt;.&lt;/h4&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/2015/01/14/persistence-ignorant-asp-net-identity-with-patterns-part-1/&quot;&gt;Part 1&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2015/01/25/persistence-ignorant-asp-net-identity-with-patterns-part-2/&quot;&gt;Part 2&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Part 3&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2015/01/28/persistence-ignorant-asp-net-identity-with-patterns-part-4/&quot;&gt;Part 4&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The source code for this series of posts is available at on my GitHub: &lt;a href=&quot;https://github.com/timschreiber/Mvc5IdentityExample&quot;&gt;https://github.com/timschreiber/Mvc5IdentityExample&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;h6 id=&quot;this-series-of-posts-requires-a-functional-understanding-of-aspnet-identity-2x-if-you-havent-had-at-least-some-kind-of-exposure-this-is-a-good-place-to-start-httpwwwaspnetidentity&quot;&gt;&lt;em&gt;This series of posts requires a functional understanding of ASP.NET Identity 2.x. If you haven’t had at least some kind of exposure, this is a good place to start: &lt;a href=&quot;http://www.asp.net/identity&quot;&gt;http://www.asp.net/identity&lt;/a&gt;.&lt;/em&gt;&lt;/h6&gt;

&lt;p&gt;In &lt;a href=&quot;/2015/01/14/persistence-ignorant-asp-net-identity-with-patterns-part-1/&quot;&gt;Part 1&lt;/a&gt;, I identified some of the shortcomings in the default template for ASP.NET MVC 5 web applications using ASP.NET Identity for &quot;Individual User Accounts&quot; authentication, and then laid out the requirements for a better implementation. In &lt;a href=&quot;/2015/01/25/persistence-ignorant-asp-net-identity-with-patterns-part-2/&quot;&gt;Part 2&lt;/a&gt;, we created the Visual Studio Solution for our ASP.NET Identity Example, broke the out-of-the-box dependencies on Entity Framework, and coded our Domain Layer. In this part, we’ll move on to the Data Layer, in which we’ll code our implementation of the repository and Unit of Work interfaces.&lt;/p&gt;

&lt;p&gt;Let’s jump right in.&lt;/p&gt;

&lt;h3 id=&quot;the-data-layer&quot;&gt;The Data Layer&lt;/h3&gt;

&lt;p&gt;For now, we’ll be using Entity Framework as the persistence mechanism in our Data Layer. Since we’re designing this with persistence-ignorant in mind, however, we could just as easily use NHibernate or plain SQL with little or no modification to our Domain and other layers. The first thing we need to do is add another class library to the solution. I called mine &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Mvc5IdentityExample.Data.EntityFramework&lt;/code&gt;. One the project has been created, let’s add two folders: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Configuration&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Repositories&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Next we’ll need to add a reference to the latest EntityFramework package. To do this, launch the Package Manager Console and run the following command:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Install-Package EntityFramework
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;entity-framework&quot;&gt;Entity Framework&lt;/h4&gt;

&lt;h5 id=&quot;entity-configuration&quot;&gt;&lt;strong&gt;Entity Configuration&lt;/strong&gt;&lt;/h5&gt;

&lt;p&gt;The first pieces of code this we’re going to write in this layer are the mapping classes that tell Entity Framework which entities map to which database tables, which properties map to which columns, and how all the relationships work. A lot of people like to rely on configuration by convention, which is fine, but I prefer to see more of the mappings in my code – call it a sort of self-documentation, if you will. Since these configurations don’t need to be visible outside the Data Layer, we can make them &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;internal&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We have four entities, so we’ll need four configuration classes:&lt;/p&gt;

&lt;h6 id=&quot;claimconfigurationcs&quot;&gt;ClaimConfiguration.cs&lt;/h6&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;using Mvc5IdentityExample.Domain.Entities;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity.ModelConfiguration;

namespace Mvc5IdentityExample.Data.EntityFramework.Configuration
{
    internal class ClaimConfiguration : EntityTypeConfiguration&amp;lt;Claim&amp;gt;
    {
        internal ClaimConfiguration()
        {
            ToTable(&quot;Claim&quot;);

            HasKey(x =&amp;gt; x.ClaimId)
                .Property(x =&amp;gt; x.ClaimId)
                .HasColumnName(&quot;ClaimId&quot;)
                .HasColumnType(&quot;int&quot;)
                .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity)
                .IsRequired();

            Property(x =&amp;gt; x.UserId)
                .HasColumnName(&quot;UserId&quot;)
                .HasColumnType(&quot;uniqueidentifier&quot;)
                .IsRequired();

            Property(x =&amp;gt; x.ClaimType)
                .HasColumnName(&quot;ClaimType&quot;)
                .HasColumnType(&quot;nvarchar&quot;)
                .IsMaxLength()
                .IsOptional();

            Property(x =&amp;gt; x.ClaimValue)
                .HasColumnName(&quot;ClaimValue&quot;)
                .HasColumnType(&quot;nvarchar&quot;)
                .IsMaxLength()
                .IsOptional();

            HasRequired(x =&amp;gt; x.User)
                .WithMany(x =&amp;gt; x.Claims)
                .HasForeignKey(x =&amp;gt; x.UserId);
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h6 id=&quot;externalloginconfigurationcs&quot;&gt;ExternalLoginConfiguration.cs&lt;/h6&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;using Mvc5IdentityExample.Domain.Entities;
using System.Data.Entity.ModelConfiguration;

namespace Mvc5IdentityExample.Data.EntityFramework.Configuration
{
    internal class ExternalLoginConfiguration : EntityTypeConfiguration&amp;lt;ExternalLogin&amp;gt;
    {
        internal ExternalLoginConfiguration()
        {
            ToTable(&quot;ExternalLogin&quot;);

            HasKey(x =&amp;gt; new { x.LoginProvider, x.ProviderKey, x.UserId });

            Property(x =&amp;gt; x.LoginProvider)
                .HasColumnName(&quot;LoginProvider&quot;)
                .HasColumnType(&quot;nvarchar&quot;)
                .HasMaxLength(128)
                .IsRequired();

            Property(x =&amp;gt; x.ProviderKey)
                .HasColumnName(&quot;ProviderKey&quot;)
                .HasColumnType(&quot;nvarchar&quot;)
                .HasMaxLength(128)
                .IsRequired();

            Property(x =&amp;gt; x.UserId)
                .HasColumnName(&quot;UserId&quot;)
                .HasColumnType(&quot;uniqueidentifier&quot;)
                .IsRequired();

            HasRequired(x =&amp;gt; x.User)
                .WithMany(x =&amp;gt; x.Logins)
                .HasForeignKey(x =&amp;gt; x.UserId);
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h6 id=&quot;roleconfigurationcs&quot;&gt;RoleConfiguration.cs&lt;/h6&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;using Mvc5IdentityExample.Domain.Entities;
using System.Data.Entity.ModelConfiguration;

namespace Mvc5IdentityExample.Data.EntityFramework.Configuration
{
    internal class RoleConfiguration : EntityTypeConfiguration&amp;lt;Role&amp;gt;
    {
        internal RoleConfiguration()
        {
            ToTable(&quot;Role&quot;);

            HasKey(x =&amp;gt; x.RoleId)
                .Property(x =&amp;gt; x.RoleId)
                .HasColumnName(&quot;RoleId&quot;)
                .HasColumnType(&quot;uniqueidentifier&quot;)
                .IsRequired();

            Property(x =&amp;gt; x.Name)
                .HasColumnName(&quot;Name&quot;)
                .HasColumnType(&quot;nvarchar&quot;)
                .HasMaxLength(256)
                .IsRequired();

            HasMany(x =&amp;gt; x.Users)
                .WithMany(x =&amp;gt; x.Roles)
                .Map(x =&amp;gt;
                {
                    x.ToTable(&quot;UserRole&quot;);
                    x.MapLeftKey(&quot;RoleId&quot;);
                    x.MapRightKey(&quot;UserId&quot;);
                });
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h6 id=&quot;userconfigurationcs&quot;&gt;UserConfiguration.cs&lt;/h6&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;using Mvc5IdentityExample.Domain.Entities;
using System.Data.Entity.ModelConfiguration;

namespace Mvc5IdentityExample.Data.EntityFramework.Configuration
{
    internal class UserConfiguration : EntityTypeConfiguration&amp;lt;User&amp;gt;
    {
        internal UserConfiguration()
        {
            ToTable(&quot;User&quot;);

            HasKey(x =&amp;gt; x.UserId)
                .Property(x =&amp;gt; x.UserId)
                .HasColumnName(&quot;UserId&quot;)
                .HasColumnType(&quot;uniqueidentifier&quot;)
                .IsRequired();

            Property(x =&amp;gt; x.PasswordHash)
                .HasColumnName(&quot;PasswordHash&quot;)
                .HasColumnType(&quot;nvarchar&quot;)
                .IsMaxLength()
                .IsOptional();

            Property(x =&amp;gt; x.SecurityStamp)
                .HasColumnName(&quot;SecurityStamp&quot;)
                .HasColumnType(&quot;nvarchar&quot;)
                .IsMaxLength()
                .IsOptional();

            Property(x =&amp;gt; x.UserName)
                .HasColumnName(&quot;UserName&quot;)
                .HasColumnType(&quot;nvarchar&quot;)
                .HasMaxLength(256)
                .IsRequired();

            HasMany(x =&amp;gt; x.Roles)
                .WithMany(x =&amp;gt; x.Users)
                .Map(x =&amp;gt;
                {
                    x.ToTable(&quot;UserRole&quot;);
                    x.MapLeftKey(&quot;UserId&quot;);
                    x.MapRightKey(&quot;RoleId&quot;);
                });

            HasMany(x =&amp;gt; x.Claims)
                .WithRequired(x =&amp;gt; x.User)
                .HasForeignKey(x =&amp;gt; x.UserId);

            HasMany(x =&amp;gt; x.Logins)
                .WithRequired(x =&amp;gt; x.User)
                .HasForeignKey(x =&amp;gt; x.UserId);
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h5 id=&quot;dbcontext&quot;&gt;&lt;strong&gt;DbContext&lt;/strong&gt;&lt;/h5&gt;

&lt;p&gt;The next piece in our Entity Framework Data Layer is the DbContext. Nothing out of the ordinary here:&lt;/p&gt;

&lt;h6 id=&quot;applicationdbcontextcs&quot;&gt;ApplicationDbContext.cs&lt;/h6&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;using Mvc5IdentityExample.Data.EntityFramework.Configuration;
using Mvc5IdentityExample.Domain.Entities;
using System.Data.Entity;

namespace Mvc5IdentityExample.Data.EntityFramework
{
    internal class ApplicationDbContext : DbContext
    {
        internal ApplicationDbContext(string nameOrConnectionString)
            : base(nameOrConnectionString)
        {
        }

        internal IDbSet&amp;lt;User&amp;gt; Users { get; set; }
        internal IDbSet&amp;lt;Role&amp;gt; Roles { get; set; }
        internal IDbSet&amp;lt;ExternalLogin&amp;gt; Logins { get; set; }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Configurations.Add(new UserConfiguration());
            modelBuilder.Configurations.Add(new RoleConfiguration());
            modelBuilder.Configurations.Add(new ExternalLoginConfiguration());
            modelBuilder.Configurations.Add(new ClaimConfiguration());
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;repositories&quot;&gt;Repositories&lt;/h4&gt;

&lt;p&gt;With our Entity Framework entity configurations and DbContext out of the way, we move on to implementing the repository interfaces we defined in the Data Layer. As we dive into the code, I want you to notice a couple things about the classes:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;You’ll notice there’s no default constructor. That’s because we’re following the Dependency Injection pattern by providing the repository with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ApplicationDbContext&lt;/code&gt; it needs in the constructor. This ensures that all our repositories will use the same DbContext per transaction scope.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;You’ll notice the constructor is marked with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;internal&lt;/code&gt; access modifier. That’s because the only class that should ever instantiate a repository will be our Unit of Work class. No need to potentially couple Entity Framework to other layers by putting our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ApplicationDbContext&lt;/code&gt; dependency in a public constructor.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You’ll recall from &lt;a href=&quot;/2015/01/25/persistence-ignorant-asp-net-identity-with-patterns-part-2/&quot;&gt;Part 2&lt;/a&gt; that we’re following the generic repository pattern. So, we’ll code the generic repository implementation first, and then move on to the entity-specific repositories. So let’s start by creating the following class in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Repositories&lt;/code&gt; folder:&lt;/p&gt;

&lt;h6 id=&quot;repositorycs&quot;&gt;Repository.cs&lt;/h6&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;using Mvc5IdentityExample.Domain.Repositories;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

namespace Mvc5IdentityExample.Data.EntityFramework.Repositories
{
    internal class Repository&amp;lt;TEntity&amp;gt; : IRepository&amp;lt;TEntity&amp;gt; where TEntity : class
    {
        private ApplicationDbContext _context;
        private DbSet&amp;lt;TEntity&amp;gt; _set;

        internal Repository(ApplicationDbContext context)
        {
            _context = context;
        }

        protected DbSet&amp;lt;TEntity&amp;gt; Set
        {
            get { return _set ?? (_set = _context.Set&amp;lt;TEntity&amp;gt;()); }
        }

        public List&amp;lt;TEntity&amp;gt; GetAll()
        {
            return Set.ToList();
        }

        public Task&amp;lt;List&amp;lt;TEntity&amp;gt;&amp;gt; GetAllAsync()
        {
            return Set.ToListAsync();
        }

        public Task&amp;lt;List&amp;lt;TEntity&amp;gt;&amp;gt; GetAllAsync(CancellationToken cancellationToken)
        {
            return Set.ToListAsync(cancellationToken);
        }

        public List&amp;lt;TEntity&amp;gt; PageAll(int skip, int take)
        {
            return Set.Skip(skip).Take(take).ToList();
        }

        public Task&amp;lt;List&amp;lt;TEntity&amp;gt;&amp;gt; PageAllAsync(int skip, int take)
        {
            return Set.Skip(skip).Take(take).ToListAsync();
        }

        public Task&amp;lt;List&amp;lt;TEntity&amp;gt;&amp;gt; PageAllAsync(CancellationToken cancellationToken, int skip, int take)
        {
            return Set.Skip(skip).Take(take).ToListAsync(cancellationToken);
        }

        public TEntity FindById(object id)
        {
            return Set.Find(id);
        }

        public Task&amp;lt;TEntity&amp;gt; FindByIdAsync(object id)
        {
            return Set.FindAsync(id);
        }

        public Task&amp;lt;TEntity&amp;gt; FindByIdAsync(CancellationToken cancellationToken, object id)
        {
            return Set.FindAsync(cancellationToken, id);
        }

        public void Add(TEntity entity)
        {
            Set.Add(entity);
        }

        public void Update(TEntity entity)
        {
            var entry = _context.Entry(entity);
            if (entry.State == EntityState.Detached)
            {
                Set.Attach(entity);
                entry = _context.Entry(entity);
            }
            entry.State = EntityState.Modified;
        }

        public void Remove(TEntity entity)
        {
            Set.Remove(entity);
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The entity-specific repository classes extend the generic repository class and implement the entity-specific repository interfaces from the Domain Layer. Let’s add the following three classes to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Repositories&lt;/code&gt; folder:&lt;/p&gt;

&lt;h6 id=&quot;externalloginrepositorycs&quot;&gt;ExternalLoginRepository.cs&lt;/h6&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;using Mvc5IdentityExample.Domain.Entities;
using Mvc5IdentityExample.Domain.Repositories;
using System.Data.Entity;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

namespace Mvc5IdentityExample.Data.EntityFramework.Repositories
{
    internal class ExternalLoginRepository : Repository&amp;lt;ExternalLogin&amp;gt;, IExternalLoginRepository
    {
        internal ExternalLoginRepository(ApplicationDbContext context)
            : base(context)
        {
        }

        public ExternalLogin GetByProviderAndKey(string loginProvider, string providerKey)
        {
            return Set.FirstOrDefault(x =&amp;gt; x.LoginProvider == loginProvider &amp;amp;&amp;amp; x.ProviderKey == providerKey);
        }

        public Task&amp;lt;ExternalLogin&amp;gt; GetByProviderAndKeyAsync(string loginProvider, string providerKey)
        {
            return Set.FirstOrDefaultAsync(x =&amp;gt; x.LoginProvider == loginProvider &amp;amp;&amp;amp; x.ProviderKey == providerKey);
        }

        public Task&amp;lt;ExternalLogin&amp;gt; GetByProviderAndKeyAsync(CancellationToken cancellationToken, string loginProvider, string providerKey)
        {
            return Set.FirstOrDefaultAsync(x =&amp;gt; x.LoginProvider == loginProvider &amp;amp;&amp;amp; x.ProviderKey == providerKey, cancellationToken);
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h6 id=&quot;rolerepositorycs&quot;&gt;RoleRepository.cs&lt;/h6&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;using Mvc5IdentityExample.Domain.Entities;
using Mvc5IdentityExample.Domain.Repositories;
using System.Data.Entity;
using System.Linq;
using System.Threading.Tasks;

namespace Mvc5IdentityExample.Data.EntityFramework.Repositories
{
    internal class RoleRepository : Repository&amp;lt;Role&amp;gt;, IRoleRepository
    {
        internal RoleRepository(ApplicationDbContext context)
            : base(context)
        {
        }

        public Role FindByName(string roleName)
        {
            return Set.FirstOrDefault(x =&amp;gt; x.Name == roleName);
        }

        public Task&amp;lt;Role&amp;gt; FindByNameAsync(string roleName)
        {
            return Set.FirstOrDefaultAsync(x =&amp;gt; x.Name == roleName);
        }

        public Task&amp;lt;Role&amp;gt; FindByNameAsync(System.Threading.CancellationToken cancellationToken, string roleName)
        {
            return Set.FirstOrDefaultAsync(x =&amp;gt; x.Name == roleName, cancellationToken);
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h6 id=&quot;userrepositorycs&quot;&gt;UserRepository.cs&lt;/h6&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;using Mvc5IdentityExample.Domain.Entities;
using Mvc5IdentityExample.Domain.Repositories;
using System.Data.Entity;
using System.Linq;
using System.Threading.Tasks;

namespace Mvc5IdentityExample.Data.EntityFramework.Repositories
{
    internal class UserRepository : Repository&amp;lt;User&amp;gt;, IUserRepository
    {
        internal UserRepository(ApplicationDbContext context)
            : base(context)
        {
        }

        public User FindByUserName(string username)
        {
            return Set.FirstOrDefault(x =&amp;gt; x.UserName == username);
        }

        public Task&amp;lt;User&amp;gt; FindByUserNameAsync(string username)
        {
            return Set.FirstOrDefaultAsync(x =&amp;gt; x.UserName == username);
        }

        public Task&amp;lt;User&amp;gt; FindByUserNameAsync(System.Threading.CancellationToken cancellationToken, string username)
        {
            return Set.FirstOrDefaultAsync(x =&amp;gt; x.UserName == username, cancellationToken);
        }
    }
}    
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;unit-of-work&quot;&gt;Unit of Work&lt;/h4&gt;

&lt;p&gt;The last piece of our Data Layer is the Unit of Work implementation. As I pointed out in &lt;a href=&quot;/2015/01/25/persistence-ignorant-asp-net-identity-with-patterns-part-2/&quot;&gt;Part 2&lt;/a&gt;, the Unit of Work pattern does two important things:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Maintains an in-memory collection of changes, and&lt;/li&gt;
  &lt;li&gt;Sends the changes as a single transaction to the data store.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Because we’re using Entity Framework, we can leverage the DbContext to manage the in-memory collection of changes. But since all of our changes are made through the repositories, we need to make sure all the repositories are using the same DbContext. That’s why the Unit of Work interface in the the Domain Layer defines getters for the repositories, as well as methods to commit any changes as a single transaction.&lt;/p&gt;

&lt;p&gt;The Unit of Work class is really the only publicly available class in the Data Layer, because it’s where all the Data Layer rubber meets the road:&lt;/p&gt;

&lt;h6 id=&quot;unitofworkcs&quot;&gt;UnitOfWork.cs&lt;/h6&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;using Mvc5IdentityExample.Data.EntityFramework.Repositories;
using Mvc5IdentityExample.Domain;
using Mvc5IdentityExample.Domain.Repositories;
using System.Threading.Tasks;

namespace Mvc5IdentityExample.Data.EntityFramework
{
    public class UnitOfWork : IUnitOfWork
    {
        #region Fields
        private readonly ApplicationDbContext _context;
        private IExternalLoginRepository _externalLoginRepository;
        private IRoleRepository _roleRepository;
        private IUserRepository _userRepository;
        #endregion

        #region Constructors
        public UnitOfWork(string nameOrConnectionString)
        {
            _context = new ApplicationDbContext(nameOrConnectionString);
        }
        #endregion

        #region IUnitOfWork Members
        public IExternalLoginRepository ExternalLoginRepository
        {
            get { return _externalLoginRepository ?? (_externalLoginRepository = new ExternalLoginRepository(_context)); }
        }

        public IRoleRepository RoleRepository
        {
            get { return _roleRepository ?? (_roleRepository = new RoleRepository(_context)); }
        }

        public IUserRepository UserRepository
        {
            get { return _userRepository ?? (_userRepository = new UserRepository(_context)); }
        }

        public int SaveChanges()
        {
            return _context.SaveChanges();
        }

        public Task&amp;lt;int&amp;gt; SaveChangesAsync()
        {
            return _context.SaveChangesAsync();
        }

        public Task&amp;lt;int&amp;gt; SaveChangesAsync(System.Threading.CancellationToken cancellationToken)
        {
            return _context.SaveChangesAsync(cancellationToken);
        }
        #endregion

        #region IDisposable Members
        public void Dispose()
        {
            _externalLoginRepository = null;
            _roleRepository = null;
            _userRepository = null;
            _context.Dispose();
        }
        #endregion
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;the-database&quot;&gt;The Database&lt;/h4&gt;

&lt;p&gt;I guess I could spend the time to write the code to generate the database from Entity Framework, but that is not the focus of this tutorial. So in order to help things along, I’ve included a SQL script that will create the database, complete with all the tables, the login, the user, and permissions necessary to run the application.&lt;/p&gt;

&lt;h6 id=&quot;createdatabasesql&quot;&gt;CreateDatabase.sql&lt;/h6&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;USE [master]
GO

SET NOCOUNT ON
GO

IF EXISTS (SELECT 1 FROM sys.databases WHERE [Name] = 'Mvc5IdentityExample')
BEGIN
    ALTER DATABASE Mvc5IdentityExample SET SINGLE_USER
    DROP DATABASE Mvc5IdentityExample
END

CREATE DATABASE Mvc5IdentityExample
GO

IF NOT EXISTS (SELECT * FROM sys.server_principals WHERE [name] = 'Mvc5IdentityExampleUser')
BEGIN
    CREATE LOGIN [Mvc5IdentityExampleUser] WITH PASSWORD = N'Password123', DEFAULT_DATABASE = [Mvc5IdentityExample],
        DEFAULT_LANGUAGE=[us_english], CHECK_EXPIRATION = OFF, CHECK_POLICY = OFF
    
    ALTER LOGIN [Mvc5IdentityExampleUser] ENABLE
END
GO

USE [Mvc5IdentityExample]
GO

CREATE USER [Mvc5IdentityExampleUser] FOR LOGIN [Mvc5IdentityExampleUser]
GO

EXEC sp_addrolemember N'db_datareader', N'Mvc5IdentityExampleUser'
EXEC sp_addrolemember N'db_datawriter', N'Mvc5IdentityExampleUser'
GO

/****** Object:  Table [dbo].[Claim]    Script Date: 1/12/2015 11:14:30 PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[Claim](
    [ClaimId] [int] IDENTITY(1,1) NOT NULL,
    [UserId] [uniqueidentifier] NOT NULL,
    [ClaimType] [nvarchar](max) NULL,
    [ClaimValue] [nvarchar](max) NULL,
 CONSTRAINT [PK_dbo.AspNetUserClaims] PRIMARY KEY CLUSTERED 
(
    [ClaimId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]

GO
/****** Object:  Table [dbo].[ExternalLogin]    Script Date: 1/12/2015 11:14:30 PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[ExternalLogin](
    [LoginProvider] [nvarchar](128) NOT NULL,
    [ProviderKey] [nvarchar](128) NOT NULL,
    [UserId] [uniqueidentifier] NOT NULL,
 CONSTRAINT [PK_dbo.AspNetUserLogins] PRIMARY KEY CLUSTERED 
(
    [LoginProvider] ASC,
    [ProviderKey] ASC,
    [UserId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

GO
/****** Object:  Table [dbo].[Role]    Script Date: 1/12/2015 11:14:30 PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[Role](
    [RoleId] [nvarchar](128) NOT NULL,
    [Name] [nvarchar](256) NOT NULL,
 CONSTRAINT [PK_dbo.AspNetRoles] PRIMARY KEY CLUSTERED 
(
    [RoleId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

GO
/****** Object:  Table [dbo].[User]    Script Date: 1/12/2015 11:14:30 PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[User](
    [UserId] [uniqueidentifier] NOT NULL,
    [Email] [nvarchar](256) NULL,
    [EmailConfirmed] [bit] NOT NULL,
    [PasswordHash] [nvarchar](max) NULL,
    [SecurityStamp] [nvarchar](max) NULL,
    [PhoneNumber] [nvarchar](max) NULL,
    [PhoneNumberConfirmed] [bit] NOT NULL,
    [TwoFactorEnabled] [bit] NOT NULL,
    [LockoutEndDateUtc] [datetime] NULL,
    [LockoutEnabled] [bit] NOT NULL,
    [AccessFailedCount] [int] NOT NULL,
    [UserName] [nvarchar](256) NOT NULL,
 CONSTRAINT [PK_dbo.AspNetUsers] PRIMARY KEY CLUSTERED 
(
    [UserId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]

GO
/****** Object:  Table [dbo].[UserRole]    Script Date: 1/12/2015 11:14:30 PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[UserRole](
    [UserId] [uniqueidentifier] NOT NULL,
    [RoleId] [nvarchar](128) NOT NULL,
 CONSTRAINT [PK_dbo.AspNetUserRoles] PRIMARY KEY CLUSTERED 
(
    [UserId] ASC,
    [RoleId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

GO
ALTER TABLE [dbo].[Claim]  WITH CHECK ADD  CONSTRAINT [FK_dbo.AspNetUserClaims_dbo.AspNetUsers_UserId] FOREIGN KEY([UserId])
REFERENCES [dbo].[User] ([UserId])
ON DELETE CASCADE
GO
ALTER TABLE [dbo].[Claim] CHECK CONSTRAINT [FK_dbo.AspNetUserClaims_dbo.AspNetUsers_UserId]
GO
ALTER TABLE [dbo].[ExternalLogin]  WITH CHECK ADD  CONSTRAINT [FK_dbo.AspNetUserLogins_dbo.AspNetUsers_UserId] FOREIGN KEY([UserId])
REFERENCES [dbo].[User] ([UserId])
ON DELETE CASCADE
GO
ALTER TABLE [dbo].[ExternalLogin] CHECK CONSTRAINT [FK_dbo.AspNetUserLogins_dbo.AspNetUsers_UserId]
GO
ALTER TABLE [dbo].[UserRole]  WITH CHECK ADD  CONSTRAINT [FK_dbo.AspNetUserRoles_dbo.AspNetRoles_RoleId] FOREIGN KEY([RoleId])
REFERENCES [dbo].[Role] ([RoleId])
ON DELETE CASCADE
GO
ALTER TABLE [dbo].[UserRole] CHECK CONSTRAINT [FK_dbo.AspNetUserRoles_dbo.AspNetRoles_RoleId]
GO
ALTER TABLE [dbo].[UserRole]  WITH CHECK ADD  CONSTRAINT [FK_dbo.AspNetUserRoles_dbo.AspNetUsers_UserId] FOREIGN KEY([UserId])
REFERENCES [dbo].[User] ([UserId])
ON DELETE CASCADE
GO
ALTER TABLE [dbo].[UserRole] CHECK CONSTRAINT [FK_dbo.AspNetUserRoles_dbo.AspNetUsers_UserId]
GO
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;next-steps&quot;&gt;Next Steps&lt;/h3&gt;

&lt;p&gt;We are so close to having a working application that uses design patterns and better practices for ASP.NET Identity! In this part, we created our Data Layer using Entity Framework. We defined our entity mappings, coded the DbContext, and implemented the interfaces for the repositories and Unit of Work that we defined in the Domain Layer. In Part 4, we’ll move on to the Presentation Layer, where we’ll focus on getting our Domain and Data Layers to work with ASP.NET Identity with custom IdentityUser, IdentityRole, UserStore, and RoleStore classes.&lt;/p&gt;

&lt;p&gt;Until then, happy coding!&lt;/p&gt;

</content>
  </entry>
  
  <entry>
    <id>http://timschreiber.com/2015/01/25/persistence-ignorant-asp-net-identity-with-patterns-part-2/</id>
    <link type="text/html" rel="alternate" href="http://timschreiber.com//2015/01/25/persistence-ignorant-asp-net-identity-with-patterns-part-2/"/>
    <title>Persistence-Ignorant ASP.NET Identity with Patterns (Part 2)</title>
    <updated>2015-01-25T00:00:00+00:00</updated>
    <author>
      <name>Timothy P. Schreiber</name>
      <uri>http://timschreiber.com/</uri>
    </author>
    <content type="html">&lt;h4 style=&quot;color:#e53935;font-style:italic;&quot;&gt;See my updated tutorial for ASP.NET Core Identity &lt;a href=&quot;/2018/05/07/aspnet-core-identity-with-patterns/&quot;&gt;here&lt;/a&gt;.&lt;/h4&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/2015/01/14/persistence-ignorant-asp-net-identity-with-patterns-part-1/&quot;&gt;Part 1&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Part 2&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2015/01/26/persistence-ignorant-asp-net-identity-with-patterns-part-3/&quot;&gt;Part 3&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2015/01/28/persistence-ignorant-asp-net-identity-with-patterns-part-4/&quot;&gt;Part 4&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The source code for this series of posts is available at on my GitHub: &lt;a href=&quot;https://github.com/timschreiber/Mvc5IdentityExample&quot;&gt;https://github.com/timschreiber/Mvc5IdentityExample&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;h6 id=&quot;this-series-of-posts-requires-a-functional-understanding-of-aspnet-identity-2x-if-you-havent-had-at-least-some-kind-of-exposure-this-is-a-good-place-to-start-httpwwwaspnetidentity&quot;&gt;&lt;em&gt;This series of posts requires a functional understanding of ASP.NET Identity 2.x. If you haven’t had at least some kind of exposure, this is a good place to start: &lt;a href=&quot;http://www.asp.net/identity&quot;&gt;http://www.asp.net/identity&lt;/a&gt;.&lt;/em&gt;&lt;/h6&gt;

&lt;p&gt;In &lt;a href=&quot;/2015/01/14/persistence-ignorant-asp-net-identity-with-patterns-part-1/&quot;&gt;Part 1&lt;/a&gt;, I identified some of the shortcomings in the default template for ASP.NET MVC 5 web applications using ASP.NET Identity for &quot;Individual User Accounts&quot; authentication, and then laid out the requirements for a better implementation. In this part, we’ll create the Visual Studio Solution, break the dependencies on Entity Framework, and start coding our Domain Layer.&lt;/p&gt;

&lt;h3 id=&quot;setting-up-the-visual-studio-solution&quot;&gt;Setting Up the Visual Studio Solution&lt;/h3&gt;

&lt;h6 id=&quot;note-this-tutorial-assumes-you-have-enough-experience-with-visual-studio-to-create-projects-add-projects-to-solutions-add-project-references-and-manage-nuget-packages&quot;&gt;&lt;em&gt;Note: This tutorial assumes you have enough experience with Visual Studio to create projects, add projects to solutions, add project references, and manage NuGet packages.&lt;/em&gt;&lt;/h6&gt;

&lt;p&gt;The first thing to do is to launch Visual Studio 2013 and create a new ASP.NET Web Application, using the MVC template with &quot;Individual User Accounts&quot; authentication. I called mine &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Mvc5IdentityExample.Web&lt;/code&gt;, and I named the solution &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Mvc5IdentityExample&lt;/code&gt; (without the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.Web&lt;/code&gt; part). Visual Studio will work on it for a few seconds, and then your new project will be ready. If you were to run it as it is, you would have a functional skeleton website with basic ASP.NET Identity functionality that includes the ability to register, login, etc. But all that out-of-the box functionality comes at the price all that tight coupling and code smell I mentioned in &lt;a href=&quot;http://www.asp.net/identity&quot;&gt;Part 1&lt;/a&gt;.&lt;/p&gt;

&lt;h4 id=&quot;break-the-coupling&quot;&gt;Break the Coupling&lt;/h4&gt;

&lt;p&gt;So instead of running it, let’s go in and delete stuff. The first thing we need to get rid of is the reference to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Microsoft.AspNet.Identity.EntityFramework&lt;/code&gt;. To do this, launch the Package Manager Console and run the following commands:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Uninstall-Package Microsoft.AspNet.Identity.EntityFramework
Uninstall-Package EntityFramework
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You may have to restart Visual Studio to complete the uninstall of EntityFramework. Now, let’s just make sure all our packages are up-to-date. Go back to the Package Manager Console and run the following command:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Update-Package
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Next, lets delete &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IdentityModels.cs&lt;/code&gt; from the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Models&lt;/code&gt; folder. And since we don’t want to couple the Presentation Layer to SQL Server, let’s just go ahead and delete the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;App_Data&lt;/code&gt; folder, too. We’ll worry about persistence in &lt;a href=&quot;/2015/01/26/persistence-ignorant-asp-net-identity-with-patterns-part-3/&quot;&gt;Part 3&lt;/a&gt;. Finally, open the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;web.config&lt;/code&gt; and remove the following:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;lt;configSections&amp;gt;
    &amp;lt;section name=&quot;entityFramework&quot; type=&quot;System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089&quot; requirePermission=&quot;false&quot; /&amp;gt;
&amp;lt;/configSections&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;and&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;lt;entityFramework&amp;gt;
    &amp;lt;defaultConnectionFactory type=&quot;System.Data.Entity.Infrastructure.LocalDbConnectionFactory, EntityFramework&quot;&amp;gt;
        &amp;lt;parameters&amp;gt;
            &amp;lt;parameter value=&quot;v11.0&quot; /&amp;gt;
        &amp;lt;/parameters&amp;gt;
    &amp;lt;/defaultConnectionFactory&amp;gt;
    &amp;lt;providers&amp;gt;
        &amp;lt;provider invariantName=&quot;System.Data.SqlClient&quot; type=&quot;System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer&quot; /&amp;gt;
    &amp;lt;/providers&amp;gt;
&amp;lt;/entityFramework&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;the-domain-layer&quot;&gt;The Domain Layer&lt;/h4&gt;

&lt;p&gt;The next thing we need to do is create a class library for our Domain Layer. Our application domain is made up of our entity classes, interfaces for our repositories, and our Unit of Work interface. Our Data Layer (which we’ll code in &lt;a href=&quot;/2015/01/26/persistence-ignorant-asp-net-identity-with-patterns-part-3/&quot;&gt;Part 3&lt;/a&gt;) will implement these interfaces to allow our entities to be persisted to a data store. The Domain Layer forms the core of our entire, well-layered, loosely-coupled application architecture.&lt;/p&gt;

&lt;p&gt;So let’s add a Class Library project to the solution. I called mine &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Mvc5IdentityExample.Domain&lt;/code&gt;. Once the project has been created, let’s add two folders: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Entities&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Repositories&lt;/code&gt;.&lt;/p&gt;

&lt;h5 id=&quot;entities&quot;&gt;&lt;strong&gt;Entities&lt;/strong&gt;&lt;/h5&gt;
&lt;p&gt;With the project and folders created, we’re ready to start coding. We’ll start with the entity classes. In keeping with our persistence-ignorant design, these are just Plain Old CLR Objects (POCOs). In order to get the same ASP.NET Identity functionality from our application as we would from the out-of-the-box Entity Framework implementation, we’ll create these four classes in our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Entities&lt;/code&gt; folder:&lt;/p&gt;

&lt;h6 id=&quot;usercs&quot;&gt;User.cs&lt;/h6&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;using System;
using System.Collections.Generic;

namespace Mvc5IdentityExample.Domain.Entities
{
    public class User
    {
        #region Fields
        private ICollection&amp;lt;Claim&amp;gt; _claims;
        private ICollection&amp;lt;ExternalLogin&amp;gt; _externalLogins;
        private ICollection&amp;lt;Role&amp;gt; _roles;
        #endregion

        #region Scalar Properties
        public Guid UserId { get; set; }
        public string UserName { get; set; }
        public virtual string PasswordHash { get; set; }
        public virtual string SecurityStamp { get; set; }
        #endregion

        #region Navigation Properties
        public virtual ICollection&amp;lt;Claim&amp;gt; Claims
        {
            get { return _claims ?? (_claims = new List&amp;lt;Claim&amp;gt;()); }
            set { _claims = value; }
        }

        public virtual ICollection&amp;lt;ExternalLogin&amp;gt; Logins
        {
            get
            {
                return _externalLogins ??
                    (_externalLogins = new List&amp;lt;ExternalLogin&amp;gt;());
            }
            set { _externalLogins = value; }
        }

        public virtual ICollection&amp;lt;Role&amp;gt; Roles
        {
            get { return _roles ?? (_roles = new List&amp;lt;Role&amp;gt;()); }
            set { _roles = value; }
        }
        #endregion
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h6 id=&quot;rolecs&quot;&gt;Role.cs&lt;/h6&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;using System;
using System.Collections.Generic;

namespace Mvc5IdentityExample.Domain.Entities
{
    public class Role
    {
        #region Fields
        private ICollection&amp;lt;User&amp;gt; _users;
        #endregion

        #region Scalar Properties
        public Guid RoleId { get; set; }
        public string Name { get; set; }
        #endregion

        #region Navigation Properties
        public ICollection&amp;lt;User&amp;gt; Users
        {
            get { return _users ?? (_users = new List&amp;lt;User&amp;gt;()); }
            set { _users = value; }
        }
        #endregion
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h6 id=&quot;externallogincs&quot;&gt;ExternalLogin.cs&lt;/h6&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;using System;

namespace Mvc5IdentityExample.Domain.Entities
{
    public class ExternalLogin
    {
        private User _user;

        #region Scalar Properties
        public virtual string LoginProvider { get; set; }
        public virtual string ProviderKey { get; set; }
        public virtual Guid UserId { get; set; }
        #endregion

        #region Navigation Properties
        public virtual User User
        {
            get { return _user; }
            set
            {
                _user = value;
                UserId = value.UserId;
            }
        }
        #endregion
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h6 id=&quot;claimcs&quot;&gt;Claim.cs&lt;/h6&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;using System;

namespace Mvc5IdentityExample.Domain.Entities
{
    public class Claim
    {
        private User _user;

        #region Scalar Properties
        public virtual int ClaimId { get; set; }
        public virtual Guid UserId { get; set; }
        public virtual string ClaimType { get; set; }
        public virtual string ClaimValue { get; set; }
        #endregion

        #region Navigation Properties
        public virtual User User
        {
            get { return _user; }
            set
            {
                if (value == null)
                    throw new ArgumentNullException(&quot;value&quot;);
                _user = value;
                UserId = value.UserId;
            }
        }
        #endregion
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h5 id=&quot;repositories&quot;&gt;&lt;strong&gt;Repositories&lt;/strong&gt;&lt;/h5&gt;

&lt;p&gt;Next up are the repositories. We’re not implementing anything here, just creating the interfaces that our future Data Layer will implement. One important pattern we’ll follow is the generic repository pattern. Creating a repository for each entity type would mean we’d have to duplicate a lot of code later on. Using the generic repository pattern eliminates that wasted effort. Our generic repository interface includes all the usual CRUD operations. Create the following interface in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Repositories&lt;/code&gt; folder.&lt;/p&gt;

&lt;h6 id=&quot;irepositorycs&quot;&gt;IRepository.cs&lt;/h6&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;

namespace Mvc5IdentityExample.Domain.Repositories
{
    public interface IRepository&amp;lt;TEntity&amp;gt; where TEntity : class
    {
        List&amp;lt;TEntity&amp;gt; GetAll();
        Task&amp;lt;List&amp;lt;TEntity&amp;gt;&amp;gt; GetAllAsync();
        Task&amp;lt;List&amp;lt;TEntity&amp;gt;&amp;gt; GetAllAsync(CancellationToken cancellationToken);

        List&amp;lt;TEntity&amp;gt; PageAll(int skip, int take);
        Task&amp;lt;List&amp;lt;TEntity&amp;gt;&amp;gt; PageAllAsync(int skip, int take);
        Task&amp;lt;List&amp;lt;TEntity&amp;gt;&amp;gt; PageAllAsync(CancellationToken cancellationToken, int skip, int take);

        TEntity FindById(object id);
        Task&amp;lt;TEntity&amp;gt; FindByIdAsync(object id);
        Task&amp;lt;TEntity&amp;gt; FindByIdAsync(CancellationToken cancellationToken, object id);

        void Add(TEntity entity);
        void Update(TEntity entity);
        void Remove(TEntity entity);
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You may have seen some generic repository examples that expose IQueryables, IEnumerables, and DbSets. Such designs violate the single responsibility principle by leaking persistence logic (like querying) to other layers where is doesn’t belong. Our design keeps those concerns where they belong by returning only generic lists and our entities, so we need to abstract any special-case querying functionality behind entity-specific interfaces that extend &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IRepository&lt;/code&gt;. Our application has three:&lt;/p&gt;

&lt;h6 id=&quot;iuserrepositorycs&quot;&gt;IUserRepository.cs&lt;/h6&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;using Mvc5IdentityExample.Domain.Entities;
using System.Threading;
using System.Threading.Tasks;

namespace Mvc5IdentityExample.Domain.Repositories
{
    public interface IUserRepository : IRepository&amp;lt;User&amp;gt;
    {
        User FindByUserName(string username);
        Task&amp;lt;User&amp;gt; FindByUserNameAsync(string username);
        Task&amp;lt;User&amp;gt; FindByUserNameAsync(CancellationToken cancellationToken, string username);

        User FindByEmail(string email);
        Task&amp;lt;User&amp;gt; FindByEmailAsync(string email);
        Task&amp;lt;User&amp;gt; FindByEmailAsync(CancellationToken cancellationToken, string email);
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h6 id=&quot;irolerepositorycs&quot;&gt;IRoleRepository.cs&lt;/h6&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;using Mvc5IdentityExample.Domain.Entities;
using System.Threading;
using System.Threading.Tasks;

namespace Mvc5IdentityExample.Domain.Repositories
{
    public interface IRoleRepository : IRepository&amp;lt;Role&amp;gt;
    {
        Role FindByName(string roleName);
        Task&amp;lt;Role&amp;gt; FindByNameAsync(string roleName);
        Task&amp;lt;Role&amp;gt; FindByNameAsync(CancellationToken cancellationToken, string roleName);
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h6 id=&quot;iexternalloginrepositorycs&quot;&gt;IExternalLoginRepository.cs&lt;/h6&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;using Mvc5IdentityExample.Domain.Entities;
using System.Threading;
using System.Threading.Tasks;

namespace Mvc5IdentityExample.Domain.Repositories
{
    public interface IExternalLoginRepository : IRepository&amp;lt;ExternalLogin&amp;gt;
    {
        ExternalLogin GetByProviderAndKey(string loginProvider, string providerKey);
        Task&amp;lt;ExternalLogin&amp;gt; GetByProviderAndKeyAsync(string loginProvider, string providerKey);
        Task&amp;lt;ExternalLogin&amp;gt; GetByProviderAndKeyAsync(CancellationToken cancellationToken, string loginProvider, string providerKey);
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h5 id=&quot;unit-of-work&quot;&gt;&lt;strong&gt;Unit of Work&lt;/strong&gt;&lt;/h5&gt;

&lt;p&gt;Another important design pattern we’re following is the Unit of Work pattern, which does two important things:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Maintains an in-memory collection of changes, and&lt;/li&gt;
  &lt;li&gt;Sends the changes as a single transaction to the data store.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IUnitOfWork&lt;/code&gt; interface defines the methods that we’ll implement in the Data Layer in &lt;a href=&quot;/2015/01/26/persistence-ignorant-asp-net-identity-with-patterns-part-3/&quot;&gt;Part 3&lt;/a&gt;. Because we may need to use more than one repository to in a single Unit of Work transaction, it’s important that we design our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IUnitOfWork&lt;/code&gt; interface to ensure all our repositories are using the same transaction during any given transaction scope. So, we’re putting the getters for our repositories in there as well.&lt;/p&gt;

&lt;p&gt;Once again, we’re not including anything that might couple this interface to any specific persistence mechanism. Those are implementation details that we’ll tackle in &lt;a href=&quot;/2015/01/26/persistence-ignorant-asp-net-identity-with-patterns-part-3/&quot;&gt;Part 3&lt;/a&gt;.&lt;/p&gt;

&lt;h6 id=&quot;iunitofworkcs&quot;&gt;IUnitOfWork.cs&lt;/h6&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;using Mvc5IdentityExample.Domain.Repositories;
using System;
using System.Threading;
using System.Threading.Tasks;

namespace Mvc5IdentityExample.Domain
{
    public interface IUnitOfWork : IDisposable
    {
        #region Properties
        IExternalLoginRepository ExternalLoginRepository { get; }
        IRoleRepository RoleRepository { get; }
        IUserRepository UserRepository { get; }
        #endregion

        #region Methods
        int SaveChanges();
        Task&amp;lt;int&amp;gt; SaveChangesAsync();
        Task&amp;lt;int&amp;gt; SaveChangesAsync(CancellationToken cancellationToken);
        #endregion
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;next-steps&quot;&gt;Next Steps&lt;/h3&gt;

&lt;p&gt;In this part, we created the Visual Studio Solution for our ASP.NET Identity Example, broke the out-of-the-box dependencies on Entity Framework, and coded our Domain Layer. In &lt;a href=&quot;/2015/01/26/persistence-ignorant-asp-net-identity-with-patterns-part-3/&quot;&gt;Part 3&lt;/a&gt;, we’ll move on to the Data Layer, in which we’ll code our implementation of the repository and Unit of Work interfaces.&lt;/p&gt;

&lt;p&gt;Until next time, happy coding!&lt;/p&gt;

</content>
  </entry>
  
  <entry>
    <id>http://timschreiber.com/2015/01/19/farewell-wordpress-hello-pretzel/</id>
    <link type="text/html" rel="alternate" href="http://timschreiber.com//2015/01/19/farewell-wordpress-hello-pretzel/"/>
    <title>Farewell WordPress, Hello Pretzel!</title>
    <updated>2015-01-19T00:00:00+00:00</updated>
    <author>
      <name>Timothy P. Schreiber</name>
      <uri>http://timschreiber.com/</uri>
    </author>
    <content type="html">&lt;p&gt;Is dealing with the plug-ins and updates really worth the few pages and handful of posts I have up there? Why does theming have to be such a royal pain in the ass?&lt;/p&gt;

&lt;p&gt;That all changed today.&lt;/p&gt;

&lt;p&gt;A while back, I read about Jeckyll – a blog-aware, static site generation tool that works on GitHub Pages. Jeckyll is a Ruby app that requires a bunch of setup to get running. And if you’re on a Windows computer? It’s even more difficult. The problem is that I’m 99% Windows, so I thought, &quot;Hmm, that would be nice to use WordPress is less of a pain.&quot; Well a couple nights ago, I discovered Pretzel – essentially the functional equivalent of Jeckyll built with .NET. It creates, builds, and helps you test Jeckyll-style sites, which can then be used on GitHub Pages. So I decided to give it a try.&lt;/p&gt;

&lt;p&gt;The first thing I did was to convert the posts I wanted to keep from my WordPress site to MarkDown and added them to my repository. A little bit of Pretzel magic, and a few seconds later, I was viewing my new blog on Pretzel’s built-in web server. A Git commit and push to GitHub, and it was live. The process was simple, yet geeky enough for me to really enjoy as a software developer.&lt;/p&gt;

&lt;p&gt;And the best part is the dead simplicity of the theming. I have to admit, the default design that Pretzel spits out is pretty stupid. So I built my own layouts based on Bootstrap, and everything looks even better than anything I ever had on WordPress.&lt;/p&gt;

&lt;p&gt;You can find Pretzel here: &lt;a href=&quot;https://github.com/Code52/pretzel&quot;&gt;https://github.com/Code52/pretzel&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And the guide I followed to get started is here: &lt;a href=&quot;http://lukencode.com/2012/02/13/using-pretzel-jekyll-to-your-blog-on-github/&quot;&gt;http://lukencode.com/2012/02/13/using-pretzel-jekyll-to-your-blog-on-github/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Happy coding!&lt;/p&gt;

</content>
  </entry>
  
  <entry>
    <id>http://timschreiber.com/2015/01/14/persistence-ignorant-asp-net-identity-with-patterns-part-1/</id>
    <link type="text/html" rel="alternate" href="http://timschreiber.com//2015/01/14/persistence-ignorant-asp-net-identity-with-patterns-part-1/"/>
    <title>Persistence-Ignorant ASP.NET Identity with Patterns (Part 1)</title>
    <updated>2015-01-14T00:00:00+00:00</updated>
    <author>
      <name>Timothy P. Schreiber</name>
      <uri>http://timschreiber.com/</uri>
    </author>
    <content type="html">&lt;h4 style=&quot;color:#e53935;font-style:italic;&quot;&gt;See my updated tutorial for ASP.NET Core Identity &lt;a href=&quot;/2018/05/07/aspnet-core-identity-with-patterns/&quot;&gt;here&lt;/a&gt;.&lt;/h4&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Part 1&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2015/01/25/persistence-ignorant-asp-net-identity-with-patterns-part-2/&quot;&gt;Part 2&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2015/01/26/persistence-ignorant-asp-net-identity-with-patterns-part-3/&quot;&gt;Part 3&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2015/01/28/persistence-ignorant-asp-net-identity-with-patterns-part-4/&quot;&gt;Part 4&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h6 id=&quot;this-series-of-posts-requires-a-functional-understanding-of-aspnet-identity-2x-if-you-havent-had-at-least-some-kind-of-exposure-this-is-a-good-place-to-start-httpwwwaspnetidentity&quot;&gt;&lt;em&gt;This series of posts requires a functional understanding of ASP.NET Identity 2.x. If you haven’t had at least some kind of exposure, this is a good place to start: &lt;a href=&quot;http://www.asp.net/identity&quot;&gt;http://www.asp.net/identity&lt;/a&gt;.&lt;/em&gt;&lt;/h6&gt;

&lt;p&gt;ASP.NET Identity is the successor to ASP.NET Simple Membership, which itself was a short-lived successor to the venerable ASP.NET Membership introduced with .NET 2.0. Microsoft’s &lt;a href=&quot;http://www.asp.net/identity/overview/getting-started/introduction-to-aspnet-identity&quot;&gt;Introduction to ASP.NET Identity&lt;/a&gt; article says this new identity management framework is the result of developer feedback and solves a long list of problems including flexible schema, external logins, testability, and support for different persistence mechanisms — going as far to say they’re &quot;easy to plug in.&quot;&lt;/p&gt;

&lt;h3 id=&quot;the-problem&quot;&gt;The Problem&lt;/h3&gt;

&lt;p&gt;What they neglect to say is all that testability and persistence ignorance flies right out the window when you create a new ASP.NET Web Application using the MVC template and &quot;Individual User Accounts&quot; authentication. What you get is a single-layered application, tightly coupled to Entity Framework, that:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;Ignores the patterns that facilitate testing, including: the repository pattern, unit of work pattern, and dependency injection;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Forces you to implement their &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IUser&lt;/code&gt; interface in your application’s User entity, thereby coupling it to ASP.NET Identity;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Eliminates any clear separation between your entities, persistence concerns, and business logic. Persistence ignorance? Forget about it.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Thankfully, due to the extensibility designed into ASP.NET Identity, it is possible to ditch the reference to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Microsoft.AspNet.Identity.EntityFramework&lt;/code&gt; assembly and write a custom implementation that can address these and other architectural issues. Just be forewarned: it is not a trivial undertaking, and you’ll have to put up with some code smell that is baked into the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Microsoft.AspNet.Identity.Core&lt;/code&gt; assembly.&lt;/p&gt;

&lt;p&gt;The most off-putting smell is the core &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UserManager&amp;lt;TUser&amp;gt;&lt;/code&gt; class, which at first glance doesn’t seem to be that big of a problem. Sure, it’s a behemoth, 663 line &lt;a href=&quot;http://en.wikipedia.org/wiki/God_object&quot;&gt;God class&lt;/a&gt; that acts as the gateway for all identity management functionality in ASP.NET Identity, but since it’s just an implementation detail baked into the core assembly, you don’t really have to worry about it, right? Yeah, not so much.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UserManager&amp;lt;TUser&amp;gt;&lt;/code&gt; has a single dependency on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IUserStore&amp;lt;TUser&amp;gt;&lt;/code&gt;, which doesn’t look like a problem at first glance…&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;public UserManager(IUserStore&amp;lt;TUser&amp;gt; store)
{
    if (store == null)
    {
        throw new ArgumentNullException(&quot;store&quot;);
    }
    this.Store = store;
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;…until you discover all the hidden dependencies. As it turns out, if you want password support, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UserManager&amp;lt;TUser&amp;gt;&lt;/code&gt; also requires an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IUserPasswordStore&amp;lt;TUser&amp;gt;&lt;/code&gt;. And where does it go looking? Here:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;private IUserPasswordStore&amp;lt;TUser&amp;gt; GetPasswordStore()
{
    IUserPasswordStore&amp;lt;TUser&amp;gt; userPasswordStore = this.Store as IUserPasswordStore&amp;lt;TUser&amp;gt;;
    if (userPasswordStore == null)
    {
        throw new NotSupportedException(Resources.StoreNotIUserPasswordStore);
    }
    return userPasswordStore;
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;So now your implementation of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IUserStore&amp;lt;TUser&amp;gt;&lt;/code&gt; also has to implement &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IUserPasswordStore&amp;lt;TUser&amp;gt;&lt;/code&gt;. If you want to support external logins, claims, security stamps, and roles, the class definition for your implementation is going to look something like this:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;public class UserStore&amp;lt;TUser&amp;gt;
    : IUserLoginStore&amp;lt;TUser&amp;gt;
    , IUserClaimStore&amp;lt;TUser&amp;gt;
    , IUserRoleStore&amp;lt;TUser&amp;gt;
    , IUserPasswordStore&amp;lt;TUser&amp;gt;
    , IUserSecurityStampStore&amp;lt;TUser&amp;gt;
    , IUserStore&amp;lt;TUser&amp;gt;
    , IDisposable
    where TUser : IdentityUser
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Yep, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UserManager&amp;lt;TUser&amp;gt;&lt;/code&gt; God class requires yet another God class to handle the data access for everything membership — and this one is all yours. Mine is a whopping 603 lines of awesomeness. And to make it extra-special, there is no way around it! So when I said you’d have to put up with some code smell, I wasn’t kidding. Luckily, this is the worst of it.&lt;/p&gt;

&lt;h3 id=&quot;how-were-going-to-fix-it&quot;&gt;How We’re Going to Fix It&lt;/h3&gt;

&lt;p&gt;I didn’t just write all that to convince you not to use ASP.NET Identity. On the contrary, you need to use it – just don’t do it the way Microsoft apparently wants you to. Follow patterns instead! So let’s start putting together some of the high-level requirements for a project where we’re doing it right.&lt;/p&gt;

&lt;h4 id=&quot;functional-requirements&quot;&gt;Functional Requirements&lt;/h4&gt;

&lt;p&gt;For the purposes of this tutorial, this application must possess all of the functionality you would get from an application created from the default ASP.NET MVC 5 web application template with &quot;Individual User Accounts&quot; authentication.&lt;/p&gt;

&lt;h4 id=&quot;technical-requirements&quot;&gt;Technical Requirements&lt;/h4&gt;

&lt;h5 id=&quot;persistence-ignorance&quot;&gt;&lt;strong&gt;Persistence Ignorance&lt;/strong&gt;&lt;/h5&gt;

&lt;p&gt;Fundamentally, persistence ignorance means that your entities shouldn’t care about how they’re stored, created, retrieved, updated, or deleted. Instead you just focus on modeling the business domain. The purpose of this post is not to explain persistence ignorance or Domain-Driven Design or try to convince you why you should use them, but if you’d like to know more, this is a good article: &lt;a href=&quot;http://www.codeproject.com/Articles/339725/Domain-Driven-Design-Clear-Your-Concepts-Before-Yo&quot;&gt;Domain Driven Design – Clear Your Concepts Before You Start&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For the purposes of this application, I am going to use persistence ignorance with an assumption that we’re using an ORM that supports lazy loading of related objects and collections through the use of dynamic proxies, which does make a couple of small difference in the code (but they’re good differences):&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;Our entity classes must not be sealed, must have a default constructor, and all public navigation properties must be virtual.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;We’ll be able to take advantage of lazy loading of related objects and collections (in the UserStore methods for example).&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h5 id=&quot;proper-layering&quot;&gt;&lt;strong&gt;Proper Layering&lt;/strong&gt;&lt;/h5&gt;

&lt;p&gt;The concept of &quot;proper layering&quot; is highly subjective. At the very least, that means presentation is isolated from data access by an intermediate core logic layer. I’ll cover the layering of this application in &lt;a href=&quot;/2015/01/25/persistence-ignorant-asp-net-identity-with-patterns-part-2/&quot;&gt;Part 2&lt;/a&gt; when we set up the Visual Studio Solution.&lt;/p&gt;

&lt;h5 id=&quot;patterns&quot;&gt;&lt;strong&gt;Patterns&lt;/strong&gt;&lt;/h5&gt;

&lt;p&gt;As I’ve mentioned before, this application will follow some important architectural patterns, including repository, unit of work, and dependency injection. The purpose of this post is not to explain what these patterns are or why you should use them, but I will provide links to more information about these patterns how to use them:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;http://martinfowler.com/eaaCatalog/repository.html&quot;&gt;Repository&lt;/a&gt; – specifically the use of generic repositories based on &lt;a href=&quot;http://www.asp.net/mvc/overview/older-versions/getting-started-with-ef-5-using-mvc-4/implementing-the-repository-and-unit-of-work-patterns-in-an-asp-net-mvc-application&quot;&gt;this example&lt;/a&gt;.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;http://martinfowler.com/eaaCatalog/unitOfWork.html&quot;&gt;Unit of Work&lt;/a&gt; – this application uses a variant of &lt;a href=&quot;http://www.asp.net/mvc/overview/older-versions/getting-started-with-ef-5-using-mvc-4/implementing-the-repository-and-unit-of-work-patterns-in-an-asp-net-mvc-application&quot;&gt;this example&lt;/a&gt;.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;http://en.wikipedia.org/wiki/Dependency_injection&quot;&gt;Dependency Injection&lt;/a&gt; – this application will use &lt;a href=&quot;https://unity.codeplex.com/&quot;&gt;Microsoft Unity&lt;/a&gt; as its dependency injection container and the &lt;a href=&quot;https://www.nuget.org/packages/Unity.Mvc5/&quot;&gt;Unity.Mvc5&lt;/a&gt; library.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h5 id=&quot;persistence&quot;&gt;&lt;strong&gt;Persistence&lt;/strong&gt;&lt;/h5&gt;

&lt;p&gt;I will be writing two different data layers and expect that I should be able to switch between them without much effort, thanks to the persistence ignorance requirement:&lt;/p&gt;

&lt;h3 id=&quot;next-steps&quot;&gt;Next Steps&lt;/h3&gt;

&lt;p&gt;In this part, I’ve identified the initial, high-level, function and technical requirements for persistence-ignorant, properly-layered ASP.NET MVC 5 web application with ASP.NET Identity. I spent time on some of the patterns I’ll be using and provided plenty of links for those who want to learn more. In &lt;a href=&quot;/2015/01/25/persistence-ignorant-asp-net-identity-with-patterns-part-2/&quot;&gt;Part 2&lt;/a&gt;, I’ll fire up Visual Studio and start putting the solution together.&lt;/p&gt;

&lt;p&gt;Until next time, happy coding!&lt;/p&gt;

</content>
  </entry>
  
  <entry>
    <id>http://timschreiber.com/music/2014/05/08/original-music-collide/</id>
    <link type="text/html" rel="alternate" href="http://timschreiber.com//music/2014/05/08/original-music-collide/"/>
    <title>Original Music: Collide</title>
    <updated>2014-05-08T00:00:00+00:00</updated>
    <author>
      <name>Timothy P. Schreiber</name>
      <uri>http://timschreiber.com/</uri>
    </author>
    <content type="html">&lt;div class=&quot;embed-responsive embed-responsive-16by9&quot;&gt;
    &lt;iframe class=&quot;embed-responsive-item&quot; src=&quot;//www.youtube.com/embed/o34h-n7Rswk&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;

&lt;h3 id=&quot;lyrics&quot;&gt;Lyrics&lt;/h3&gt;

&lt;p&gt;I am the sun and you are the moon&lt;br /&gt;
I will go down and you will writhe soon&lt;br /&gt;
The stars in the sky watch over tonight&lt;br /&gt;
As bodies in motion collide&lt;/p&gt;

&lt;p&gt;Collide&lt;br /&gt;
Collide&lt;br /&gt;
Collide&lt;br /&gt;
Collide&lt;/p&gt;

&lt;p&gt;No longer bound by space and time&lt;br /&gt;
I'm only yours and you're only mine&lt;br /&gt;
There's nothing in sight but the black of night&lt;br /&gt;
As we descend and collide&lt;/p&gt;

&lt;p&gt;Collide&lt;br /&gt;
Collide&lt;br /&gt;
Collide&lt;br /&gt;
Collide&lt;/p&gt;

&lt;p&gt;Copyright &amp;copy; 2013 Tim Schreiber. All rights reserved.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>http://timschreiber.com/2013/10/30/beware-the-stackoverlords/</id>
    <link type="text/html" rel="alternate" href="http://timschreiber.com//2013/10/30/beware-the-stackoverlords/"/>
    <title>Beware the StackOverlords!</title>
    <updated>2013-10-30T00:00:00+00:00</updated>
    <author>
      <name>Timothy P. Schreiber</name>
      <uri>http://timschreiber.com/</uri>
    </author>
    <content type="html">&lt;p&gt;&lt;em&gt;UPDATE: StackOverflow has acknowledged they have an asshole problem, and I blogged about it &lt;a href=&quot;/2018/04/27/stackoverflow-finally-admits-it-has-an-asshole-problem/&quot;&gt;here&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I used to think StackOverflow was awesome. Used to. But now, not so much. No, now it’s pretty much ruled by the early adopters, many of whom don’t bother asking or answering questions anymore, but rather sit on their asses and rack up massive reputation from remedial questions they answered five years ago. And with that massive reputation comes elevated privileges, which they ruthlessly wield against newbie scum to protect their standing as the StackOverlords of the Q&amp;amp;A empire.&lt;/p&gt;

&lt;p&gt;At least it seems like that sometimes. Sure, it’s a little dramatized, but it points out some glaring systemic failures the StackOverflow folks are refusing to address:&lt;/p&gt;

&lt;h4 id=&quot;damned-if-you-do-damned-if-you-dont&quot;&gt;Damned if you do, damned if you don’t&lt;/h4&gt;

&lt;p&gt;The gamification of StackOverflow encourages users to build reputation points and earn badges by asking and answering questions. Imagine for a moment that you are a new StackOverflow user. You can’t ask questions that have been asked and answered before, any broad or open-ended question, or try to solicit opinions or anything like that – even if it will help you learn and grow as a programmer. No, if you do, then the StackOverlords will smack you down with down-votes, flags, closing your question, deletion, and all other manner of other moderator assholery. And we’re not talking over a period of time – it’s going to happen in seconds. So, you aren’t going to earn any reputation or badges by asking questions.&lt;/p&gt;

&lt;p&gt;Okay, so you can get started by answering questions. Nope. Many new users like you are new not only in terms of StackOverflow membership, but also as programmers in general. More often than not, the questions you are capable of answering have already been answered by someone else. No point in expending a ton of effort on something that’s already been done, amiright? You might be able to catch a few duplicate questions as they flow into the site and provide answers before a StackOverlord kills them, but when it’s all said and done, you’re not really earning any reputation answering questions, either.&lt;/p&gt;

&lt;p&gt;The problem is that reputation is the key to gaining privileges on StackOverflow. Without it, you can basically only ask and answer questions. But as a newbie, you can’t really gain reputation by asking and answering questions.&lt;/p&gt;

&lt;h4 id=&quot;stackoverlords-are-assholes&quot;&gt;StackOverlords are assholes&lt;/h4&gt;

&lt;p&gt;There are many questions and discussions on meta.stackoverflow.com that point out the problem with rudeness on StackOverflow: &lt;a href=&quot;http://meta.stackoverflow.com/search?q=rude&quot;&gt;here&lt;/a&gt;, &lt;a href=&quot;http://meta.stackoverflow.com/questions/191029/general-attitude-stack-overflow-non-constructive-attitude-rather-than-constru&quot;&gt;here&lt;/a&gt;, &lt;a href=&quot;http://meta.stackoverflow.com/questions/161539/rude-responses-from-the-community&quot;&gt;here&lt;/a&gt; and &lt;a href=&quot;http://meta.stackoverflow.com/search?q=rude&quot;&gt;more&lt;/a&gt;.  Read the comments on &lt;a href=&quot;http://sergworks.wordpress.com/2012/09/26/why-stackoverflow-sucks/&quot;&gt;this blog post&lt;/a&gt;. If it’s beginning to seem like the StackOverlords are a bunch of old-timers yelling at the newbie kids to stay the off their lawn, you wouldn’t be too far off.&lt;/p&gt;

&lt;h4 id=&quot;stackoverlords-are-hypocrites&quot;&gt;StackOverlords are hypocrites&lt;/h4&gt;

&lt;p&gt;A StackOverlord once told me that StackOverflow is not a game, that rushing to build reputation is meaningless. I don’t buy it. If StackOverflow isn’t a game, then why they hell did they gamify it with points and badges?  And if reputation is so meaningless, why did Chief-StackOverlord-in-Charge Joel Spolsky say (and I quote), &quot;Spend a few months earning a five digit Stack Overflow reputation, and you’ll be getting job offers in the $100K+ range without an interview&quot; (go ahead, &lt;a href=&quot;http://programmers.stackexchange.com/questions/20369/career-advice-stay-with-php-or-start-a-new-career-in-something-else-net/20373#20373&quot;&gt;check my source&lt;/a&gt;)? They obviously think reputation is more than they want you to believe.&lt;/p&gt;

&lt;h4 id=&quot;stackoverlords-think-stackoverflow-is-something-its-not&quot;&gt;StackOverlords think StackOverflow is something it’s not&lt;/h4&gt;

&lt;p&gt;I’ve heard (read) StackOverlords opine that users are using StackOverflow as their first resort for solving problems, when it should really be their last. That means they expect users to thoroughly research their issues and try to solve them on their own first; which is fine, I guess. Except that StackOverflow’s mission is to become the best collection of concise programming questions and answers – no discussions or opinions allowed, just &quot;if your problem is x, then your answer is y.&quot; But doesn’t that create the very thing they’re trying to avoid? When Google a particular problem, and the first result is that problem asked and answered on StackOverflow, hasn’t it just become your option of first resort? Regardless of what the StackOverlords want it to be, it has become something else.&lt;/p&gt;

&lt;h4 id=&quot;so&quot;&gt;So…&lt;/h4&gt;

&lt;p&gt;I used to think StackOverflow was interested in helping users become better programmers. When they say they want to be the best collection of programming questions and answers, that’s what I think any rational human being would think. But that’s just not the case. Since their massive venture capital infusion, they are only interested in revenue generated by ads and sponsorships driven by their content. Actually, it’s not even their content – it’s yours, and everyone else’s who participates. Now, all of a sudden, the hostile patrolling of questions and answers and the pedantic bullying of their users makes sense. They’ve got to protect the all-important content. That’s fine. It’s their site.&lt;/p&gt;

&lt;p&gt;I guess I’m just more interested in becoming a better programmer and helping others become better programmers. I’m more interested in treating people the way I’d like to be treated. So maybe StackOverflow isn’t the place for me after all.&lt;/p&gt;

</content>
  </entry>
  
  <entry>
    <id>http://timschreiber.com/2013/10/14/you-keep-writing-that-code-i-do-not-think-it-does-what-you-think-it-does/</id>
    <link type="text/html" rel="alternate" href="http://timschreiber.com//2013/10/14/you-keep-writing-that-code-i-do-not-think-it-does-what-you-think-it-does/"/>
    <title>You Keep Writing That Code. I Do Not Think It Does What You Think It Does.</title>
    <updated>2013-10-14T00:00:00+00:00</updated>
    <author>
      <name>Timothy P. Schreiber</name>
      <uri>http://timschreiber.com/</uri>
    </author>
    <content type="html">&lt;p&gt;Dear unknown previous programmer,&lt;/p&gt;

&lt;p&gt;Good for you that you used a StringBuilder. But you completely missed the point of using a StringBuilder. Even worse that you’re concatenating SQL like this:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;StringBuilder SomeRecord = new StringBuilder(&quot;Insert into [SomeData] (SomeNumber, SomeName, SomeAttribute1, SomeAttribute2, SomeAttribute3, SomeAttribute4, SomeAttribute5, SomeAttribute6, SomeAttribute7, SomeAttribute8, SomeAttribute9, SomeFlag) &quot; +
    &quot; values ( &quot; 
    + &quot;'&quot; + lsSomeNumber + &quot;' , &quot; 
    + &quot;'&quot; + lsSomeName + &quot;', &quot;
    + &quot;'&quot; + lsSomeAttribute1 + &quot;', &quot; 
    + &quot;'&quot; + lsSomeAttribute2 + &quot;', &quot; 
    + &quot;'&quot; + lsSomeAttribute3 + &quot;', &quot;
    + &quot;'&quot; + lsSomeAttribute4 + &quot;', &quot;
    + &quot;'&quot; + lsSomeAttribute5 + &quot;', &quot;
    + &quot;'&quot; + lsSomeAttribute6 + &quot;', &quot; 
    + &quot;'&quot; + lsSomeAttribute7 + &quot;', &quot; 
    + &quot;'&quot; + lsSomeAttribute8 + &quot;', &quot;
    + &quot;'&quot; + lsSomeAttribute9 + &quot;', &quot; 
    + &quot;'&quot; + lsSomeFlag +&quot;' )&quot; );
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</content>
  </entry>
  
  <entry>
    <id>http://timschreiber.com/2013/07/29/things-you-should-almost-never-do-part-i/</id>
    <link type="text/html" rel="alternate" href="http://timschreiber.com//2013/07/29/things-you-should-almost-never-do-part-i/"/>
    <title>Things You Should (Almost) Never Do, Part I</title>
    <updated>2013-07-29T00:00:00+00:00</updated>
    <author>
      <name>Timothy P. Schreiber</name>
      <uri>http://timschreiber.com/</uri>
    </author>
    <content type="html">&lt;p&gt;Of course, required reading for anyone considering refactor vs. rewrite is Joel Spolsky’s &quot;&lt;a href=&quot;http://www.joelonsoftware.com/articles/fog0000000069.html&quot;&gt;Things You Should Never Do, Part I&lt;/a&gt;&quot; article, in which he posits the single worst strategic mistake you can make on a software project is to rewrite the code from scratch. If you haven’t read the article, stop and read it now. You’ll be glad you did.&lt;/p&gt;

&lt;p&gt;I respect Mr. Spolsky a great deal, but one paragraph in his article is really causing me a little heartburn, because I think it oversimplifies a situation that my project at work is currently facing:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;The idea that new code is better than old is patently absurd. Old code has been used. It has been tested. Lots of bugs have been found, and they’ve been fixed. There’s nothing wrong with it. It doesn’t acquire bugs just by sitting around on your hard drive. Au contraire, baby! Is software supposed to be like an old Dodge Dart, that rusts just sitting in the garage? Is software like a teddy bear that’s kind of gross if it’s not made out of all new material?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If there’s one thing I’d like to say to Mr. Spolsky right now it’s, &quot;Thank you, thank you for using the Dodge Dart analogy.&quot; Not only am I a huge Mopar fan, but it just so happens that Dodge resurrected the Dart as an all-new, redesigned &quot;from scratch&quot; model in 2013 (actually, they just did a custom implementation of a vehicle platform from Fiat). See where I’m going with this? Old vs. new, rusted vs. shiny…&lt;/p&gt;

&lt;div class=&quot;text-center&quot;&gt;&lt;img src=&quot;/img/old-new-dart.jpg&quot; alt=&quot;Old and Busted / New Hotness&quot; /&gt;&lt;/div&gt;

&lt;p&gt;I completely agree with Mr. Spolsky that software won’t acquire bugs just sitting around on your hard drive, but then an old Dodge Dart won’t &quot;rust just sitting in the garage&quot; if it’s taken care of properly. Similarly, I’ve seen plenty of software fall apart because it was left out in the elements without proper maintenance.&lt;/p&gt;

&lt;p&gt;And that’s the first point I want to make: Businesses cut budgets for software systems that appear to be functioning at an arbitrary minimum level of reliability. Competent, expensive senior-level developers are replaced by cheaper junior programmers. Invariably, things go wrong, enhancements are required; and whether through inexperience or apathy, their once beautiful, shiny software gradually becomes – well, this:&lt;/p&gt;

&lt;div class=&quot;text-center&quot;&gt;&lt;img src=&quot;/img/cardboard-tank.jpg&quot; alt=&quot;They're &amp;quot;Features&amp;quot;&quot; /&gt;&lt;/div&gt;

&lt;p&gt;Is it possible to strip away the cardboard and duct tape and return the car to its pristine working condition? Of course! With enough time, money, and effort, I believe anything is possible; but therein lays my second point: it’s impossible to remove the all the crap and rot, and refactor all the bad code without losing at least some of the features, fixes, and knowledge that have accumulated over time – at least on a budget and timeline the business is happy with.&lt;/p&gt;

&lt;p&gt;A recent &lt;a href=&quot;http://dsc.discovery.com/tv-shows/fast-n-loud/season-2-episodes4.htm&quot;&gt;episode&lt;/a&gt; of the Discovery Channel’s Fast ‘n Loud comes to mind. Richard Rawlings and Aaron Kaufman from Gas Monkey Garage bought a 1964 Dodge Sweptline pickup truck in pretty rough shape for $750, and turned it into something better than the original, but all did not go smoothly. They ripped out the original front end and were forced to make some major, expensive frame modifications to get a front end from a Ford Crown Victoria to fit. Similarly, they used an engine and transmission from a modern Chevrolet Tahoe, which caused all kinds of mounting difficulties and required the steering to be custom built. And whether by choice or by budget, they just sanded and clear coated the rather rough looking exterior instead of giving it a shiny new paint job. The price for the final result? Just shy of $50,000.&lt;/p&gt;

&lt;div class=&quot;text-center&quot;&gt;&lt;img src=&quot;/img/dodge-hodge-podge.jpg&quot; alt=&quot;Dodge Hodge Podge&quot; /&gt;&lt;/div&gt;

&lt;p&gt;The performance of the &quot;Dodge Hodge Podge,&quot; as they call it, is quite impressive; but in my opinion, it still looks like a rusted out piece of crap. Lacking many of the features, comforts, and conveniences that have become expected – if not required – in today’s market, it is nothing more than a very exciting novelty. And that brings me to my third and final point: the passage of time in the real world will eventually render even the best designed and implemented software systems obsolete.&lt;/p&gt;

&lt;p&gt;As software developers, we live in a world where the only constant is change. Technologies progress, frameworks evolve, patterns found, and best practices appear. And then there’s Microsoft. In the early days of .NET, they developed what I can only describe as anti-patterns and anti-practices and published them on MSDN as their holy gospel. Bloggers and tutorial websites picked up on it; and before long, a whole generation of web developers was learning how to program by spreading application logic across all the Page_Load and Button_Click event handlers in their CodeBehind classes.&lt;/p&gt;

&lt;p&gt;There are many other examples in many different languages and frameworks and projects, but my point is this: what seem like good decisions in an early in the game almost always come back to bite us later, and unless the business makes sure the project stakeholders and developers are well-funded, and unless everyone involved in the project stays 100% vigilant, eventually the fast paced technological world passes up the old software system. The problem is that once the business decides a product is stable or performant enough, the money dries up and it becomes impossible to keep it from becoming obsolete. The attitude that old code is good enough because it works, as Mr. Spolsky asserts, only exacerbates the problem.&lt;/p&gt;

&lt;p&gt;So what to do? That’s what I’m trying to figure out. I don’t necessarily want to rewrite the project I inherited at work, but I’m thinking I might have to. Our data layer is buggy, generated by an in-house tool we no longer have the source code for, and based on antiquated ADO.NET DataSets. It needs to be completely redesigned and rewritten, with a requirement from above that we move to Entity Framework. Our business logic layer is tightly coupled to our data access layer, and huge swaths of code will have to be rewritten to accommodate the new data layer. The service layer was written by someone who obviously didn’t understand SOAP. For some reason, someone thought passing objects as XML string parameters in WebMethods and serializing/deserializing them on both ends was a good idea – nope, but now the presentation layer needs a reference to the business layer to work. There are many other facets that need help as well.&lt;/p&gt;

&lt;p&gt;With such a mess of tightly coupled, poorly executed code, does it still make sense to keep what we have just because it works? Do we do the &quot;Dodge Hodge Podge&quot; thing to our software and hope that our incremental changes and improvements don’t take forever and leave us with sticker shock in the end? Like I mentioned before, are we at the tipping point at which we’ve accrued so much technical debt that it just makes more sense to cut our losses and start over with fresh, new code? Should the &quot;Things You Should Never Do&quot; be renamed &quot;Things You Should Almost Never Do?&quot;&lt;/p&gt;

&lt;p&gt;UPDATE: I just had a thought that if the project had been initiated with incremental improvement in mind, then incrementally improving it would be easier to swallow. But every time I turn around, there’s some new roadblock or missing source code or whatever standing in my way. A commenter advised to be prepared for the pain, but I’m in pain either direction I go. I just need to choose the option with the least amount of it.&lt;/p&gt;

</content>
  </entry>
  
  <entry>
    <id>http://timschreiber.com/2013/07/28/welcome-to-my-new-improved-whatever-this-is/</id>
    <link type="text/html" rel="alternate" href="http://timschreiber.com//2013/07/28/welcome-to-my-new-improved-whatever-this-is/"/>
    <title>Welcome to my New, Improved Whatever-This-Is</title>
    <updated>2013-07-28T00:00:00+00:00</updated>
    <author>
      <name>Timothy P. Schreiber</name>
      <uri>http://timschreiber.com/</uri>
    </author>
    <content type="html">&lt;p&gt;I’ve been using WordPress for some side projects lately and decided it was a great time to consolidate my blog and résumé sites into one. Not a lot has changed in terms of content, except for my new homepage. You should go check it out if you haven’t already. Other than that, there’s my blog and my résumé, and that’s pretty much it.&lt;/p&gt;

&lt;p&gt;I know I promised last year to start posting again once things calmed down, and now I intend to do just that. Nine months after I picked my family up and moved us halfway across the United States from Utah to Kentucky, we are finally settling into what I believe has become our new normal. Okay, maybe we might have been settled for a while, but… whatever.&lt;/p&gt;

&lt;p&gt;At work, my project is suffocating under a mountain of technical debt that has accumulated over its nine year history, which led me to tweet the following question yesterday:&lt;/p&gt;

&lt;blockquote class=&quot;twitter-tweet&quot; lang=&quot;en&quot;&gt;&lt;p&gt;I know Refactor &amp;gt; Rewrite, but is there a point at which you accrue enough technical debt that you declare bankruptcy and start over?&lt;/p&gt;&amp;mdash; Tim Schreiber (@tim_schreiber) &lt;a href=&quot;https://twitter.com/tim_schreiber/status/361330768972234753&quot;&gt;July 28, 2013&lt;/a&gt;&lt;/blockquote&gt;
&lt;script async=&quot;&quot; src=&quot;//platform.twitter.com/widgets.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;

&lt;p&gt;I’m still reading and considering a great many things, but that will be the topic of my next post, and apparently my job, for the foreseeable future.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>http://timschreiber.com/2012/08/14/things-are-a-changin/</id>
    <link type="text/html" rel="alternate" href="http://timschreiber.com//2012/08/14/things-are-a-changin/"/>
    <title>Things Are A-Changin'</title>
    <updated>2012-08-14T00:00:00+00:00</updated>
    <author>
      <name>Timothy P. Schreiber</name>
      <uri>http://timschreiber.com/</uri>
    </author>
    <content type="html">&lt;p&gt;&quot;Wait, what?&quot; some might be thinking, &quot;I thought you were pleased as punch to be an STG company man.&quot;&lt;/p&gt;

&lt;p&gt;Well, I have been and for the most part still am, but nothing lasts forever. Believe me, I’d be happy to stay put at my current gig, but I have decided to relocate to the Lexington, Kentucky area for family reasons. Given the difference in the software development job market between Salt Lake City and Lexington, this is quite a commitment to be making. There just aren’t as many opportunities out there. But that also means the talent pool is smaller, and I have a great opportunity to really try to stand out.&lt;/p&gt;

&lt;p&gt;So yes, I’m looking for direct-hire or long-term contract employment in and around Lexington and Frankfort, Kentucky. Maybe if things don’t pan out by early next year, I might consider Louisville then. STG is trying to find client work out that way for me, but I’m definitely exploring opportunities on my own as well.&lt;/p&gt;

&lt;p&gt;To support my job hunt, I’ve updated my &lt;a href=&quot;http://timschreiber.com&quot;&gt;timschreiber.com&lt;/a&gt; website with my latest résumé and contact form. So, go check it out!&lt;/p&gt;

&lt;p&gt;Finally, if you or anyone you know has any leads in Kentucky, pass them on! If you don’t have my e-mail address, you can always use my contact form at &lt;a href=&quot;http://timschreiber.com/contact.aspx&quot;&gt;timschreiber.com/contact.aspx&lt;/a&gt;. Thanks!&lt;/p&gt;

</content>
  </entry>
  
  <entry>
    <id>http://timschreiber.com/2012/08/12/dear-recruiters/</id>
    <link type="text/html" rel="alternate" href="http://timschreiber.com//2012/08/12/dear-recruiters/"/>
    <title>Dear Recruiters</title>
    <updated>2012-08-12T00:00:00+00:00</updated>
    <author>
      <name>Timothy P. Schreiber</name>
      <uri>http://timschreiber.com/</uri>
    </author>
    <content type="html">&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;If you are recruiting candidates who reside in the United States for positions located in the United States, do not offshore your cold calls to India.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;If you find my résumé with a keyword search on the job boards, take ten seconds to see if my location, skills, and experience are actually a match. Pushing some three-month junior/entry-level contract in New Jersey on someone in Salt Lake City with 15 years of experience is a good way to piss them off.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;If you call me, I will most likely not answer if I do not immediately recognize your number. If that happens, do not call over and over again. That is called harassment and will get you reported to the authorities. Please leave a single voice mail. If I do not have a pre-existing personal or business relationship with you, do not mark that voice mail &quot;urgent.&quot; That is unprofessional and annoying. If I do not return your call, you may interpret it as &quot;not interested,&quot; after which I would appreciate if you would stop calling.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;If you email me, do not use some crappy form letter. Feeling like just another database record does not exactly give me the warm fuzzies about your agency or the companies you represent. But if you must use a form letter, at least try to customize it with my information. Few things turn me off faster than reading &quot;Dear Consultant&quot; at the top of an email. As with phone calls, if I do not respond to your email, you may interpret it as &quot;not interested.&quot; If you continue to email me, I will blacklist company’s domain name and report you to SpamCop.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You will eventually learn as I have, that in career matters, it is the relationships that are most important. Fail to build and nurture those relationships, and you will fail to attract the best talent. Yes, it takes effort. No, it is not easy. But follow these simple guidelines, and you will see considerable improvement in the quantity and quality of your placements.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <id>http://timschreiber.com/2012/04/07/my-first-randori/</id>
    <link type="text/html" rel="alternate" href="http://timschreiber.com//2012/04/07/my-first-randori/"/>
    <title>Randori: Group Practice</title>
    <updated>2012-04-07T00:00:00+00:00</updated>
    <author>
      <name>Timothy P. Schreiber</name>
      <uri>http://timschreiber.com/</uri>
    </author>
    <content type="html">&lt;p&gt;In the second meeting of the Utah Software Craftsmanship group that I attended, &lt;a href=&quot;http://blog.softwareontheside.com&quot;&gt;Mike Clement&lt;/a&gt; presented on Randori: Group Practice. Randori may be contrasted with kata, as two potentially complementary types of training.&lt;/p&gt;

&lt;p&gt;Kata is a Japanese word describing detailed choreographed patterns of movements practiced either solo or in pairs. A code kata is an exercise in programming which helps a programmer hone their skills through practice and repetition. Dave Thomas has published many katas at &lt;a href=&quot;http://codekata.pragprog.com/codekata&quot;&gt;http://codekata.pragprog.com/codekata&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Where kata is primarily an individual exercise, Randori is a team exercise, and Mike does a great job at explaining it on his blog: &lt;a href=&quot;http://blog.softwareontheside.com/2012/03/utah-code-camp-spring-2012-slide-decks.html&quot;&gt;http://blog.softwareontheside.com/2012/03/utah-code-camp-spring-2012-slide-decks.html&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The 11 or 12 of us in attendance performed the Numbers to LCD kata from Mike’s &lt;a href=&quot;http://www.slideshare.net/mdclement/randori&quot;&gt;slide deck&lt;/a&gt; and &lt;a href=&quot;https://github.com/mdclement/NumbersToLcdRandoriBase&quot;&gt;starter project&lt;/a&gt;. It was interesting to see the direction the pairs took as we rotated through the audience. First, there were tests for each digit and multi-digits, along with a bunch of if..else if..else statements to translate the integer into the string representation of the LCD. Then someone refactored the if..else if..else statements into a switch statement. Then someone streamlined testing with [Setup] and [TestCase]. Then my navigator and I refactored the switch to a Dictionary. Finally, after much trial and error, we arrived at the obvious answer staring us in the face the whole time: indexing through the string representation of the LCDs.&lt;/p&gt;

&lt;p&gt;It was yet another example of how we as software developers tend to overcomplicate the problem upon first analysis, and even though I strive for simplicity in software, my Dictionary solution was unnecessarily complex in comparison to the final result. But the value we gained from the exercise had nothing to do with what ended up producing. Instead, it was the activity of producing it that proved to be of most value. And seeing how my peers approached the problem provided a ton of insight that I would otherwise have missed on my own. Last night’s exercise was not only my first introduction to Randori, but also to Code Kata in general. I intend to learn more and start applying the principles of code practice in my individual professional development. I’m also thinking about conducting a couple of STG brown bags on Code Kata and Randori.&lt;/p&gt;

&lt;p&gt;===&lt;/p&gt;

</content>
  </entry>
  
</feed>