ASP.NET Identity в многоуровневой архитектуре. Часть 2

Данное руководство устарело. Актуальное руководство: Руководство по ASP.NET Core

Последнее обновление: 11.11.2015

Теперь добавим в решение новый проект по типу Class Library, который назовем UserStore.BLL.

Так как наше приложение будет использовать систему ASP.NET Identity, то в этот проект через Nuget пакеты Microsoft.AspNet.Identity.Core и Microsoft.AspNet.Identity.EntityFramework.

И также добавим в проект ссылку на проект DAL.

Вначале определим в проекте новую папку, назовем ее DTO, и добавим в нее новый класс UserDTO:

namespace UserStore.BLL.DTO
{
    public class UserDTO
    {
        public string Id { get; set; }
        public string Email { get; set; }
        public string Password { get; set; }
        public string UserName { get; set; }
        public string Name { get; set; }
        public string Address { get; set; }
        public string Role { get; set; }
    }
}

Через данный класс мы будем передавать информацию о пользователях на уровень представления или, наоборот, получать с этого уровня данные. Данный класс содержит все основные свойства, соответствующие свойствам моделей ApplicationUser и ClientProfile.

Также добавим в проект папку Infrastructure, которая будет хранить различные вспомогательные классы. Определим в ней один класс OperationDetails:

namespace UserStore.BLL.Infrastructure
{
    public class OperationDetails
    {
        public OperationDetails(bool succedeed, string message, string prop)
        {
            Succedeed = succedeed;
            Message = message;
            Property = prop;
        }
        public bool Succedeed { get; private set; }
        public string Message { get; private set; }
        public string Property { get; private set; }
    }
}

Данный класс будет хранить информацию об успешности операции. Свойство Succedeed указывает, успешна ли операция, а свойства Message и Property будут хранить соответственно сообщение об ошибке и свойство, на котормо произошла ошибка.

Далее добавим в проект папку Interfaces. Поместим в нее новый интерфейс IUserService:

using System;
using System.Collections.Generic;
using System.Security.Claims;
using System.Threading.Tasks;
using UserStore.BLL.DTO;
using UserStore.BLL.Infrastructure;

namespace UserStore.BLL.Interfaces
{
    public interface IUserService : IDisposable
    {
        Task<OperationDetails> Create(UserDTO userDto);
        Task<ClaimsIdentity> Authenticate(UserDTO userDto);
        Task SetInitialData(UserDTO adminDto, List<string> roles);
    } 
}

Через объекты данного интерфейса уровень представления будет взаимодействовать с уровнем доступа к данным. Здесь определены только три метода: Create (создание пользователей), Authenticate (аутентификация пользователей) и SetInitialData (установка начальных данных в БД - админа и списка ролей).

Далее добавим в папку Interfaces новый интерфейс IServiceCreator:

namespace UserStore.BLL.Interfaces
{
    public interface IServiceCreator
    {
        IUserService CreateUserService(string connection);
    }
}

В данном случае для упрощения примера я не буду использовать контейнеры внедрения зависимостей, а вместо этого воспользуюсь абстрактной фабрикой, которую будет представлять интерфейс IServiceCreator. Хотя естественнно можно также использовать для внедрения зависимостей DI-контейнеры типа Ninject.

И в конце добавим в проект папку Services. В ней определим класс UserService:

using UserStore.BLL.DTO;
using UserStore.BLL.Infrastructure;
using UserStore.DAL.Entities;
using System.Threading.Tasks;
using Microsoft.AspNet.Identity;
using System.Security.Claims;
using UserStore.BLL.Interfaces;
using UserStore.DAL.Interfaces;
using System.Collections.Generic;
using System.Linq;

namespace UserStore.BLL.Services
{
    public class UserService : IUserService
    {
        IUnitOfWork Database { get; set; }

        public UserService(IUnitOfWork uow)
        {
            Database = uow;
        }

        public async Task<OperationDetails> Create(UserDTO userDto)
        {
            ApplicationUser user = await Database.UserManager.FindByEmailAsync(userDto.Email);
            if (user == null)
            {
                user = new ApplicationUser { Email = userDto.Email, UserName = userDto.Email };
                var result = await Database.UserManager.CreateAsync(user, userDto.Password);
				if (result.Errors.Count() > 0)
					return new OperationDetails(false, result.Errors.FirstOrDefault(), "");
                // добавляем роль
                await Database.UserManager.AddToRoleAsync(user.Id, userDto.Role);
                // создаем профиль клиента
                ClientProfile clientProfile = new ClientProfile { Id = user.Id, Address = userDto.Address, Name = userDto.Name };
                Database.ClientManager.Create(clientProfile);
                await Database.SaveAsync();
                return new OperationDetails(true, "Регистрация успешно пройдена", "");
            }
            else
            {
                return new OperationDetails(false, "Пользователь с таким логином уже существует", "Email");
            }
        }

        public async Task<ClaimsIdentity> Authenticate(UserDTO userDto)
        {
            ClaimsIdentity claim = null;
            // находим пользователя
            ApplicationUser user = await Database.UserManager.FindAsync(userDto.Email, userDto.Password);
            // авторизуем его и возвращаем объект ClaimsIdentity
            if(user!=null)
                claim= await Database.UserManager.CreateIdentityAsync(user,
                                            DefaultAuthenticationTypes.ApplicationCookie);
            return claim;
        }

        // начальная инициализация бд
        public async Task SetInitialData(UserDTO adminDto, List<string> roles)
        {
            foreach (string roleName in roles)
            {
                var role = await Database.RoleManager.FindByNameAsync(roleName);
                if (role == null)
                {
                    role = new ApplicationRole { Name = roleName };
                    await Database.RoleManager.CreateAsync(role);
                }
            }
            await Create(adminDto);
        }

        public void Dispose()
        {
            Database.Dispose();
        }
    }   
}

С помощью объекта IUnitOfWork сервис будет взаимодействовать с базой данных.

И определим в этой же папке класс фабрики, которая будет создавать этот сервис:

using UserStore.BLL.Interfaces;
using UserStore.DAL.Repositories;

namespace UserStore.BLL.Services
{
    public class ServiceCreator : IServiceCreator
    {
        public IUserService CreateUserService(string connection)
        {
            return new UserService(new IdentityUnitOfWork(connection));
        }
    }
}

В итоге получился проект со следующей структурой:

ASP.NET Identity in Business Logic Layer
Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850