Язык программирования C#9 и платформа .NET5
modelBuilder.Entity<CustomerOrderViewModel>(entity =>{entity.HasNoKey().ToView("CustomerOrderView","dbo");});Добавление хранилищ
Распространенный паттерн проектирования для доступа к данным называется "Хранилище" (Repository). Согласно описанию Мартина Фаулера (
) ядро этого паттерна является посредником между уровнями предметной области и сопоставления с данными. Наличие обобщенного хранилища, которое содержит общий код доступа к данным, помогает устранить дублирование кода. Наличие специфических хранилищ и интерфейсов, производных от базового хранилища, также хорошо подходит для работы с инфраструктурой внедрения зависимостей в ASP.NET Core.http://www.martinfowler.com/eaaCatalog/repository.htmlКаждая сущность предметной области внутри уровня доступа к данным
будет иметь строго типизированное хранилище для инкапсуляции всей работы по доступу к данным. Первым делом создайте в проектеAutoLotновый каталог по имениAutoLot.Dal, предназначенный для хранения всех классов.ReposНа заметку! Не воспринимайте следующий раздел как буквальную интерпретацию паттерна проектирования "Хранилище". Если вас интересует исходный паттерн, который послужил мотивом для создания приведенной здесь версии, тогда почитайте о нем по ссылке
.http://www.martinfowler.com/eaaCatalog/repository.htmlДобавление базового интерфейса IRepo
Базовый интерфейс
предоставляет множество общих методов, используемых при доступе к данным. Добавьте в проектIRepoновый каталог по имениAutoLot.Dalи создайте в нем еще один каталог под названиемRepos. Поместите в каталогBaseновый файл интерфейса по имениRepos\Base. Обновите операторыIRepo.cs, как показано ниже:usingusing System;using System.Collections.Generic;Так выглядит полный интерфейс:
namespace AutoLot.Dal.Repos.Base{public interface IRepo<T>: IDisposable{int Add(T entity, bool persist = true);int AddRange(IEnumerable<T> entities, bool persist = true);int Update(T entity, bool persist = true);int UpdateRange(IEnumerable<T> entities, bool persist = true);int Delete(int id, byte[] timeStamp, bool persist = true);int Delete(T entity, bool persist = true);int DeleteRange(IEnumerable<T> entities, bool persist = true);T? Find(int? id);T? FindAsNoTracking(int id);T? FindIgnoreQueryFilters(int id);IEnumerable<T> GetAll();IEnumerable<T> GetAllIgnoreQueryFilters();void ExecuteQuery(string sql, object[] sqlParametersObjects);int SaveChanges();}}Добавление класса BaseRepo
Добавьте в каталог
файл класса по имениRepos\Base. КлассBaseRepo.csбудет реализовывать интерфейсBaseRepoи предлагать основную функциональность для хранилищ, специфичных к типам (рассматриваются далее). Приведите операторыIRepoк следующему виду:usingusing System;using System.Collections.Generic;using System.Linq;using AutoLot.Dal.EfStructures;using AutoLot.Dal.Exceptions;using AutoLot.Models.Entities.Base;using Microsoft.EntityFrameworkCore;Сделайте класс обобщенным с типом
и добавьте к нему ограниченияТиBaseEntity, что сузит набор типов до классов, которые имеют конструктор без параметров. Реализуйте интерфейсnew():IRepo<T>public abstract class BaseRepo<T> : IRepo<T> where T : BaseEntity, new()Классу хранилища нужен экземпляр
, внедренный через конструктор. В случае использования с контейнером внедрения зависимостей ASP.NET Core временем жизни контекста будет управлять контейнер. Второй конструктор будет приниматьApplicationDbContextи должен создавать экземплярDbContextOptions, который понадобится освобождать. Поскольку этот класс является абстрактным, оба конструктора определяются как защищенные. Добавьте в открытый классApplicationDbContextследующий код:ApplicationDbContextprivate readonly bool _disposeContext;public ApplicationDbContext Context { get; }protected BaseRepo(ApplicationDbContext context){Context = context;_disposeContext = false;}protected BaseRepo(DbContextOptions<ApplicationDbContext> options) : this(newApplicationDbContext(options)){_disposeContext = true;