.Net Framework и С#, весна 2015: ASP.NET WEB Patterns
Transcript of .Net Framework и С#, весна 2015: ASP.NET WEB Patterns
Толстиков Никита
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();}
Валидация моделей
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[]{});}