Данное руководство устарело. Актуальное руководство: Руководство по ASP.NET Core
Первым уровнем, который мы реализуем, будет Data Access Layer или уровень доступа к данным. Этот уровень обычно содержит все модели данных, хранящихся в БД, а также классы, через которые идет взаимодействие с БД. И вначале создадим новый проект ASP.NET MVC без аутентификации. При этом решение назовем NLayerApp, а проект NLayerApp.WEB:
Проект NLayerApp.WEB будет представлять уровень представления.
После создания решения и проекта добавим в решение новый проект по типу Class Library, который назовем NLayerApp.DAL. Этот проект будет представлять уровень доступа к данным.
Первым делом определим модели, объекты которых будут храниться в бд. Для этого в проект добавим новую папку Entities, а в этой папке определим следующий класс модели Phone:
using System.Collections.Generic; namespace NLayerApp.DAL.Entities { public class Phone { public int Id { get; set; } public string Name { get; set; } public string Company { get; set; } public decimal Price { get; set; } public ICollection<Order> Orders { get; set; } } }
И модели Order:
using System; namespace NLayerApp.DAL.Entities { public class Order { public int Id { get; set; } public decimal Sum { get; set; } public string PhoneNumber { get; set; } public string Address { get; set; } public int PhoneId { get; set; } public Phone Phone { get; set; } public DateTime Date { get; set; } } }
Теперь добавим в проект через NuGet пакет Entity Framework и также определим в проекте новую папку, которая пусть будет называться EF. В эту папку добавим класс контекста данных:
using System.Collections.Generic; using System.Data.Entity; using System.Threading.Tasks; using NLayerApp.DAL.Entities; namespace NLayerApp.DAL.EF { public class MobileContext : DbContext { public DbSet<Phone> Phones { get; set; } public DbSet<Order> Orders { get; set; } static MobileContext() { Database.SetInitializer<MobileContext>(new StoreDbInitializer()); } public MobileContext(string connectionString) : base(connectionString) { } } public class StoreDbInitializer : DropCreateDatabaseIfModelChanges<MobileContext> { protected override void Seed(MobileContext db) { db.Phones.Add(new Phone { Name = "Nokia Lumia 630", Company = "Nokia", Price = 220 }); db.Phones.Add(new Phone { Name = "iPhone 6", Company = "Apple", Price = 320 }); db.Phones.Add(new Phone { Name = "LG G4", Company = "lG", Price = 260 }); db.Phones.Add(new Phone { Name = "Samsung Galaxy S 6", Company = "Samsung", Price = 300 }); db.SaveChanges(); } } }
Как и в проекте с монолитной архитектурой здесь также используется инициализатор БД, который теперь выполняется в статическом конструкторе контекста. Кроме того, для строки подключения конструктор контекста принимает строковый параметр connectionString.
Для увеличения гибкости подключения к БД используются репозитории. Поэтому вначале определим в проекте еще одну папку Interfaces. И в нее добавим интерфейс репозиториев IRepository:
using System; using System.Collections.Generic; namespace NLayerApp.DAL.Interfaces { public interface IRepository<T> where T : class { IEnumerable<T> GetAll(); T Get(int id); IEnumerable<T> Find(Func<T, Boolean> predicate); void Create(T item); void Update(T item); void Delete(int id); } }
Поскольку мы будем использовать несколько репозитория для каждой сущности, то для упрощения использования подключения к бд будем использовать паттерн Unit Of Work. И для этого также в папку Interfaces добавим новый интерфейс IUnitOfWork:
using NLayerApp.DAL.Entities; using System; namespace NLayerApp.DAL.Interfaces { public interface IUnitOfWork : IDisposable { IRepository<Phone> Phones { get;} IRepository<Order> Orders { get;} void Save(); } }
Для хранения реализаций данных интерфейсов определим в проекте еще одну папку Repositories. Добавим в нее класс репозитория для смартфонов PhoneRepository:
using System; using System.Collections.Generic; using System.Linq; using NLayerApp.DAL.Entities; using NLayerApp.DAL.EF; using NLayerApp.DAL.Interfaces; using System.Data.Entity; namespace NLayerApp.DAL.Repositories { public class PhoneRepository : IRepository<Phone> { private MobileContext db; public PhoneRepository(MobileContext context) { this.db = context; } public IEnumerable<Phone> GetAll() { return db.Phones; } public Phone Get(int id) { return db.Phones.Find(id); } public void Create(Phone book) { db.Phones.Add(book); } public void Update(Phone book) { db.Entry(book).State = EntityState.Modified; } public IEnumerable<Phone> Find(Func<Phone, Boolean> predicate) { return db.Phones.Where(predicate).ToList(); } public void Delete(int id) { Phone book = db.Phones.Find(id); if (book != null) db.Phones.Remove(book); } } }
Также добавим в эту же папку класс репозитория для заказов OrderRepository:
using System; using System.Collections.Generic; using System.Data.Entity; using System.Linq; using NLayerApp.DAL.Entities; using NLayerApp.DAL.EF; using NLayerApp.DAL.Interfaces; namespace NLayerApp.DAL.Repositories { public class OrderRepository : IRepository<Order> { private MobileContext db; public OrderRepository(MobileContext context) { this.db = context; } public IEnumerable<Order> GetAll() { return db.Orders.Include(o => o.Phone); } public Order Get(int id) { return db.Orders.Find(id); } public void Create(Order order) { db.Orders.Add(order); } public void Update(Order order) { db.Entry(order).State = EntityState.Modified; } public IEnumerable<Order> Find(Func<Order, Boolean> predicate) { return db.Orders.Include(o => o.Phone).Where(predicate).ToList(); } public void Delete(int id) { Order order = db.Orders.Find(id); if (order != null) db.Orders.Remove(order); } } }
И также в папку Repositories добавим класс EFUnitOfWork:
using System; using NLayerApp.DAL.EF; using NLayerApp.DAL.Interfaces; using NLayerApp.DAL.Entities; namespace NLayerApp.DAL.Repositories { public class EFUnitOfWork : IUnitOfWork { private MobileContext db; private PhoneRepository phoneRepository; private OrderRepository orderRepository; public EFUnitOfWork(string connectionString) { db = new MobileContext(connectionString); } public IRepository<Phone> Phones { get { if (phoneRepository == null) phoneRepository = new PhoneRepository(db); return phoneRepository; } } public IRepository<Order> Orders { get { if (orderRepository == null) orderRepository = new OrderRepository(db); return orderRepository; } } public void Save() { db.SaveChanges(); } private bool disposed = false; public virtual void Dispose(bool disposing) { if (!this.disposed) { if (disposing) { db.Dispose(); } this.disposed = true; } } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } } }
Класс EFUnitOfWork в конструкторе принимает строку - названия подключения, которая потом будет передаваться в конструктор контекста данных. Собственно через EFUnitOfWork мы и будем взаимодействовать с базой данных.
В итоге структура проекта будет выглядеть следующим образом:
Собственно, и весь уровень доступа к данным, который мы могли бы сделать для проекта из прошлой темы, и теперь перейдем к построению бизнес-логики.