Download - Net Framework и С#, весна 2015: Data Sources
Реляционная БД
• Реляционная база данных — база
данных, основанная на реляционной
модели данных.
• Реляционность – это отношения (связи)
от англ. relation.
• Реляционная база данных – это таблицы
и отношения между ними
30.04.2015 Толстиков Никита 3ASP.NET
Таблица
• Таблица состоит из столбцов и строк.
• Столбцы имеют свойства – имя, тип данных.
• Таблицы должны обладать следующими свойствами:
– у таблицы есть имя (уникальное)
– нет двух одинаковых строк
– столбцы имеют разные наименования (нет двух одинаковых столбцов)
– порядок строк в таблице произвольный
• Структуру таблицы представдена в виде:– Имя столбца
– Тип данных для этого столбца
30.04.2015 Толстиков Никита 5ASP.NET
Связи
• Между таблицами существуют связи (relation).
• Для установки связи необходимо иметь следующее:
– Первичный ключ – это набор столбцов (атрибутов) таблицы, однозначно определяющих уникальность строки.
– Внешний ключ – это набор столбцов (атрибутов) таблицы, которые однозначно определяют уникальность строки в другой таблице.
• Прописана связь между первичным ключом и внешним ключом
30.04.2015 Толстиков Никита 6ASP.NET
Связи
• Связи бывают трех типов:
– Один-к-одному
– Один-ко-многим
– Многие-ко-многим
30.04.2015 Толстиков Никита 7ASP.NET
SQL Express LocalDB
• Начиная с версии Visual Studio 2012 в
комплекте устанавливается SQL Server
Express LocalDB
• В VisualStudio:
– VIEW -> Server Explorer (Ctrl+W,L) ->
DataConnections -> Add Connections.. ->
(localdb)\v11.0 (версия сервера)
30.04.2015 Толстиков Никита 9ASP.NET
SqlConnection
• System.Data.SqlClient.SqlConnection – BCL класс отвечающий за соединение с MsSqlServer
• Для соединения с другими базами данных поддерживающих OLE DB используйте OleDbConnection• SqlConnection – это ресурс
• Для ускорения работы используется пул соединений
• Для подключения к базе данных необходима ConnectionString
• Соединение не всегда открыто при создании
30.04.2015 Толстиков Никита 10ASP.NET
ConnectionString
• Строка содержащая информацию о том с чем и как установить соединение
• Представляет из себя пары ключ=знчение;
• Основные параметры:– Server или Data Source – адрес СУБД
– Database – имя базы данных
– Integrated Security – использовать или нет windows аккаунт
– User Id – имя аккаунта на сервере
– Password – пароль от аккаунта
• Полный список параметров здесь
• Список параметров может меняться от версии к версии
30.04.2015 Толстиков Никита 11ASP.NET
@"Server=(localdb)\v11.0;Database=test_monsters_db;
Integrated Security=true"
SqlConnection
30.04.2015 Толстиков Никита 12ASP.NET
var connection = new System.Data.SqlClient.SqlConnection(@"Server=(localdb)\v11.0;Database=test_monsters_db;Integrated Security=true");
using (connection){
connection.Open();Console.WriteLine(connection.State);
}
SqlCommand
• SqlCommand – класс ответственный за выполнение команд через соединение
• Свойство CommandType определяет тип:– CommandType.Text Sql текст. (Default.)
– CommandType.StoredProcedure для вызова процедуры по имени
– CommandType.TableDirect для получения всей таблицы(OleDb)
• 3 способа исполнить команду:– ExecuteNonQuery – возвращает кол-во измененных строк
– ExecuteScalar – возвращает один объект в ячейке [0,0]таблицы результата
– ExecuteReader – возвращает таблицу результата обернутуюSqlDataReader
• SqlCommand для исполнения ad hoc sql поддерживает параметры
30.04.2015 Толстиков Никита 13ASP.NET
SqlCommand
30.04.2015 Толстиков Никита 14ASP.NET
var connection = new SqlConnection(@"Server=(localdb)\v11.0;Database=test_monsters_db;Integrated Security=true");
using (connection){
connection.Open();Console.WriteLine("Connection state: {0}",
connection.State);var command = new SqlCommand(
"insert into dbo.Monstersvalues ('MegaLuckyMonster', 1, 100, '2010.1.1')",connection);
using (command){
int updated = command.ExecuteNonQuery();Console.WriteLine("Updated {0} rows", updated);
}}
SqlDataReader
• SqlDataReader – класс инкапсулирующий результирующую таблицу
• Ресурс
• Содержит кол-во столбцов в таблице результате
• В один момент времени хранит только одну строчку
• Для получения следующей строчки нужно выполнить Read(), которая проинициализирует реадер и вернет true, или вернет false если строк не осталось
30.04.2015 Толстиков Никита 15ASP.NET
SqlDataReader
30.04.2015 Толстиков Никита 16ASP.NET
using (var command = new SqlCommand("select * from dbo.Monsters", connection)){
command.CommandType = CommandType.Text;using (var reader = command.ExecuteReader()){ //Columns Names
for (int i = 0; i < reader.FieldCount; i++){
Console.WriteLine("Column {0} - {1}", i, reader.GetName(i));
}while (reader.Read()){
Console.WriteLine("Monster: {0} {1} {2} {3}", (int)reader[0] //Monster Id
, (string)reader["Name"] //Monster Name, reader.GetDouble(3) //Monster LuckRate, reader.GetFieldValue<DateTime>(4) //Created Date);
}}
}
ORM
• Object-relational mapping – техника для
доступа к базам данных из ООП языков
программирования
30.04.2015 Толстиков Никита 17ASP.NET
ORM
• Существует множество реализаций
ORM для разных языков:
– Hibernate – Java
– Active Record – Ruby on Rails
– Doctrine – PHP
– SQLAclchemy – Python
30.04.2015 Толстиков Никита 18ASP.NET
ORM in .NET
• В С# удобно использовать ORM из-за
LINQ и query DSL
• Реализации:
– EntityFramework от Microsoft
– LinqToSql от Microsoft
– Data Access от Telerik
– NHibernate от open source
30.04.2015 Толстиков Никита 19ASP.NET
ORM
Отзывы об использовании ORM:
• Плюсы:
– Добавляет прослойку между СУБД и кодом
– Упрощает работу с базами данных
• Минусы:
– Добавляет накладные расходы
– Сложно оптимизировать
– Есть тонкие моменты
30.04.2015 Толстиков Никита 20ASP.NET
Entity Framework
• ORM Framework от Microsoft
• NuGet – менеджер пакетов для
VisualStudio
• Tools -> Library Package Manager -> Library
Package Manager Console
Install-Package EntityFramework
30.04.2015 Толстиков Никита 21ASP.NET
Entity Framework
• DbContext – основной класс никапсулирующий работу с базой данных
• Для работы с собственной базой нужно унаследоваться от него и перечислить объекты БД
• Сущность – это обычный класс
• DbContext – управляет таблицами, которые представлены как DbSet ваших табличных сущностей
30.04.2015 Толстиков Никита 22ASP.NET
Управление сущностями
• Управленеие сущностями происходит чере через DbSet’ы
• Интерфейс работы DbSet‘ов напоминает интерфейс работы с коллекциями
• DbSet реализует интерфейс IQuerable• SbContext – проксирует объекты для ускорения работы
• По умолчанию подгружаются только поля таблицы
• Для сохранения изменений нужно вызвать SaveChanges
30.04.2015 Толстиков Никита 23ASP.NET
Управление сущностями
• Add – добавляет сущность в БД
• Remove – удалить сущность из БД
• Attach – добавить сущность под контроль
DbContext
• Detach – явно удалить сущность из контекста
30.04.2015 Толстиков Никита 24ASP.NET
DbContext
• Существует 3 способа получить
DbContext:
– Создать все классы сущностей и наследника
DbContext в коде (Code First)
– Создать базу при помощи SQL и
сгенерировать необходимые классы в коде
(DB First)
– Создать модель, на основе которой
сгенерируется и то и другое (Model First)
30.04.2015 Толстиков Никита 25ASP.NET
Code First
• Создание сущностей:
– Типы из C# отображаются на типы SQL здесь
– Для регулирования сопоставления колонок
таблиц и полей класса можно использовать
DataAnnotations или FluentAPI
• Работа с контекстом:
– Можно управлять способом инициализации
БД для контекста через свойство
Database.SetInitializer
30.04.2015 Толстиков Никита 26ASP.NET
DataAnnotations
• Аттрибуты управляющие отображением в базу данных классов сущностей
• Находятся в System.ComponentModel.DataAnnotations• Основные:
– Key – определяет PRIMARY KEY таблицы. Может быть составным
– Required – аналогично NOT NULL
– MaxLength и MinLength мощность типа в байтах
– NotMapped – не отображать в БД
– ComplexType – создание под сущности в той же таблице
– ConcurrencyCheck и TimeStamp – позволяют использовать optimisctic concurency
– Table и Column – позволяют задать имена таблиц и колонок
– DatabaseGenerated – поле обновляется базой данных
– Index – создание индексов
– ForeignKey – FOREIGN KEY
30.04.2015 Толстиков Никита 27ASP.NET
FluentApi
• При соблюдении name conventions DataAnnotatinons покрывают практически все случаю отображения
• Для случаев которые не покрываются или вы не согласны с name conventions можно использовать FluentAPI
• Переопределение стандартного поведения происходит в OnModelCreating c использованием DbModelBuilder
• Полный список можно найти здесь
30.04.2015 Толстиков Никита 28ASP.NET
Миграция БД
• Процесс адаптации структуры БД к новой структре полей классов
• Этапы миграции:
– Создается модель базы на основе текущего кода
– Сравнивается модель текущего кода и модель базы последней миграции
– Генерируется код для обновления структуры БД
– Генерируеция новая модель миграции
30.04.2015 Толстиков Никита 29ASP.NET
Миграция БД
• История миграции находится в таблице
__MigrationsHistory
• Миграция:
– Enable-Migrations команда включающая
миграцию
– Add-Migration – создает код для миграции
– Update-Database – применяет последнюю
миграцию
30.04.2015 Толстиков Никита 30ASP.NET
Virtual поля
• Если поле сущности помеченно как
virtual, то EntityFramework может создать
класс наследника
• Это позволяет переопределить свойства
так, что бы сделать их ленивыми
• Такой подход ускоряет нахождение
изменений
30.04.2015 Толстиков Никита 31ASP.NET
ModelFirst
• Подход при котором EntityFramework сам
генерирует все классы на основе модели
• DbFirst Используется когда база данных
уже спроектирована и используется
• Такой подход позволяет разделить
работу на БД и работу над приложением
• Генерация происходит при помощи T4
шаблонов
30.04.2015 Толстиков Никита 32ASP.NET
Паттерн репозиторий
• Репозиторий – уровень абстракции между бизнесс логикой и контекстом базы данных
• Репозиторий – контролирует доступ к одному виду сущностей
• UnitOfWork – отвечает за то что бы все репозитории использовали один DbContext
30.04.2015 Толстиков Никита 34ASP.NET
Реализация
30.04.2015 Толстиков Никита 35ASP.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);
}
Реализация
30.04.2015 Толстиков Никита 36ASP.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>();
}...
Реализация
30.04.2015 Толстиков Никита 37ASP.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>();
}...
Реализация
30.04.2015 Толстиков Никита 38ASP.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();
}
Реализация
30.04.2015 Толстиков Никита 39ASP.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;
}
Реализация
30.04.2015 Толстиков Никита 40ASP.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);
}
Реализация
30.04.2015 Толстиков Никита 41ASP.NET
public interface IMonsterRepository{
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
30.04.2015 Толстиков Никита 42ASP.NET
public interface IUnitOfWork : IDisposable{
GenericRepository<Monster> DepartmentRepository { get; }GenericRepository<Weapoon> CourseRepository { get; }void Save();
}
UnitOfWork
30.04.2015 Толстиков Никита 43ASP.NET
public class UnitOfWork : IUnitOfWork{
private MonstersContext context = new MonstersContext();private GenericRepository<Monster> departmentRepository;private GenericRepository<Weapoon> courseRepository;
public GenericRepository<Weapoon> CourseRepository{
get{
if (this.courseRepository == null){
this.courseRepository = new GenericRepository<Weapoon>(context);}return courseRepository;
}}
public void Save(){
context.SaveChanges();}