9. MVC. Проектирование решении.pptx
- Количество слайдов: 41
MVC!!! Проектирование проекта 1
Начало = проблема ? 2
Проблемы организации проекта ● Слишком много проектов ● Слишком мало проектов ● Неверная абстракция 3
S Single responsibility principle Принцип единственной обязанности На каждый класс должна быть возложена одна-единственная обязанность. O Open/closed principle Принцип открытости/закрытости Программные сущности должны быть открыты для расширения, но закрыты для изменения. L Liskov substitution principle Принцип подстановки Барбары Лисков Функции, которые используют базовый тип, должны иметь возможность использовать подтипы базового типа, не зная об этом. I Interface segregation principle Принцип разделения интерфейса Много специализированных интерфейсов лучше, чем один универсальный. D Dependency inversion principle Принцип инверсии зависимостей Зависимости внутри системы строятся на основе абстракций. Модули верхнего уровня не зависят от модулей нижнего уровня. Абстракции не должны зависеть от деталей. Детали должны зависеть от абстракций 4
Общие принципы ● ● ● Принцип локализации изменений (common closure principle) Общий принцип повторного использования (common reuse principle) Принцип стабильности зависимостей(stable dependencies) Принцип стабильности абстракции Разделение ответственности (separation of concerns) Принцип инверсии зависимостей (Dependency Inversion Principle, DIP) + SOLID(Или нафиг ? : ) ) 5
Локализация изменений Классы которые меняются вместе - хранятся вместе 6
Принцип повторного использования Компоненты которые используются вместе - хранятся вместе. Компоненты которые НЕ используются вместе , не должны храниться вместе. 7
Стабильность зависимостей ● Граф зависимостей БЕЗ циклов ● Пакет должен зависеть только от пакетов, более стабильных, чем он сам нестабильность пакета - мера вероятности появления в нем изменений вследствие изменений других пакетов. 8
Принцип стабильности абстракция бесполезный не гибкий стабильность 9
Разделение ответственностей как можно меньше перекрывающие функции 10
Принцип инверсии зависимостей Абстракции не должны зависеть от деталей. Детали должны зависеть от абстракций. Нарушаем СОЛИД %) 11
Узнали принципы. И ? 12
N-layered архитектура 13
Правила ● каждый слой занимается конкретной задачей. ● логика разных слоёв не повторяется и не пересекается ● способ обращения к нижестоящему слою чётко определён ● способ поставки информации вышестоящему слою чётко определён ● слои расположены вертикально, хотя есть сквозная функциональность, которая может пронизывать пирамиду сверху вниз ● слои могут размещаться физически на одном компьютере ( в пределах одного уровня), а могут быть на разных машинах, например, в распределённых приложениях. ● логика разных слоёв инкапсулирована, соответственно, разным слоям не нужно делать никаких предположений о том, как реализован код других слоёв приложения. 14
У нас 90% используют многослойную архитектуру ? 15
Недостатки ● Разработка ведется снизу вверх (недостаток ? ? !? !? ) ● Изменения “снизу” могу вызывать проблемы наверху ● Разработчики часто путаются в какой из слоев запрограммировать (запихать) новый код: DAL, BLL ? ● Разработчик часто смотрит на архитектуру БД. Или это не недостаток ? ● Мб кто еще подскажет ? ) 16
Луковая архитектура 17
Рассмотрим пример. Проект на mvc 5 18
Теперь добавим в решение (не в проект) новую папку. Назовем ее Domain. Затем добавим в папку новый проект. В качестве типа проекта выберем тип Class Library, а в качестве его названия укажем Onion. App. Domain. Core: и добавим сюда класс: namespace Onion. App. Domain. Core { public class Book { public int Id { get; set; } public string Name { get; set; } public decimal Price { get; set; } } } 19
Затем добавим в папку Domain новый проект также по типу Class Library, а в качестве его названия укажем Onion. App. Domain. Interfaces. Затем добавим в этот проект ссылку на вышеопределенный проект Onion. App. Domain. Core и также добавим новый интерфейса: using System; using System. Collections. Generic; using Onion. App. Domain. Core; namespace Onion. App. Domain. Interfaces { public interface IBook. Repository: IDisposable { IEnumerable<Book> Get. Book. List(); Book Get. Book(int id); void Create(Book item); void Update(Book item); void Delete(int id); void Save(); } } 20
Очередь за уровнем Domain Services , добавим в этот проект интерфейс IOrder: using Onion. App. Domain. Core; namespace Onion. App. Services. Interfaces { public interface IOrder { void Make. Order(Book book); } } 21
Теперь перейдем к созданию внешнего уровня, который и будет реализовывать данные интерфейсы. Для этого добавим в решение папку Infrastructure и затем в нее добавим новый проект по типу Class Library, который назовем Onion. App. Infrastructure. Data. using System; System. Collections. Generic; System. Data. Entity; Onion. App. Domain. Core; namespace Onion. App. Infrastructure. Data { public class Order. Context : Db. Context { public Db. Set<Book> Books { get; set; } } } using using System; System. Collections. Generic; System. Linq; Onion. App. Domain. Core; Onion. App. Domain. Interfaces; System. Data. Entity; namespace Onion. App. Infrastructure. Data { public class Book. Repository : IBook. Repository { private Order. Context db; public Book. Repository() { this. db = new Order. Context(); } public IEnumerable<Book> Get. Book. List() { return db. Books. To. List(); } 22
Получим 23
Тоже самое для IOrder. using Onion. App. Domain. Core; using Onion. App. Services. Interfaces; namespace Onion. App. Infrastructure. Business { public class Cache. Order : IOrder { public void Make. Order(Book book) { // код покупки книги при оплате наличностью } } } using Onion. App. Domain. Core; using Onion. App. Services. Interfaces; namespace Onion. App. Infrastructure. Business { public class Credit. Order : IOrder { public void Make. Order(Book book) { // код покупки книги с помощью кредитной карты } } } 24
using Onion. App. Domain. Core; using Onion. App. Domain. Interfaces; using Onion. App. Services. Interfaces; namespace Onion. App. Controllers { public class Home. Controller : Controller { IBook. Repository repo; IOrder order; public Home. Controller(IBook. Repository r, IOrder o) { repo = r; order = o; } public Action. Result Index() { var books = repo. Get. Book. List(); return View(); } public Action. Result Buy(int id) { Book book = repo. Get. Book(id); order. Make. Order(book); return View(); } 25
namespace Onion. App. Util { public class Ninject. Dependency. Resolver : IDependency. Resolver { private IKernel kernel; public Ninject. Dependency. Resolver(IKernel kernel. Param) { kernel = kernel. Param; Add. Bindings(); } public object Get. Service(Type service. Type) { return kernel. Try. Get(service. Type); } public IEnumerable<object> Get. Services(Type service. Type) { return kernel. Get. All(service. Type); } private void Add. Bindings() { kernel. Bind<IBook. Repository>(). To<Book. Repository>(); kernel. Bind<IOrder>(). To<Cache. Order>(); } } } 26
27
28
Паттерн Unit of Work SQL Транзакции -> Бизнес транзакции 29
public class Book { public int Id { get; set; } public string Name { get; set; } public decimal Price { get; set; } } public class Order { public int Id { get; set; } public string Number { get; set; } public int Book. Id { get; set; } public Book { get; set; } } public class Order. Context : Db. Context { public Db. Set<Book> Books { get; set; } public Db. Set<Order> Orders { get; set; } } 30
interface IRepository<T> where T : class { IEnumerable<T> Get. All(); T Get(int id); void Create(T item); void Update(T item); void Delete(int id); } public class Book. Repository : IRepository<Book> { private Order. Context db; public Book. Repository(Order. Context context) { this. db = context; } public IEnumerable<Book> Get. All() { return db. Books; } public Book Get(int id) { return db. Books. Find(id); } public class Order. Repository : IRepository<Order> { private Order. Context db; public Order. Repository(Order. Context context) { this. db = context; } public IEnumerable<Order> Get. All() { return db. Orders. Include(o=>o. Book); } public Order Get(int id) { return db. Orders. Find(id); } public void Create(Order order) { db. Orders. Add(order); } 31
public class Unit. Of. Work : IDisposable { private Order. Context db = new Order. Context(); private Book. Repository book. Repository; private Order. Repository order. Repository; public void Save() { db. Save. Changes(); } private bool disposed = false; public Book. Repository Books { get { if (book. Repository == null) book. Repository = new Book. Repository(db); return book. Repository; } } public Order. Repository Orders { get { if (order. Repository == null) order. Repository = new Order. Repository(db); return order. Repository; } } public virtual void Dispose(bool disposing) { if (!this. disposed) { if (disposing) { db. Dispose(); } } this. disposed = true; } public void Dispose() { Dispose(true); GC. Suppress. Finalize(this); } } 32
using using System; System. Collections. Generic; System. Linq; System. Web. Mvc; Uo. WMvc. App. Models; namespace Uo. WMvc. App. Controllers { public class Home. Controller : Controller { Unit. Of. Work unit. Of. Work; public Home. Controller() { unit. Of. Work = new Unit. Of. Work(); } public Action. Result Index() { var books = unit. Of. Work. Books. Get. All(); return View(); } public Action. Result Create() { return View(); } [Http. Post] public Action. Result Create(Book b) { if(Model. State. Is. Valid) { unit. Of. Work. Books. Create(b); unit. Of. Work. Save(); return Redirect. To. Action("Index"); } return View(b); } public Action. Result Edit(int id) { Book b = unit. Of. Work. Books. Get(id); if (b == null) return Http. Not. Found(); return View(b); } [Http. Post] public Action. Result Edit(Book b) { if (Model. State. Is. Valid) { unit. Of. Work. Books. Update(b); unit. Of. Work. Save(); return Redirect. To. Action("Index"); 33
Unit of Work For ADO. NET using (Transaction. Scope _scope = new Transaction. Scope()) { using (Sql. Connection _con = new Sql. Connection(_connection. String)) { Бизнес транзакции } _scope. Complete(); } 34
AUTOMAPPER 35
public class User { public int Id { get; set; } public string Name { get; set; } public int Age { get; set; } public string Email { get; set; } } public class Index. User. View. Model { public int Id { get; set; } public string Name { get; set; } public int Age { get; set; } } public class Create. User. View. Model { public string First. Name { get; set; } public string Last. Name { get; set; } public int Age { get; set; } public string Login { get; set; } } // Настройка Auto. Mapper. Create. Map<User, Index. User. View. Model>(); // сопоставление var users = Mapper. Map<IEnumerable<User>, List<Index. User. View. Model>>(repo. Get. All()); Mapper. Create. Map<Create. User. View. Model, User>(). For. Member("Name", opt => opt. Map. From(c => c. First. Name + " " + c. Last. Name)). For. Member("Email", opt => opt. Map. From(src => src. Login)); 36
Немного “точечных” советов 37
Отключите неиспользуемые движки представлений View. Engines. Clear(); View. Engines. Add(new Razor. View. Engine()); Избегайте передачи в представления null вместо модели Html. Text. Box. For(model => model. Name) - БДЫЩЬ!!!! public Action. Result Insert() { // return View(); <-- вот этот код передал бы null в качестве модели return View(new Product()); } 38
Сделать сессию Read. Only или выключить вообще. Отменяет блокировки обработчиков запросов. <!--No session state by default--> <configuration> <system. web> <pages enable. Session. State="false" /> </system. web> </configuration> <!--Read-only session state by default--> <configuration> <system. web> <pages enable. Session. State="Read. Only" /> </system. web> </configuration> 39
Выкладываемся в режиме RELEASE!!! : ) Кэшируем конроллеры [Output. Cache(Duration=10, Vary. By. Param="none")] Используем “тупые” view Если else, то htmlhelper Максимально используем чистый html в view 40
Используйте Async- контроллеры namespace Async. Contollers. Controllers { public class Home. Controller : Controller { Book. Context db = new Book. Context(); // синхронный public Action. Result Index() { IEnumerable<Book> books = db. Books; View. Bag. Books = books; return View(); } // асинхронный метод public async Task<Action. Result> Book. List() { IEnumerable<Book> books = await Task. Run(() => db. Books); View. Bag. Books = books; return View("Index"); } } } 41
9. MVC. Проектирование решении.pptx