Задать вопрос
Ответы пользователя по тегу Веб-разработка
  • Как реализовать Asp.Net Identity 2.0 авторизацию на Onion-архитектуре?

    Valeriy1991
    @Valeriy1991
    Разработчик .NET C# (ASP.NET MVC) в Alfa-B, Moscow
    Добрый день!

    Я пока что обнаружил рабочий вариант, который очень хорошо тестируется.

    1. Создаем несколько проектов:
    - DAL.Abstract - лежат интерфейсы Generic-репозитория, специализированных репозиториев и UnitOfWork'а;
    - DAL.Real - содержит реализации интерфейсов из DAL.Abstract;
    - DAL.Fakes - fake-реализации для юнит-тестов;
    - BL.Abstract - лежат интерфейсы бизнес-сервисов;
    - BL.Real - здесь реализации интерфейсов бизнес-сервисов;
    - BL.Fakes - fake-реализации вспомогательных сервисов (EmailService, SmsService из Identity);
    - BL.Tests - юнит-тесты бизнес-логики;
    - Core.Models - модели предметной области (code-first).

    2. Т.к. я использую шаблон UnitOfWork, то пришлось отказаться от готовой реализации из библиотеки Identity.EntityFramework в силу того, что в ней расположены уже готовые классы User, Role, Claims и др., и приходится эту библиотеку тащить во все проекты, где нужно использовать хотя бы User'а. А вслед за ней во все проекты тащится библиотека EntityFramework... Мне это не нравится, потому что, на мой взгляд, слой MVC не должен знать про частности реализации (т.е. про EntityFramework).
    Поэтому самый простой вариант - сделать велоси... гм, собственную реализацию всяких там IUserStore, IPasswordStore и др.интерфейсов из Microsoft.AspNet.Identity.Core (ну и соответственно, создать собственные классы User, Role, UserRole, Claims, ExternalLogin), которые могли бы работать с нужным мне UnitOfWork'ом.

    3. Пишем тест:
    [TestFixture]
        public class UserServiceTests
        {
            private FakeEmailService _emaiService;
            private FakeSmsService _smsService;
            private IUserServiceFactory _userServiceFactory;
            private IUserService _userSrv;
            private IAuthenticationManager _authManager;
            private IUserRepository _userRepository;
    
            [SetUp]
            public void PreInit()
            {
                var authManagerMock = new Mock<IAuthenticationManager>();
                _authManager = authManagerMock.Object;
    
                _userRepository = new FakeUserRepository();
    
                _smsService = new FakeSmsService();
                _emaiService = new FakeEmailService();
                _userServiceFactory = new UserServiceFactory(
                    new FakeUnitOfWorkFactory(_userRepository),
                    _emaiService, _smsService, _authManager);
    
                _userSrv = _userServiceFactory.Create();
                // Заполняем хранилище пользователем "exist@exist.ru"
                RegisterExistUser();
            }
            private RegisterResult RegisterExistUser()
            {
                var model = new RegisterNewUserViewModel(ConstForTest.Users.ExistUser.Email, ConstForTest.Users.ExistUser.Password);
                return RegisterUser(model);
            }
            // Регистрация пользователя "valid@user.ru"
            private RegisterResult RegisterValidUser()
            {
                var model = new RegisterNewUserViewModel(ConstForTest.Users.ValidUser.Email, ConstForTest.Users.ValidUser.Password);
                return RegisterUser(model);
            }
    
            [Category(ConstForTest.Categories.UserServiceTests.RegisterNewUser)]
            [Test]
            public void RegisterNewUser_ValidEmailAndPassword_AfterRegistrationEmailDoesNotConfirmed()
            {
                var email = ConstForTest.Users.ValidUser.Email;
    
                // Act
                RegisterValidUser();
    
                var foundUser = _userSrv.GetUser(email);
                Assert.IsNotNull(foundUser);
                Assert.IsFalse(foundUser.EmailConfirmed);
            }
        }


    4. Пишем логику бизнес-сервисов. Приведу пример регистрации юзера:
    public class UserService : IUserService
        {
            private readonly IUnitOfWorkFactory _unitFactory;
            private readonly IUserIdentityManagerFactory _userManagerFactory;
            private readonly ISignInManagerFactory _signInManagerFactory;
    
            public UserService(IUnitOfWorkFactory unitFactory, 
                IUserIdentityManagerFactory userManagerFactory,
                ISignInManagerFactory signInManagerFactory)
            {
                _unitFactory = unitFactory;
                _userManagerFactory = userManagerFactory;
                _signInManagerFactory = signInManagerFactory;
            }
    
            public RegisterResult RegisterNewUser(RegisterNewUserViewModel model)
            {
                using (var unit = _unitFactory.Create())
                {
                    RegisterResult result = new RegisterResult();
                    IdentityResult identityResult;
                    try
                    {
                            var username = model.Email;
                            var manager = CreateUserManager(unit.UserRepository);
                            identityResult = manager.Create(new User(username), model.Password);
                            if (identityResult.Succeeded)
                            {
                                var createdUser = unit.UserRepository.GetByUsernameQuery(username);
                                manager.AddToRole(createdUser.Id, Roles.Client.ToString());
    
                                var confirmEmailCode = manager.GenerateEmailConfirmationToken(createdUser.Id);
                                result.ConfirmEmailCode = confirmEmailCode;
                                manager.SendEmail(createdUser.Id, Const.EmailSubjects.Account.ConfirmEmail,
                                    "<Тут содержимое  письма для подтверждения е-майл пользователя>");
                            }
                            unit.Commit();
                    }
                    catch (Exception ex)
                    {
                        identityResult = IdentityResult.Failed("В процессе регистрации возникла ошибка. Попробуйте выполнить операцию еще раз.");
                        unit.Rollback();
                    }
                    result.IdentityResult = identityResult;
                    return result;
                }
            }
            
            // ... другие методы бизнес-сервиса
            
        }


    Метод CreateUserManager(unit.UserRepository) создает экземпляр класса UserManager из библиотеки Microsoft.AspNet.Identity), передавая ему репозиторий работы с пользователями (да-да, т.к. у нас Identity, то необходимо вынести репозиторий для юзеров в отдельный интерфейс IUserRepository):
    private UserManager<User, Guid> CreateUserManager(IUserRepository userRepository)
            {
                return _userManagerFactory.Create(userRepository);
            }


    Фабрика создания UnitOfWork'а выглядит так:
    public interface IUnitOfWorkFactory
        {
            IUnitOfWork Create();
            IUnitOfWork Create(IsolationLevel level);
        }    
        public class EfUnitOfWorkFactory : IUnitOfWorkFactory
        {
            public IUnitOfWork Create()
            {
                return Create(IsolationLevel.ReadCommitted);
            }
    
            public IUnitOfWork Create(IsolationLevel level)
            {
                var uow = new EfUnitOfWork(level);
                return uow;
            }
        }

    Сам UnitOfWork:
    public interface IUnitOfWork : IDisposable, IUnitOfWorkRepositories
        {
            void SaveChanges();
            void Commit();
            void Rollback();
        }
        public interface IUnitOfWorkRepositories
        {
            IUserRepository UserRepository { get; }
        }
        public class EfUnitOfWork : IUnitOfWork
        {
            private bool _isDisposed = false;
            private DbContext _db;
            private DbContextTransaction _transaction;
    
            public EfUnitOfWork(IsolationLevel level = IsolationLevel.ReadCommitted)
            {
                _db = new MyApplicationDbContext();
                _transaction = _db.Database.BeginTransaction(level);
            }
    
            #region IDisposable
    
            protected virtual void Dispose(bool disposing)
            {
                if (!_isDisposed)
                {
                    if (disposing)
                    {
                        if (_transaction != null)
                        {
                            _transaction.Rollback();
                            _transaction.Dispose();
                            _transaction = null;
                        }
                        _db.Dispose();
                        _db = null;
                    }
                }
                _isDisposed = true;
            }
    
            public void Dispose()
            {
                Dispose(true);
                GC.SuppressFinalize(this);
            }
    
            #endregion
    
            #region IUnitOfWork
    
            public void SaveChanges()
            {
                _db.SaveChanges();
            }
    
            public void Commit()
            {
                SaveChanges();
                _transaction.Commit();
            }
    
            public void Rollback()
            {
                _transaction.Rollback();
            }
    
            #endregion
    
            private IUserRepository _userRepo;
    
            public IUserRepository UserRepository
            {
                get { return _userRepo ?? (_userRepo = new EfUserRepository(_db)); }
            }
        }


    В итоге мы получаем, на мой взгляд, хорошо тестируемую архитектуру. При этом можно написать десятки тестов, проверяющих функционал бизнес-сервиса работы с пользователями, в изоляции от MVC, EntityFramework и др. конкретных реализаций.

    P.S.
    1. Буду рад замечаниям со стороны опытных коллег относительно данной реализации.
    2. В качестве основы взяты материалы цикла статей
    3. Я придерживаюсь той концепции, что ни один Repository не должен знать про SaveChanges, а уж тем более про Commit и Rollback. Это прерогатива UnitOfWork'а, а Repository должен реализовывать лишь CRUD-операции. Поэтому метод SaveChanges вынесен в интерфейс IUnitOfWork.
    Ответ написан
    Комментировать
  • Переход. From ASP.NET To ASP.NET MVC?

    Valeriy1991
    @Valeriy1991
    Разработчик .NET C# (ASP.NET MVC) в Alfa-B, Moscow
    Добрый день!
    По собственному опыту могу сказать, что ASP.NET MVC показался мне более понятным, простым, ясным и прозрачным, чем "эти ужасные" (уж извините за субъективное мнение) ASP.NET WebForms. Начал свое знакомство с ASP.NET именно с WebForms, потом перешел на MVC - моему счастью не было предела.

    По Вашим "стадиям":
    1. Когда изучите ASP.NET MVC по книге "ASP.NET MVC x для профессионалов" (x - номер версии).
    2. Здесь все очень субъективно и зависит от самих проектов и их задач. Можно написать 15 проектов на MVC, но они все будут как один. А можно написать 2 проекта на MVC, но при этом они могут различаться настолько, что имея за плечами всего пару проектов, Вы уже будете знать наизусть весь MVC, C#, Entity Framework, JavaScript и паттерны проектирования в придачу.

    Можно, конечно, всю жизнь лепить проекты на WebForms, но я бы Вам настоятельно рекомендовал все новые проекты делать уже исключительно на MVC. Тем более что за неделю Вы сможете его спокойно изучить по книге (при условии полного рабочего дня).
    Ответ написан
    4 комментария
  • Какая должна быть структура SQL запросов, учитывая текущего пользователя?

    Valeriy1991
    @Valeriy1991
    Разработчик .NET C# (ASP.NET MVC) в Alfa-B, Moscow
    Добрый день! Возможно, имеет смысл взять MembershipAPI из коробки и особо не париться с моделью пользователей? А мыслите, в принципе, в правильном направлении. Поддержу Станислав Макаров - если вы делаете update и delete по id записи, то и переписывать все эти запросы не надо. Возможно, использование ORM Вам упростит жизнь.
    Ответ написан
    1 комментарий
  • Авторизация для SignalR?

    Valeriy1991
    @Valeriy1991
    Разработчик .NET C# (ASP.NET MVC) в Alfa-B, Moscow
    Добрый день!

    Я, к сожалению, такую задачу не решал, но могу предположить, что логику авторизации в методе действия, который занимается авторизацией, можно просто продублировать в метод C# внутри хаба SignalR. Этот метод будет принимать, например, string username и string password, и дальше с ними работать.

    Плюс (Вы наверняка знаете) на www.asp.net есть статьи, посвященные модели безопасности SignalR и авторизации с помощью SignalR:

    www.asp.net/signalr/overview/security/introduction...
    www.asp.net/signalr/overview/security/hub-authorization
    www.asp.net/signalr/overview/security/persistent-c...

    Вроде бы там достаточно просто... Хотя я могу ошибаться.
    Ответ написан
  • Какую выбрать технологию и язык для написания диплома (ASP.NET MVC 4 или Yii PHP Framework или Ruby on Rails)?

    Valeriy1991
    @Valeriy1991
    Разработчик .NET C# (ASP.NET MVC) в Alfa-B, Moscow
    Добрый день!
    Вставлю свои 5 копеек по ASP.NET MVC:

    1. Скорость изучения.
    Учитывая, что Вы уже знакомы с C#, то изучение основ ASP.NET и особенностей разработки на ASP.NET MVC с помощью книг "Microsoft ASP .NET 4 с примерами на c# 2010 для профессионалов" (Мэтью МакДональд, Адам Фримен, Марио Шпушта) и "ASP.NET MVC3 (или 4) Framework с примерами на C# для профессионалов" (Адам Фримен, Стивен Сандерсон) займет у Вас примерно месяц при условии 4-5 часов занятий в день.
    Если будете разрабатывать на ASP.NET MVC и выберете ORM EntityFramework, то берите сразу последнюю (6.1.) версию. Статья по EF6 Вам в помощь.

    2. Функциональность.
    Этого пункта не понял, поэтому лучше промолчу.

    3. Создание api (или через что лучше организовать доступ с сайту через мобильное приложение?) для приложения андроид.
    На asp.net есть отличные статьи по ASP.NET WebAPI.

    4. Возможность найти помощь по проблемам (другими словами размер лояльного сообщества)
    К Вашим услугам:
    MSDN
    asp.net
    Stackoverflow
    Собственно, сам Toster и Хабр
    Лично я обычно делаю так: захожу в гугл и формирую запрос по английски, потому что по личному опыту, так решить проблему можно быстрее, чем искать на русскоязычных источниках.

    5. Сложность изучения
    Опять-таки учитывая тот факт, что с C# Вы уже знакомы, то - примерно средняя (чисто субъективно).

    6. Производительность
    Боюсь, тут я ничем Вам не смогу помочь, т.к. на PHP/RoR я не программировал и производительность всех трех технологий не сравнивал. Что касательно MVC, то заметил, что на локальном ПК (localhost) при разработке веб-приложение грузится чуть медленнее, чем на реальном хостинге или выделенном сервере (Да оно, собственно говоря, и понятно, т.к. на хостинге-то стоит нормальный IIS, а в VisualStudio при build проекта он запускается под Local IIS Express)

    7. Подскажите что еще важно при выборе?
    Пожалуй, цена вопроса. Так, для разработки на PHP Вам достаточно (если я не ошибаюсь) любого бесплатного редактора (Eclipse, Aptana, Codelobster, ...) и бесплатного веб-сервера Apache. Что касается разработки под ASP.NET MVC (да и вообще на стеке технологий .NET), то тут у Вас есть 2 пути:
    1) использование free Express-версий продуктов (Visual Studio, MS SQL Server)
    2) соответственно, использование платных версий тех же продуктов.

    И еще пару моментов:
    1. Узнайте, состоит ли Ваш универ в альянсе Microsoft Academy Alliance (или как-то так). Мой универ состоял, и нам в Dreamspark были доступны все платные продукты за бесплатно - VS вплоть до Ultimate, MS SQL Server, все версии Windows и др. (естесственно, "в образовательных целях"). Если состоит, то узнайте у Ваших IT-преподователей, как получить доступ. Когда учился в универе, меня регистрирова препод, а потом на мой email пришло письмо на english, что-то вроде "ура, вы зарегистрированы, ваши данные для входа такие-то...".
    2. Если п.1 не сработал, то есть сайт - ИНТУИТ. Закончив там (вроде бы достаточно только 1 курса) курс от Microsoft, вы получаете ключ для регистрации на Dreamspark, в котором Вам открываются след.возможности:
    - Visual Studio all versions professional
    - MS SQL Server all versions
    - Windows + Windows Server
    И это, опять-таки, все бесплатно.

    По поводу Вашей темы диплома.
    Лично я немного не согласен с @SamDark в том плане, что "важна тема диплома, ее новизна и практическое значение". Новизна и практическое значение важны больше при разработке и защите различных диссертаций (докторских и какие там еще есть?), чем для диплома. Ваша идея, считаю, отличная, и неважно, что кто-то говорит, что "таких много". Да, много, но можно предложить свое решение задач, которое будет интереснее, красивее, удобнее, дешевле, проще, ... (нужное подчеркнуть). Если Вам эта тема интересна, и, что называется, тревожит душу, то беритесь за нее. Важна не идея, а ее реализация. К примеру: zappos.com - интернет-магазин обуви. Вроде бы чертова обувь... А если присмотреться, то их бизнес уникален (точнее, уникально предложенное ими решение проблемы - покупка обуви в интернете). Или basecamp от 37signals - вроде бы система управления проектами. А ведь Microsoft Project - тоже система управления проектами. Но на мой субъективный взгляд они и рядом не стоят. Разница в идее? Нет. Разница в реализации. А если получится действительно хороший продукт, то можно его превратить в SaaS и получать за это свои кровные.

    Надеюсь, я Вам немного помог в Вашем выборе.
    Ответ написан
    2 комментария