Как работать с entity framework в wpf

Обновлено: 09.05.2024

Expression Tree и его отличие от делегата

    — позволяет смотреть на код как на метаданные, производить динамические операции вызова, искать нужные методы и классы. — позволяет генерировать код на лету, напрямую используя промежуточный язык CLR — MSIL (IL) — предшественник Roslyn, способ скомпилировать куски кода на ходу. — API для кодогенерации и динамической компиляции кода.

Разницу между CodeDOM & Roslyn можно почитать тут. Возвращаясь к деревьям выражений — основная идея в том, что вы можете, как на ходу собрать кусок кода, руководствуясь какой-то логикой, так и проанализировать ранее собранный Expression, преобразовав его, например, в язык запросов к источнику данных (так работает Entity Framework). Также вы можете скомпилировать его и запустить на исполнение. Так вот, запись в форме Expression сразу создает дерево выражений на основе кода, который является анонимным делегатом. Кстати, деревья выражений иммутабельные, следовательно, к примеру для операций объединения предикатов через OR или AND — вам потребуется создать новое дерево, на основе двух старых. Эту проблему решают через ExpressionVisitor. Одна из лучших статей, которую я видел по этой теме.

Разница между Делегатом и событием (event)

Ключевое слово event ни что иное, как синтаксический сахар, позволяющий добавить дополнительное поведение свойству типа делегата. Проблема, которую синтаксически решают события — это сделать так, чтобы мы не могли случайно переопределить всю цепочку вызовов методов, добавленных к переменной типа делегата. Вы можете использовать псевдо методы (почти аналог геттера и сеттера) add & remove, за исключением что event будет оберткой вокруг переменной типа делегата. Также, если нам не нужны псевдо-методы add & remove ключевое слово event не позволяет сделать присвоение делегата извне класса, тем самым переписав всю цепочку вызовов.

Разница между локальными функциями и делегатами

Более подробно про отличия локальных функций и делегатов можете найти в моей предыдущей статье. Локальную функцию можно определить в конце метода, переменную делегата только перед использованием. Главное, что нужно понимать, локальная функция не является делегатом и не преобразуется в делегат во время использования, следовательно, не будет потреблять ресурсы в управляемой куче. В целом, локальные функции и делегаты имеют разные сферы применения. Локальные функции более уместны для выделения заново используемого куска кода внутри конкретного метода.

Какой LINQ синтаксис лучше использовать ?

  • Method chain, т.е. Цепочка методов
  • SQL Like, т.е. Псевдо sql код.

Лично я сторонник использования цепочки методов в большинстве случаев. Но некоторые выражения возможно реализовать только в SQL-like форме, а именно: использование let выражений. Так же можно имплементировать LEFT join & CROSS join в обеих формах, вопрос вкуса и читаемости кода:

Лично мне кажется, что sql-like форма тут более лаконична и понятна. В иных случаях, мне кажется, более целесообразно использовать форму записи цепочки методов.

Зачем нужны Properties & Fields

Property (свойство) — используется как внешний контракт класса Field (поле) — как приватный член класса. Интерфейсы синтаксически позволяют работать только со свойствами, так же объявляя для них псевдо методы get & set. Свойства могут быть virtual & override, но вот управлять приватностью псевдо методов в переопределенном свойстве в наследнике не выйдет.

Разница Readonly и const модификаторов

Const — на этапе компиляции производит inline подстановку значения. Readonly же защищает поле от изменения после выполнения конструктора. Следовательно, для того, чтобы значение из const подставилось в другие сборки — необходима их перекомпиляция.

Чем IQueryable отличается от IEnumerable

Чисто синтаксически — ничем, это одинаковые по сигнатуре интерфейсы. Но наличие интерфейса IQueryable у некоторого объекта подразумевает наличие, например extension методов, реализующих удаленный запрос к данным, используя этот объект. При работе с LINQ в Entity Framework, если вы напишите что-то вроде

То метод Where — будет взят из linq to entities, т.е. Произойдет вызов в базу данных, затем фильтрация. Если же вы используете Var customers = db.Customers.AsQueryable(); , то на IQueryable будет вызван extension-метод, который может построить дерево выражений и вернуть его для вызова другого extension-метода ToList, где будет запущен код вызова в базу данных

Тонкости работы с null

2. Оператор поглощения null

Тут в случае Prop = null мы поглотим это значение с помощью создания нового инстанса PropClass. 3. Оператор условной инициализации В случае если значение переменной null, мы производим инициализацию, в противном случае — нет. Например someList[0] ??= new PropClass(); выполнит присвоение первому элементу листа значением нового инстанса, если там был null. Довольно удобно пользоваться конструкциями, вроде a ?? b ?? c , либо d ??= e ??= f Код выше будет исполнен как:

Работа с исключениями

Рассмотрим вопрос — чем отличается проброс исключения из блока catch через Throw и throw e :

Также реализовать поведение, схожее с

сохраняя стек оригинального вызова, можно использовав класс ExceptionDispatchInfo, делая это в любом месте:

Тонкости работы с датой и временем

Работа с датой и временем всегда боль. Почти на каждом зрелом проекте я встречал баги, связанные с часовыми поясами или неправильной интерпретацией времени. Лично мое мнение (и не только), что хранение времени вместе с тайм зоной в классе DateTime чревато ошибками. Гораздо удобней хранить его либо в DateTimeOffset либо в Long, как Unix time, т.е. хранение даты и времени и работа с временем в целом в приложении должны быть отвязаны от часовых поясов (UTC), и они должны накладываться при необходимости в нашей бизнес-логике.

Ко и контр вариантность — это возможность производить неявное преобразование обобщенных параметров, в обобщенных делегатах и интерфейсах. Т.к. Ключевое слово out указывает, что на месте параметра T может быть сам класс так и его родительские классы, как в сигнатуре интерфейса IEnumerable:

Для ключевого слова in — наоборот, сам класс и его производные. Для демонстрации приведу такой кусок кода, где все сразу станет понятно:

Если бы не ключевые слова in & out в интерфейсе ISome — неявное преобразование типов не работало бы.

Default interface implementation vs Abstract class

Асинхронное программирование

Разница между Task и Thread

Класс Task является всего лишь моделью, переносящей состояние выполнения куска кода, переданного ей. За стратегию выполнения (т.е. определения потока выполнения) отвечает TaskScheduler, в случае если вы его не укажете явно — будет взят планировщик по-умолчанию (планировщик пула потоков). Класс Thread является объектной оберткой вокруг реального потока операционной системы, а также методами управления этим потоком. Поток, созданный с помощью инстанцирования класса Thread не будет помещен в пул потоков.

Что отличает вызовы await и .Result

  • Await — неблокирующий вызов, отпускающий вызывающий поток, ожидая результат.
  • .Result — блокирующий вызов, текущий поток будет удержан до завершения операции и возврата результата Таски.

Как работает Aggregate exception

Aggregate Exception — исключение, оборачивающее исключения в классе Task. Зачем оно было придумано ? Во-первых, задачи (Task) могут комбинироваться, где в результате будет получена новая задача, например методом WhenAll. Так вот, исключения всех участвующих задач будут обернуты в одно исключение AggregateException у задачи-результата этого вызова. Кстати, если вы захотите сделать await такой задачи, то будет выброшено только первое исключение.

Разница между async / await и Task API ContinueWith

Механизм async / await делает из метода стейт-машину под капотом, присоединяя продолжение после неблокирующего ожидания используя контекст синхронизации текущего потока как стратегию. ContinueWith никак не связан с async / await, производит продолжение цепочки тасок, когда таска в коллбеке этого метода завершена по какой-то причине. Кстати, по этой же причине если вы разместите внутри ContinueWith await какого-то метода, то после прерывания на неблокирующее ожидание, будет выполнен следующий ContinueWith, а затем только продолжение первого. Более подробно можно почитать в моей статье.

Вызов ConfigureAwait

Использование CancellationTokenSource

CancellationTokenSource (CTS) — фабрика CancellationToken’ов (CT). CT используется для передачи как параметр в Task при ее запуске, чтобы управлять механизмом согласованной отмены задачи. При вызове операции отмены в CTS (Cancel), мы можем использовать инстанс CT внутри кода таски чтобы проверить

  • Была ли отменена операция ? token.IsCancellationRequested, далее реализуя свою логику
  • Либо же вызывать время от времени ThrowIfCancellationRequested , который выбросит исключение, если на CTS произошла отмена. Главный нюанс здесь — если токен не был ассоциирован с Task и мы выбросили исключение — статус задачи будет Failed, в случае же ассоциации — статус будет Cancelled.

Многопоточность

Чем отличаются Monitor, Semaphore и Mutex

Порядок в понятиях : Task, TAP и TPL

Множество людей путают Task, TAP и TPL.

Работа CLR

Подписанные сборки и строгие имена

InternalsVisibleToAttribute

Для того, чтобы иметь возможность преодолеть ограничения модификаторов доступа, например, если нам нужно будет сослаться в тестовой сборке на internal класс — можно использовать атрибут InternalsVisibleToAttribute

И много чего еще. Вот неплохой доклад на эту тему.

В заключение

эта статья посвящена использованию средств данных в Visual Studio и не пытается объяснить основные технологии в любой глубине. Предполагается, что у вас есть базовые знания XAML, Entity Framework и SQL. В этом примере также не демонстрируется Архитектура Model-View-ViewModel (MVVM), которая является стандартом для приложений WPF. Однако этот код можно скопировать в собственное приложение MVVM с небольшими изменениями.

Установка и подключение к Northwind

Установите учебную базу данных Northwind, выполнив следующие действия.

в Visual Studio откройте окно обозреватель объектов SQL Server . (SQL Server обозреватель объектов устанавливается как часть рабочей нагрузки хранения и обработки данных в Visual Studio Installer.) разверните узел SQL Server . щелкните правой кнопкой мыши экземпляр LocalDB и выберите создать запрос.

Откроется окно редактора запросов.

скопируйте скрипт Transact-SQL Northwind в буфер обмена. этот сценарий T-SQL создает базу данных Northwind с нуля и заполняет ее данными.

вставьте скрипт T-SQL в редактор запросов, а затем нажмите кнопку выполнить .

По истечении короткого времени выполнение запроса завершается и создается база данных Northwind.

Настройка проекта

добавьте пакет NuGet для Entity Framework 6. В Обозреватель решений выберите узел проекта. в главном меню выберите Project > управление пакетами NuGet.

пункт меню "управление пакетами NuGet"

пакет NuGet Entity Framework

теперь можно использовать Visual Studio для создания модели на основе базы данных Northwind.

Создание модели

Новый элемент модели Entity Framework

Вызовите модель Northwind_model и нажмите кнопку ОК. Откроется мастер EDM . Выберите элемент конструктор EF из базы данных , а затем нажмите кнопку Далее.

Модель EF из базы данных

на следующем экране введите или выберите подключение LocalDB Northwind (например, (LocalDB) \MSSQLLocalDB), укажите базу данных northwind и нажмите кнопку далее.

На следующей странице мастера выберите таблицы, хранимые процедуры и другие объекты базы данных для включения в модель Entity Framework. Разверните узел dbo в представлении в виде дерева и выберите Customers, Orders и Order Details. Оставьте установленными значения по умолчанию и нажмите кнопку Готово.

Выбор объектов базы данных для модели

Файлы модели обозреватель решений EF

Область конструктора для файла EDMX позволяет изменять некоторые свойства и связи в модели. Мы не будем использовать конструктор в этом пошаговом руководстве.

Файлы . TT являются общим целям и необходимо настроить один из них для работы с DataBinding WPF, что требует обсерваблеколлектионс. В Обозреватель решений разверните узел Northwind_model, пока не найдете Northwind_model. TT. (Убедитесь, что вы не используете . Файл Context.tt , который находится непосредственно под EDMX -файлом.)

Замените первое вхождение HashSet<T> на ObservableCollection<T> около строки 51. Не заменяйте второй экземпляр hash.

Нажмите клавиши CTRL + SHIFT + B , чтобы построить проект. По завершении сборки классы модели видимы в мастере источников данных.

Теперь все готово для подключения этой модели к XAML-странице, чтобы можно было просматривать, перемещать и изменять данные.

Привязка модели к странице XAML

можно написать собственный код привязки данных, но гораздо проще позволить Visual Studio сделать это за вас.

в главном меню выберите Project > добавить новый источник данных , чтобы открыть мастер настройки источника данных. Выберите объект , так как вы привязываетесь к классам модели, а не к базе данных:

Мастер настройки источника данных с источником объекта

Разверните узел проекта и выберите Customer (клиент). (Источники для заказов автоматически формируются из свойства навигации Orders в Customer.)

Добавление классов сущностей в качестве источников данных

Перейдите к файлу MainWindow. XAML в представлении кода. Мы постоянно используем XAML для целей этого примера. Измените заголовок MainWindow на что-то более описательное и увеличьте его высоту и ширину до 600 x 800. Вы всегда можете изменить его позже. Теперь добавьте эти три определения строк в главную сетку, одну строку для кнопок навигации, одну для сведений клиента и одну для сетки, которая показывает их заказы:

Теперь откройте файл MainWindow. XAML , чтобы просмотреть его в конструкторе. в результате окно источники данных будет отображаться как параметр в Visual Studio поле окна рядом с областью элементов. щелкните вкладку, чтобы открыть окно, или нажмите клавиши Shift + Alt + D или выберите просмотреть > другие Windows > источники данных. Мы будем отображать каждое свойство в классе Customers в отдельном текстовом поле. Сначала щелкните стрелку в поле со списком Клиенты и выберите сведения. Затем перетащите узел в среднюю часть области конструктора, чтобы разработчик знал, что он должен находиться в средней строке. В случае его невозможности вручную указать строку в XAML. По умолчанию элементы управления размещаются вертикально в элементе Grid, но на этом этапе их можно расположить в форме. Например, может иметь смысл разместить текстовое поле Name сверху, над адресом. Пример приложения для этой статьи переупорядочивает поля и перемещает их в два столбца.

Привязка источника данных клиентов к отдельным элементам управления

В представлении кода теперь можно увидеть новый Grid элемент в строке 1 (средняя строка) родительской сетки. Родительская сетка имеет DataContext атрибут, который ссылается на CollectionViewSource, добавленный в Windows.Resources элемент. Учитывая этот контекст данных, при привязке первого текстового поля к адресу это имя сопоставляется со Address свойством в текущем Customer объекте в CollectionViewSource.

Когда клиент отображается в верхней половине окна, необходимо просмотреть заказы в нижней половине. Заказы отображаются в одном элементе управления представления сетки. Для работы привязки "основной — подробности" необходимо выполнить привязку к свойству Orders в классе Customers, а не к отдельному узлу Orders. Перетащите свойство Orders класса Customers в нижнюю половину формы, чтобы конструктор поместит его в строку 2:

Перетаскивание классов Orders в виде сетки

Visual Studio создала весь код привязки, который подключает элементы управления пользовательского интерфейса к событиям в модели. Для того чтобы увидеть некоторые данные, необходимо написать код для заполнения модели. Сначала перейдите к файлу MainWindow. XAML. CS и добавьте член данных в класс MainWindow для контекста данных. Этот объект, который был создан для вас, действует примерно так же, как элемент управления, отслеживающий изменения и события в модели. Вы также добавите элементы данных CollectionViewSource для клиентов и заказов, а также связанную логику инициализации конструктора. Начало класса должно выглядеть следующим образом:

Добавьте using директиву для System. Data. Entity, чтобы перевести метод расширения нагрузки в область:

Теперь прокрутите вниз и найдите Window_Loaded обработчик событий. обратите внимание, что Visual Studio добавил объект CollectionViewSource. Представляет объект Норсвиндентитиес, выбранный при создании модели. Вы уже добавили это, так что вам это не нужно. Выполним замену кода в Window_Loaded , чтобы метод теперь выглядел следующим образом:

Нажмите клавишу F5. Вы должны увидеть сведения для первого клиента, полученного в CollectionViewSource. Их заказы также должны отображаться в сетке данных. Форматирование не имеет ничего хорошего, так что давайте исправляется. Можно также создать способ просмотра других записей и выполнения базовых операций CRUD.

Настройка макета страницы и добавление сеток для новых клиентов и заказов

расположение по умолчанию, созданное Visual Studio, не идеально подходит для вашего приложения, поэтому мы предоставим конечный XAML-код, чтобы скопировать его в свой исходный текст. Также требуются некоторые «формы» (которые являются сетками), чтобы пользователь мог добавить нового клиента или заказ. Чтобы добавить нового клиента и заказ, необходим отдельный набор текстовых полей, не привязанных к данным CollectionViewSource . Вы можете указать, какую сетку пользователь видит в любой момент, установив свойство Visible в методах обработчика. Наконец, вы добавите кнопку Удалить в каждую строку сетки заказы, чтобы позволить пользователю удалить отдельный заказ.

Сначала добавьте эти стили в Windows.Resources элемент в файле MainWindow. XAML:

Затем замените всю внешнюю сетку этой разметкой:

Добавление кнопок для навигации, добавления, обновления и удаления

в Windows Forms приложениях вы получаете объект BindingNavigator с кнопками для навигации по строкам в базе данных и выполнения базовых операций CRUD. WPF не предоставляет BindingNavigator, но достаточно просто создать его. Это можно сделать с помощью кнопок в горизонтальном StackPanel и связать кнопки с командами, привязанными к методам в коде программной части.

Логика команды состоит из четырех частей: (1) команды, (2) привязки, (3) кнопки и (4) обработчики команд в коде программной части.

Добавление команд, привязок и кнопок в XAML

Сначала добавьте команды в файл MainWindow. XAML внутри Windows.Resources элемента:

CommandBinding сопоставляет RoutedUICommand событие с методом в коде программной части. Добавьте этот CommandBindings элемент после Windows.Resources закрывающего тега:

Теперь добавьте с помощью StackPanel кнопок навигации, добавления, удаления и обновления. Сначала добавьте этот стиль в Windows.Resources :

Во-вторых, вставьте этот код сразу после элемента RowDefinitions для внешнего Grid элемента в верхнюю часть страницы XAML:

Добавление обработчиков команд в класс MainWindow

Код программной части является минимальным, за исключением методов Add и DELETE. Навигация выполняется путем вызова методов для свойства View объекта CollectionViewSource. В этом DeleteOrderCommandHandler примере показано, как выполнить каскадное удаление в заказе. Сначала необходимо удалить связанные с ним Order_Details. UpdateCommandHandler Добавляет нового клиента или заказа в коллекцию или просто обновляет существующего клиента или заказ с учетом изменений, внесенных пользователем в текстовые поля.

Добавьте эти методы обработчика в класс MainWindow в MainWindow. XAML. CS. Если CollectionViewSource для таблицы Customers имеет другое имя, необходимо изменить имя в каждом из этих методов:

Выполнение приложения

Всем привет.Столкнулся с проблемой в том что не сохраняет изменения в базе данных(в таблице Books).Как надо изменить ViewModel что бы корректно сохранялись данные в бд?Может можно как нибудь привязать коллекцию книг из контекста к ObservableCollection<Book> Books в BookViewModel?Что бы при изменении ObservableCollection<Book> Books изменялась и соответствующая таблица(Book) в бд?

Добавлено через 5 часов 9 минут
Проблема все еще актуальна)Может у кого есть есть похожий код или ссылка на подобное решение? __________________
Помощь в написании контрольных, курсовых и дипломных работ здесь

Использование MVVM + Entity Framework
Добрый вечер! Хотел бы понять как работать по паттерну MVVM в связке с entity, есть некая проблема.

Entity Framework и WPF
Доброго вечера, форумчане! Вопрос где можно почитать (на русском языке) про работу с Entity.

Entity Framework и ObservableCollection
хочу понять как правильно использовать энтити модель для управления данными (сохранения.

Entity Framework Core + SQlite
У меня есть вот такая модель БД Делаю запрос в базу для объединения данных из всех таблиц .

Решение

Какого рода изменения? У тебя реализовано только добавление в базу данных новой записи и у тебя отсутствует вызов метода для сохранения состояния источника данных context.SaveChanges(). Для обновления объекта в базе данных следует добавить кнопку Update, в которой вызывать:

И да, для выгрузки данных из БД для дальнейшего их отображения, в конструкторе следует вызывать context.Books.ToList() и выгружать полученные данные в ObservableCollection.

все что ты сказал уже сделал,кроме кнопки апдейта. Сейчас попробую)

Добавлено через 4 минуты
В этом месте ругается
context.Books.SaveChanges(SelectedBook);

Error CS1061 'DbSet<Book>' does not contain a definition for 'SaveChanges' and no extension method 'SaveChanges' accepting a first argument of type 'DbSet<Book>' could be found

Да,я увидел,сижу пока разбираюсь как нормально этот метод к команде прикрутить. Плохо пока разбираюсь как правильно писать команды)

Добавлено через 3 часа 28 минут
спасибо за хелп,все работает)

Добавлено через 3 минуты
для тех кого это тоже будет интересовать:
прекрасно работает и без кнопки апдейта(после изменения комманд добавления и удаления).
так изменил класс BookViewModel:

Взаимосвязь Entity Framework и XAML
Доброго вечера. Столкнулся с очередной трудностью, которую не могу решить перечитывая разные.

Entity Framework и сортировка в DataGrid
Добрий день. Для соединения с DataGrid использываю Entity Framework, и передаю в ItemSourse свою.

Работа с Entity Framework Core
Никак не могу разобраться, как работать с EFCore в WPF. Может, плохо искал, поэтому, буду очень.

DataGrid или ListView + Entity Framework
Столкнулся со следующей проблемой (на примере DataGrid, с ListView аналогичная штука). В.


Свой метод с фильтром в Entity Framework
Добрый день. Смог создать модель DomainService.cs. В ней есть метод GetPeopes(). Возвращает всех.

Сегодня мы затронем тему использования паттерна Repository в своих приложениях для Entity Framework ( EF ). Идея написания статьи возникла после обнаружения ошибок в примерах реализации использования данного паттерна на одном из популярных курсов от pluralsight .

Паттерн Repository посредничает между уровнями области определения и распределения данных (domain and data mapping layers), используя интерфейс, схожий с коллекциями для доступа к объектам области определения. Такое определение базового понятия данного паттерна. Реализация данного паттерна подразумевает разделение вашего приложения на слои: слой данных ( domain data ) и слой, отвечающий за логику работы с доменными данными . Пример реализации приведен на рисунке ниже.



Далее для работы с базой данных (БД) нужно установить Entity Framework 6.0 ( EF ), для логирования используем NLog , а для работы через интерфейсы без жесткой привязки к реализации воспользуемся IoC контейнером Autofac . Пример установки Autofac с помощью Managed NuGet Packages можно посмотреть на рисунке ниже.


Создадим базовый класс для репозитория, вокруг которого будет построен весь каркас работы с БД. Для этого добавим новый класс, который назовем RepositoryBase.

public virtual TEntity Get<TEntity>( Expression < Func <TEntity, bool >> predicate) where TEntity : class

predicate.CheckNotNull( "Predicate value must be passed to Get<TResult>." );

return DataContext.Set<TEntity>().Where(predicate).SingleOrDefault();

public virtual IQueryable <TEntity> GetList<TEntity>( Expression < Func <TEntity, bool >> predicate) where TEntity : class

predicate.CheckNotNull( "Predicate value must be passed to GetList<TResult>." );

public virtual IQueryable <TEntity> GetList<TEntity, TKey>( Expression < Func <TEntity, bool >> predicate,

Expression < Func <TEntity, TKey>> orderBy) where TEntity : class

public virtual IQueryable <TEntity> GetList<TEntity, TKey>( Expression < Func <TEntity, TKey>> orderBy) where TEntity : class

public virtual IQueryable <TEntity> GetList<TEntity>() where TEntity : class

public virtual bool Save<TEntity>(TEntity entity) where TEntity : class

LoggerHelper .Logger.ErrorException( "Error saving " + typeof (TEntity) + "." , ex);

public virtual bool Update<TEntity>(TEntity entity, params string [] propsToUpdate) where TEntity : class

LoggerHelper .Logger.ErrorException( "Error saving " + typeof (TEntity) + "." , ex);

public virtual bool Delete<TEntity>(TEntity entity) where TEntity : class

ObjectSet <TEntity> objectSet = (( IObjectContextAdapter )DataContext).ObjectContext.CreateObjectSet<TEntity>();

LoggerHelper .Logger.ErrorException( "Error deleting " + typeof (TEntity), ex);

Для тех разработчиков, которые уже работали с БД с помощью EF , например, через Code First , Model First или Database First , инструкции, используемые в этом классе, не должны представлять каких-либо неудобств. Единственное, что для проверки значения, передаваемого в методы, я воспользовался extension методом CheckNotNull . Реализация этого метода приведена ниже.

public static void CheckNotNull( this object value, string error)

public virtual TEntity CreateNewEntity<TEntity>() where TEntity : class , new ()

Немного отвлекусь от темы, приведя один из неплохих способов построения приложения, если оно работает с несколькими БД или должно выполнять с базой несколько операций в одной транзакции. Для этого нужно добавить в репозиторий функции для работы с транзакциями или построить класс, который будет описывать уровень транзакции для ваших доменных моделей. Для такого случая необходимо добавить интерфейс, в котором объявить методы BeginTransaction () и CommitTransaction (). Затем в классе, который будет реализовывать этот интерфейс, добавить также наследование от нашего интерфейса RepositoryBase < T >. Работа с транзакциями не является основной для данной статьи, поэтому возвращаемся к работе с нашей моделью данных, или, другими словами, domain model . Необходимо создать слой для моделей. Для этого создадим папку Models в нашем проекте и реализуем наши классы, которые добавим в БД.


Осталось добавить эти модели в класс, наследуемый от DbContext , чтобы в БД создались соответствующие таблицы.

Теперь продемонстрируем, как это будет работать в действии.

using ( var repository = new RepositoryBase < OrderContext >())

Console .WriteLine( "Name = <0>, Order = " , order.Name, order.Price);

Осталось добавить логику по имплементации этого интерфейса.

public class CustomerRepository : RepositoryBase < OrderContext >, ICustomerRepository

return Get< Customer >(customer => customer.Address == address);

Классический пример использования данного репозитория выглядит следующим образом:

var findEntity = repository.FindByAddress( "1234567890" );

В этом примере есть существенная проблема, а именно: явный доступ к методу Delete с базового класса, что нарушает логику нашего репозитория, поскольку для конечного пользователя не должна быть доступна возможность удаления клиентов. В этом случае используем управление зависимостями с помощью установленного ранее IoC контейнера Autofac . С помощью Autofac мы сможем оперировать работой с интерфейсами без жесткой привязки к конкретной реализации этого интерфейса. Это позволяет легко тестировать наш интерфейс, используя моки и стабы. Реализация:

builder.RegisterType< CustomerRepository >().As< ICustomerRepository >();

var repository = container.Resolve< ICustomerRepository >();

Console .WriteLine( "Customer Address <0>" , customer.Address);

Читайте также: