.Net Framework и С#, весна 2015: ASP.NET WEB Patterns

41
Толстиков Никита [email protected] ASP.NET WEB Patterns 16.05.2015 1 ASP.NET

Transcript of .Net Framework и С#, весна 2015: ASP.NET WEB Patterns

Толстиков Никита

[email protected]

ASP.NET WEB Patterns

16.05.2015 1ASP.NET

План лекции

• Паттерн репозиторий

• Валидация моделей

• Entity Mapping

• IoC контейнеры и DI

• Авторизация и аутентификация

16.05.2015 Толстиков Никита 2ASP.NET

Паттерн репозиторий

• Репозиторий – уровень абстракции между бизнесс логикой и контекстом базы данных

• Репозиторий – контролирует доступ к одному виду сущностей

• UnitOfWork – отвечает за то что бы все репозитории использовали один DbContext

16.05.2015 Толстиков Никита 3ASP.NET

Реализация

16.05.2015 Толстиков Никита 4ASP.NET

public interface IRepository<TEntity> : IDisposablewhere TEntity : class

{IEnumerable<TEntity> Get(

Expression<Func<TEntity, bool>> filter = null,string includeProperties = "",Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null);

TEntity GetById(object id);void Insert(TEntity entity);void Delete(object id);void Delete(TEntity entityToDelete);void Update(TEntity entityToUpdate);

}

Реализация

16.05.2015 Толстиков Никита 5ASP.NET

public class GenericRepository<TEntity> : IRepository<TEntity> where TEntity : class{

internal MonstersContext context;internal DbSet<TEntity> dbSet;

public GenericRepository(MonstersContext context){

this.context = context;this.dbSet = context.Set<TEntity>();

}...

Реализация

16.05.2015 Толстиков Никита 6ASP.NET

public class GenericRepository<TEntity> : IRepository<TEntity> where TEntity : class{

internal MonstersContext context;internal DbSet<TEntity> dbSet;

public GenericRepository(MonstersContext context){

this.context = context;this.dbSet = context.Set<TEntity>();

}...

Реализация

16.05.2015 Толстиков Никита 7ASP.NET

public virtual IEnumerable<TEntity> Get(Expression<Func<TEntity, bool>> filter = null,Func<IQueryable<TEntity>,

IOrderedQueryable<TEntity>> orderBy = null,string includeProperties = "")

{IQueryable<TEntity> query = dbSet;if (filter != null){

query = query.Where(filter);}foreach (var includeProperty in includeProperties.Split

StringSplitOptions.RemoveEmptyEntries)){

query = query.Include(includeProperty);}if (orderBy != null) return orderBy(query).ToList();else query.ToList();

}

Реализация

16.05.2015 Толстиков Никита 8ASP.NET

public virtual void Delete(TEntity entityToDelete){

if (context.Entry(entityToDelete).State == EntityState.Detached){

dbSet.Attach(entityToDelete);}dbSet.Remove(entityToDelete);

}

public virtual void Update(TEntity entityToUpdate){

dbSet.Attach(entityToUpdate);context.Entry(entityToUpdate).State = EntityState.Modified;

}

Реализация

16.05.2015 Толстиков Никита 9ASP.NET

public virtual TEntity GetByID(object id){

return dbSet.Find(id);}

public virtual void Insert(TEntity entity){

dbSet.Add(entity);}

public virtual void Delete(object id){

TEntity entityToDelete = dbSet.Find(id);Delete(entityToDelete);

}

Реализация

16.05.2015 Толстиков Никита 10ASP.NET

public interface IMonsterRepository : IRepository{

double CalcAvverageLucky();}

public class MonsterRepository : GenericRepository<Monster>, IMonsterRepository

{public MonsterRepository(MonstersContext context) : base(context){}

public double CalcAvverageLucky(){

return DbSet.Sum(m => m.LuckyRate)/DbSet.Count();}

}

IUnitOfWork

16.05.2015 Толстиков Никита 11ASP.NET

public interface IUnitOfWork : IDisposable{

GenericRepository<Monster> DepartmentRepository { get; }GenericRepository<Weapoon> CourseRepository { get; }void Save();

}

UnitOfWork

16.05.2015 Толстиков Никита 12ASP.NET

public class UnitOfWork : IUnitOfWork{

private MonstersContext context = new MonstersContext();private GenericRepository<Monster> monsterRepository;private GenericRepository<Weapoon> weapoonRepository;

public GenericRepository<Weapoon> CourseRepository{

get{

if (this.courseRepository == null){

this.courseRepository = new GenericRepository<Weapoon>(context);}return courseRepository;

}}

public void Save(){

context.SaveChanges();}

Структура

16.05.2015 Толстиков Никита 13ASP.NET

Структура

16.05.2015 Толстиков Никита 14ASP.NET

Обработка исключений

16.05.2015 Толстиков Никита 15ASP.NET

Валидация моделей

using System.ComponentModel.DataAnnotations;

• Способы задания валидации:

– Аннотации

– IValidateObject

• Валидация проверяется классомModelState

• Валидация примитивных типов происходит автоматически

16.05.2015 Толстиков Никита 16ASP.NET

Validation Attributes

• Аттрибуты для проверки полей:– [Required] – поле обязательно для

заполнения

– [Compare] – сравнивает два поля

– [DataType] – задать специфичный тип (время, валюта, кредитная карта и т.д.)

– [StringLength] – диапазон длинны строки

– [EmailAddres] – правильный email

– [RegularExpression] – валидация по регулярному выражению

16.05.2015 Толстиков Никита 17ASP.NET

ValidationAttribute

16.05.2015 Толстиков Никита 18ASP.NET

public abstract class ValidationAttribute : Attribute{

//проверяет валидность значенияpublic virtual bool IsValid(object value)

//устанавливает формат сообщения об обшибке//name – имя поля классаpublic virtual string FormatErrorMessage(string name)

...}

ValidationAttribute

16.05.2015 Толстиков Никита 19ASP.NET

public class PositiveNumberAttribute : ValidationAttribute{

public override string FormatErrorMessage(string name){

return String.Format("{0} should be positive", name);}

public override bool IsValid(object value){

double doubleValue;if (value != null && double.TryParse(value.ToString(), out doubleValue)){

return doubleValue > 0;}else{

return false;}

}}

Валидация на клиенте

• Включить валидацию на клиенте

• Install-Package jQuery

• Подключить jQuery

16.05.2015 Толстиков Никита 20ASP.NET

<appSettings><add key="ClientValidationEnabled" value="true" /><add key="UnobtrusiveJavaScriptEnabled" value="true" />

</appSettings>

<script src="@Url.Content("~/Scripts/jquery-2.1.3.js")" type="text/javascript"></script>

IValidationObject

• Валидация определнной модели в

одном месте

16.05.2015 Толстиков Никита 21ASP.NET

public interface IValidatableObject{

IEnumerable<ValidationResult> Validate(ValidationContext validationContext);}

public class ValidationResult{

public ValidationResult(string errorMessage, IEnumerable<string> memberNames)}

AutoMapper

Install-Package AutoMapper

• Object-Object mapping framework

• Позволяет автоматически

преобразовывать объекты

• Сопоставление происходит на основе

имен

• Поддерживает Flattening

16.05.2015 Толстиков Никита 22ASP.NET

AutoMapper

• Создание преобразования:

• Использование преобразование:

• Собственное преобразование:

16.05.2015 Толстиков Никита 23ASP.NET

public interface IValueResolver{

ResolutionResult Resolve(ResolutionResult source);}

Mapper.CreateMap<LoginModel, User>();

var user = Mapper.Map<LoginModel, User>(model);

Mapper.CreateMap<LoginModel, User>().ForMember(dest => dest.Email,

opt => opt.ResolveUsing<CustomEmailResolver>());

Inversion of Control

• IoC – паттерн для устронения сильных

зависимостей в коде

• Основная идея: использовать только

интерфесы, а реализацию получать

различнмыми способами

• Dependency Injection – получение

реализации через конструктор

• Service Locator – получение реализации

через сервис

16.05.2015 Толстиков Никита 24ASP.NET

Inversion of Control

16.05.2015 Толстиков Никита 25ASP.NET

public class TextEditor{

private SpellChecker checker;public TextEditor(){

checker = new SpellChecker();}

}

Inversion of Control

• Dependency Injection:

• Service Locator:

16.05.2015 Толстиков Никита 26ASP.NET

public class TextEditor{

private ISpellChecker checker;public TextEditor(ISpellChecker checker){

this.checker = checker;}

}

public class TextEditor{

private ISpellChecker checker;public TextEditor(){

this.checker = ServiceLocator.Get<ISpellChecker>();}

}

Inversion of Control

• Плюсы:

– Ваш код избавляется от сильных

зависимостей

– Принудительная инкаспсуляция

– Легко писать юнит-тесты

• Минусы:

– Усложняет читаемость кода

– Новый тип багов свзанный с самим IOC

16.05.2015 Толстиков Никита 27ASP.NET

DI and .NET

• Основные отличия по релализации:

– Инициализация

– Получение объектов

• Реализации:

– Unity от Microsoft – поддерживает инициализацию через код и XML. Умеет создавать ServiceLocator

– Autofac – поддерживает код и XML.

– Ninject – поддерживает только код. Умеет внедрятся через аннотации

16.05.2015 Толстиков Никита 28ASP.NET

Ninject

Install-Package Microsoft.AspNet.WebApi.Client

Install-Package Microsoft.AspNet.WebApi.Core

Install-Package Ninject.MVC5

Install-Package Ninject.Web.Common.WebHost

• Создается файл NinjectWebCommon.cs

• В методе RegisterServices происходит

привязывание интерфейсов и

реализации

16.05.2015 Толстиков Никита 29ASP.NET

Ninject

16.05.2015 Толстиков Никита 30ASP.NET

/// <summary>/// Load your modules or register your services here!/// </summary>/// <param name="kernel">The kernel.</param>private static void RegisterServices(IKernel kernel){

kernel.Bind<IUnitOfWork>().To<UnitOfWork>().InRequestScope();}

public class LoginController : Controller{

public IUnitOfWork UOW { get; set; }

public LoginController(IUnitOfWork uow){

UOW = uow;}

Ninject

16.05.2015 Толстиков Никита 31ASP.NET

/// <summary>/// Load your modules or register your services here!/// </summary>/// <param name="kernel">The kernel.</param>private static void RegisterServices(IKernel kernel){

kernel.Bind<IUnitOfWork>().To<UnitOfWork>().InRequestScope();}

public class LoginController : Controller{

[Inject]public IUnitOfWork UOW { get; set; }...

Объекты областей в Ninject

• InTransientScope() - объект класса будет создаваться по каждому требованию (метод по умолчанию)

• InSingletonScope() - объект класса будет создан один раз и будет использоваться повторно

• InThreadScope() - один объект на поток

• InRequestScope() - один объект будет на каждый web-запрос

16.05.2015 Толстиков Никита 32ASP.NET

Авторизация и аутентификация

• ASP.NET MVC 5 поддерживает

несколько видов:

– Через ActiveDirectory

– Через Windows аккаунт

– Через Forms. Информация о пользователе

хранится в cookies

16.05.2015 Толстиков Никита 33ASP.NET

Авторизация и аутентификация

• ASP.NET MVC 5 поддерживает несколько видов:

– Через ActiveDirectory

– Через Windows аккаунт

– Через Forms

• Объект IPrincipal – основной объект для идентификации пользователя и его прав

• Объект IIdentity – идентификатор пользователя

16.05.2015 Толстиков Никита 34ASP.NET

Авторизация и аутентификация

• IPrincipal хранится в:

– HttpContext.User

– Thread.CurrentPrincipal

• Запись IPrincipal происходит в

IHttpModule на событие

AuthorizeRequest

• Для упрощения были введены GenericPrincipal

16.05.2015 Толстиков Никита 35ASP.NET

Авторизация и аутентификация

• IPrincipal хранится в:

– HttpContext.User

– Thread.CurrentPrincipal

• Запись IPrincipal происходит в IHttpModule на событие AuthorizeRequest

• Для упрощения были введены GenericPrincipal

• Для генерации cookie используется FormsAuthenticationTicket

16.05.2015 Толстиков Никита 36ASP.NET

Авторизация и аутентификация

• Cookies:

16.05.2015 Толстиков Никита 37ASP.NET

private const string AUTH_COOKIE_NAME = "a_id";

public static HttpCookie CookieFromLogin(string login, bool isPersistent){

var ticket = new FormsAuthenticationTicket(1,login,DateTime.Now, DateTime.Now.AddYears(1), isPersistent,string.Empty, FormsAuthentication.FormsCookiePath);

// Encrypt the ticket.var encTicket = FormsAuthentication.Encrypt(ticket);// Create the cookie.var authCookie = new HttpCookie(AUTH_COOKIE_NAME){

Value = encTicket,Expires = DateTime.Now.Add(FormsAuthentication.Timeout)

};return authCookie;

}}

Авторизация и аутентификация

• Cookies:

16.05.2015 Толстиков Никита 38ASP.NET

public static string LoginFromCookie(HttpCookieCollection cookies){

HttpCookie authCookie = cookies[AUTH_COOKIE_NAME];if (authCookie != null){

var ticket = FormsAuthentication.Decrypt(authCookie.Value);if (ticket != null) return ticket.Name;

}return String.Empty;

}

Авторизация и аутентификация

• AuthModule:

16.05.2015 Толстиков Никита 39ASP.NET

public class AuthModule : IHttpModule{

public IUnitOfWork UOW { get; set; }

public void Init(HttpApplication context){

context.AuthorizeRequest += Authorize;}

public void Dispose(){

}...

<system.webServer><modules><add name="AuthModule" type="HelloWorldMVC.Modules.AuthModule" /></modules>

</system.webServer>

Авторизация и аутентификация

• AuthModule:

16.05.2015 Толстиков Никита 40ASP.NET

private void Authorize(object sender, EventArgs eventArgs){

var application = (HttpApplication) sender;UOW = DependencyResolver.Current.GetService<IUnitOfWork>();string login = FormsAuthUtils.LoginFromCookie(application.Request.Cookies);if (!String.IsNullOrEmpty(login)){

var requestUser = UOW.UserRepository.Get(user => user.Login == login).FirstOrDefault();

if (requestUser != null){

application.Context.User = new GenericPrincipal(new GenericIdentity(requestUser.Login),

new string[]{});return;

}}application.Context.User = new GenericPrincipal(new GenericIdentity("Anonimus"),

new string[]{});}

The End

16.05.2015 Толстиков Никита 41ASP.NET