Данное руководство устарело. Актуальное руководство: Руководство по ASP.NET Core
Теперь добавим в решение новый проект по типу 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)); } } }
В итоге получился проект со следующей структурой: