Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Применимо к: База данных SQL Azure
Это важно
Эластичные запросы в режиме диспетчера карт сегментов (горизонтальное секционирование), используя EXTERNAL DATA SOURCE тип SHARD_MAP_MANAGER, достигают конца поддержки 31 марта 2027 г. После этой даты существующие рабочие нагрузки будут продолжать функционировать, но больше не будут получать поддержку, а создание новых внешних источников данных типа SHARD_MAP_MANAGER больше не будет возможным. Сведения о параметрах миграции см. в руководстве по миграции из режима диспетчера сопоставления сегментов эластичных запросов.
В этом документе показаны изменения, которые следует внести в приложение Entity Framework для интеграции со средствами эластичных баз данных. Основное внимание уделяется совмещению методов управления картой сегментов и маршрутизации, зависящей от данных, с помощью подхода Entity Framework Code First. Руководство Code First — создание базы данных для Entity Framework используется в этом документе как пример. Примером кода для этого документа является часть набора примеров для эластичной базы данных в Visual Studio.
Примечание.
Эта статья неприменима к Entity Framework Core (EF Core).
Скачивание и запуск примера кода
Как загрузить код для этой статьи
- Требуется Visual Studio 2012 или более поздней версии.
- Скачайте пример средств эластичной базы данных для SQL Azure (интеграция с Entity Framework). Распакуйте образец в выбранную папку.
- Запустите Visual Studio.
- Откройте Visual Studio, выберите "Файл" -> "Открыть проект или решение".
- В диалоговом окне "Открыть проект " перейдите к примеру, который вы скачали, и выберите
EntityFrameworkCodeFirst.sln, чтобы открыть пример.
Чтобы запустить пример, необходимо создать три пустые базы данных в Базе данных SQL Azure:
- База данных диспетчера сопоставления сегментов
- База данных сегмента 1
- База данных сегмента 2
После создания этих баз данных заполните плейсхолдеры в Program.cs с использованием имени вашего сервера, имен баз данных и учетных данных для подключения к этим базам данных. Постройте решение в Visual Studio. В процессе построения Visual Studio скачает необходимые пакеты NuGet для клиентской библиотеки эластичной базы данных, платформы Entity Framework и обработки временных сбоев. Убедитесь, что для вашего решения включена функция восстановления пакетов NuGet. Этот параметр можно включить, щелкнув файл решения правой кнопкой мыши в обозревателе решений Visual Studio.
Рабочие процессы Entity Framework
Разработчики Entity Framework используют один из следующих четырех рабочих процессов для построения приложений и обеспечения сохранения объектов приложения.
- Code First (создание базы данных). Разработчик EF в коде приложения создает модель, и затем на ее основе платформа EF создает базу данных.
- Code First (имеющаяся база данных). Разработчик позволяет EF сформировать для модели код приложения на основе имеющейся базы данных.
- Model First. Разработчик создает модель в конструкторе EF, после чего EF создает базу данных на основе модели.
- Database First. Разработчик использует инструментарий EF для получения модели из имеющейся базы данных.
Во всех подходах класс DbContext используются для прозрачного управления подключениями к базе данных и схемой базы данных приложения. Различные конструкторы базового класса DbContext обеспечивают разный уровень управления при создании подключений и схем, а также при начальной загрузке базы данных. В основном сложности связаны с тем, что управление подключениями к базе данных, обеспечиваемое EF, пересекается с функциями управления подключением интерфейсов маршрутизации, зависящей от данных, предоставляемых клиентской библиотекой эластичной базы данных.
Допущения в отношении средств эластичной базы данных
Определения терминов см. в статье Глоссарий по средствам работы с эластичными базами данных.
При использовании клиентской библиотеки эластичной базы данных данные приложения разделяются на сегменты, называемые шардлетами. Шардлеты идентифицируются по ключу сегментирования и сопоставляются с определенными базами данных. Приложение может иметь столько баз данных, сколько необходимо, и распределить шарды, чтобы обеспечить достаточную производительность и емкость с учетом текущих требований бизнеса. Сопоставление значений ключей сегментирования с базами данных хранится в сопоставлении сегментов, которое предоставляют клиентские API-интерфейсы эластичной базы данных. Эта функция называется управлением размещением сегментов (или сокращенно SMM). Сопоставление сегментов также выступает в качестве посредника подключений к базе данных для запросов с ключом сегментирования. Эта функция называется маршрутизацией, зависящей от данных.
Диспетчер сопоставления сегментов защищает пользователей от получения несогласованных представлений данных шардлета, которые могут иметь место при выполнении параллельных операций управления шардлетом (например, при перемещении данных из одного сегмента в другой). Для предотвращения таких случаев сопоставления сегментов, управляемые клиентской библиотекой, устанавливают подключения к базе данных для приложения. Это позволяет функции карты сегментов автоматически убить подключение к базе данных, если операции управления сегментами могут повлиять на сегментлет, для которого было создано подключение. Этот подход должен интегрироваться с некоторыми функциями EF, такими как создание новых подключений из существующего, чтобы проверить наличие базы данных. В общем случае наши наблюдения показали, что стандартные конструкторы DbContext работают надежно только для закрытых подключений к базе данных, которые можно безопасно клонировать для работы в EF. В противоположность этому принцип эластичной базы данных заключается в использовании открытых подключений только через посредника. Можно подумать, что закрытие подключения под управлением клиентской библиотеки перед передачей его в EF DbContext может решить эту проблему. Однако, закрыв подключение и полагаясь на то, что EF откроет его снова, разработчик отказывается от проверок согласованности, выполняемых библиотекой. Однако, функции миграции в EF используют эти подключения для управления схемой базы данных таким образом, что это эти действия видны и в приложении. В идеальном случае необходимо сохранить и объединить возможности клиентской библиотеки эластичной базы данных и платформы EF в одном приложении. В следующем разделе эти свойства и требования описываются более подробно.
Требования
Во время работы одновременно с клиентской библиотекой эластичной базы данных и интерфейсами API Entity Framework цель заключается в том, чтобы сохранить следующие свойства.
- Горизонтальное масштабирование.Необходимо добавлять и удалять базы данных на уровне данных сегментированного приложения в соответствии с потребностями приложения в ресурсах. Это означает контроль создания баз данных и их удаления, а также использование интерфейсов API диспетчера сопоставления сегментов эластичной базы данных для управления базами данных и сопоставлениями шардлетов.
- Согласованность. В приложении применяется сегментирование и используются возможности клиентской библиотеки по маршрутизации, зависящей от данных. Чтобы избежать повреждения данных и получения неправильных результатов запросов, соединения проходят через посредника в виде диспетчера сопоставления сегментов. Это также позволяет использовать проверки и поддерживать согласованность данных.
- Код первое. Чтобы сохранить удобство кода EF в первой парадигме. В парадигме Code First классы приложения прозрачно сопоставляются со структурами базы данных. Код приложения взаимодействует с наборами DbSet, в которых упрятаны большинство используемых аспектов обработки базы данных.
- Схема.Entity Framework берет на себя создание начальной схемы базы данных и последующего развития схемы в ходе миграций. Если сохранить эти возможности, то адаптация приложения будет таким же простым как развитие данных.
В следующем руководстве показано, как с помощью средств эластичной базы данных достичь соответствия этим требованиям для приложений с приоритетом кода.
Маршрутизация, зависящая от данных, с использованием EF DbContext
Подключения к базе данных с Entity Framework обычно управляются подклассами DbContext. Создайте эти подклассы, производные от DbContext. Здесь вы определяете DbSets, которые реализуют коллекции объектов CLR для приложения, поддерживаемые базой данных. В контексте маршрутизации, зависящей от данных, можно выделить несколько полезных свойств, которые не обязательно будут применимы в других сценариях использования приложений EF Сode First.
- База данных уже существует и зарегистрирована в сопоставлении сегментов эластичной базы данных.
- Схема приложения уже развернута в базу данных (как описано ниже).
- Подключение маршрутизации на основе данных к базе данных устанавливается через сопоставление сегментов.
Чтобы интегрировать DbContexts с маршрутизацией, зависящей от данных, в целях масштабирования системы горизонтально:
- Создать подключения к физическим базам данных через клиентские интерфейсы эластичной базы данных диспетчера сопоставления сегментов.
- Оберните соединение подклассом
DbContext - Передайте подключение
DbContextв базовые классы, чтобы гарантировать выполнение всех процессов также на стороне EF.
Этот подход показан в следующем примере кода. (Этот код также есть в соответствующем проекте Visual Studio)
public class ElasticScaleContext<T> : DbContext
{
public DbSet<Blog> Blogs { get; set; }
...
// C'tor for data-dependent routing. This call opens a validated connection
// routed to the proper shard by the shard map manager.
// Note that the base class c'tor call fails for an open connection
// if migrations need to be done and SQL credentials are used. This is the reason for the
// separation of c'tors into the data-dependent routing case (this c'tor) and the internal c'tor for new shards.
public ElasticScaleContext(ShardMap shardMap, T shardingKey, string connectionStr)
: base(CreateDDRConnection(shardMap, shardingKey, connectionStr),
true /* contextOwnsConnection */)
{
}
// Only static methods are allowed in calls into base class c'tors.
private static DbConnection CreateDDRConnection(
ShardMap shardMap,
T shardingKey,
string connectionStr)
{
// No initialization
Database.SetInitializer<ElasticScaleContext<T>>(null);
// Ask shard map to broker a validated connection for the given key
SqlConnection conn = shardMap.OpenConnectionForKey<T>
(shardingKey, connectionStr, ConnectionOptions.Validate);
return conn;
}
Основные положения
Новый конструктор заменяет конструктор по умолчанию подкласса DbContext
Новый конструктор принимает аргументы, которые требуются для маршрутизации, зависящей от данных, через клиентскую библиотеку эластичной базы данных.
- сопоставление сегментов для доступа к интерфейсам маршрутизации на основе данных,
- ключ сегментирования для определения шардлета,
- Строка подключения с учетными данными для подключения к сегменту через маршрутизацию на основе данных.
Вызов конструктора базового класса приводит к выполнению статического метода, реализующего все действия, необходимые для маршрутизации на основе данных.
- Конструктор использует вызов OpenConnectionForKey клиентских интерфейсов эластичной базы данных в сопоставлении сегментов для установления открытого подключения.
- Сопоставление создает открытое подключение к сегменту, в котором содержится шардлет, соответствующий заданному ключу сегментирования.
- Это открытое подключение передается обратно в конструктор базового класса DbContext, чтобы EF использовал его вместо автоматического создания нового подключения. Так подключение помечается клиентским API-интерфейсом эластичной базы данных, что гарантирует согласованность данных при выполнении операций по управлению сопоставлением сегментов.
Используйте в коде новый конструктор для подкласса DbContext вместо конструктора по умолчанию. Рассмотрим пример:
// Create and save a new blog.
Console.Write("Enter a name for a new blog: ");
var name = Console.ReadLine();
using (var db = new ElasticScaleContext<int>(
sharding.ShardMap,
tenantId1,
connStrBldr.ConnectionString))
{
var blog = new Blog { Name = name };
db.Blogs.Add(blog);
db.SaveChanges();
// Display all Blogs for tenant 1
var query = from b in db.Blogs
orderby b.Name
select b;
...
}
Новый конструктор открывает подключение к шарде, в которой хранятся данные для шардлета, определяемого значением tenantid1. Код в блоке using остается неизменным для доступа к блогам DbSet, использующим EF на шарде для tenantid1. Это изменяет семантику кода в блоке using, чтобы все операции базы данных теперь были ограничены одним сегментом, где tenantid1 хранится. Например, запрос LINQ по блогам вернет только блоги DbSet , хранящиеся в текущем сегменте, но не те, которые хранятся на других сегментах.
Обработка временных сбоев
Команда Microsoft Patterns & Practices опубликовала статью 4 - Perseverance, Secret of All Triumphs: Using the Transient Fault Handling Application Block (4. Настойчивость — секрет всех побед. Использование блока приложения для обработки временных ошибок). Библиотека используется с клиентской библиотекой эластичного масштабирования совместно с EF. Тем не менее обеспечьте возврат всех временных исключений в место, где после временного сбоя будет использоваться новый конструктор, чтобы все попытки установки нового подключения выполнялись с помощью измененных нами конструкторов. В противном случае не гарантируется установка подключения с правильным сегментом и нет гарантий, что подключение будет поддерживаться в случае изменения в сопоставлении сегментов.
В следующем примере кода показано, как можно использовать политику повторных попыток SQL вокруг новых DbContext конструкторов подклассов:
SqlDatabaseUtils.SqlRetryPolicy.ExecuteAction(() =>
{
using (var db = new ElasticScaleContext<int>(
sharding.ShardMap,
tenantId1,
connStrBldr.ConnectionString))
{
var blog = new Blog { Name = name };
db.Blogs.Add(blog);
db.SaveChanges();
...
}
});
SqlDatabaseUtils.SqlRetryPolicy в примере кода определяется как SqlDatabaseTransientErrorDetectionStrategy с количеством повторных попыток 10 и временем ожидания 5 секунд между попытками. Такой подход согласуется с рекомендациями для EF и транзакций, инициированных пользователями. См. статью Entity Framework (Ограничения Entity Framework в стратегиях с повторами попыток (далее EF6)). В обоих случаях требуется, чтобы приложение определяло область действия, куда возвращается временное исключение: чтобы снова открыть транзакцию или (как показано) повторно создать контекст из подходящего конструктора, использующего клиентскую библиотеку эластичной базы данных.
Необходимость контролировать, где временные исключения возвращают нас в область действия, также исключает использование встроенной SqlAzureExecutionStrategy , которая поставляется с EF.
SqlAzureExecutionStrategy откроет подключение повторно, но OpenConnectionForKey не будет использоваться и, поэтому, будет обходить все проверки, которые выполняются в рамках вызова OpenConnectionForKey. Вместо этого в примере кода используется встроенная DefaultExecutionStrategy, которая также идет в комплекте с EF. В отличие от SqlAzureExecutionStrategy, он работает правильно в сочетании с политикой повторных попыток обработки временных сбоев. Политика выполнения устанавливается в ElasticScaleDbConfiguration классе. Мы решили не использовать DefaultSqlExecutionStrategy, так как он предлагает использовать SqlAzureExecutionStrategy в случае временных исключений, что, как обсуждалось, приведет к неправильному поведению. Дополнительные сведения о различных политиках повтора и EF см. в статье Entity Framework Connection Resiliency and Retry Logic (EF6 onwards) (Устойчивость подключения и логика повторных попыток Entity Framework (далее EF6)).
Переделка конструктора
Вышеприведенные примеры кода показывают переделку конструктора по умолчанию, необходимую для приложения, использующего маршрутизацию, зависящую от данных, с платформой Entity Framework. Следующая таблица обобщает этот подход для других конструкторов.
| Текущий конструктор | Переделанный конструктор для данных | Базовый конструктор | Примечания. |
|---|---|---|---|
MyContext() |
ElasticScaleContext(ShardMap, TKey) |
DbContext(DbConnection, bool) |
Подключение должно быть функцией сопоставления карты сегментов и ключа маршрутизации на основе данных. Нужно обойти автоматическое создание подключения в EF и вместо этого использовать сопоставление сегментов в качестве посредника подключения. |
MyContext(string) |
ElasticScaleContext(ShardMap, TKey) |
DbContext(DbConnection, bool) |
Подключение является функцией сопоставления карты сегментов и ключа маршрутизации на основе данных. Фиксированное имя базы данных или строка подключения не сработает, так как они обходят проверку в сопоставлении сегментов. |
MyContext(DbCompiledModel) |
ElasticScaleContext(ShardMap, TKey, DbCompiledModel) |
DbContext(DbConnection, DbCompiledModel, bool) |
Подключение будет создано для заданного сопоставления сегментов и ключа сегментирования по предоставленной модели. Скомпилированная модель передается базовому конструктору. |
MyContext(DbConnection, bool) |
ElasticScaleContext(ShardMap, TKey, bool) |
DbContext(DbConnection, bool) |
Подключение должно быть зависимым от карты сегментов и ключа. Оно не может поступать в качестве входного аргумента (если этот аргумент ранее не был получен с помощью сопоставления сегментов и ключа). Передается логическое значение. |
MyContext(string, DbCompiledModel) |
ElasticScaleContext(ShardMap, TKey, DbCompiledModel) |
DbContext(DbConnection, DbCompiledModel, bool) |
Подключение должно быть зависимым от карты сегментов и ключа. Оно не может поступать в качестве входного аргумента (если этот аргумент не был получен с помощью сопоставления сегментов и ключа). Передается скомпилированная модель. |
MyContext(ObjectContext, bool) |
ElasticScaleContext(ShardMap, TKey, ObjectContext, bool) |
DbContext(ObjectContext, bool) |
Новый конструктор должен обеспечить перенаправление подключений в ObjectContext, переданного в качестве входных данных, на подключение, которым управляет Elastic Scale. Подробное рассмотрение ObjectContext выходит за рамки данного документа. |
MyContext(DbConnection, DbCompiledModel, bool) |
ElasticScaleContext(ShardMap, TKey, DbCompiledModel, bool) |
DbContext(DbConnection, DbCompiledModel, bool); |
Подключение должно быть зависимым от карты сегментов и ключа. Подключение не может поступать в виде входного аргумента (если этот аргумент ранее не был получен с помощью сопоставления сегментов и ключа). Модель и логическое значение передаются конструктор базового класса. |
Развертывание схемы сегментирования через миграции EF
Автоматическое управление схемой – одно из удобств, предоставляемых платформой Entity Framework. В контексте приложения с использованием инструментов эластичной базы данных желательно сохранить эту возможность автоматической подготовки схемы для вновь созданных сегментов при добавлении баз данных в сегментированное приложение. Основной случай использования – увеличение емкости уровня данных для сегментированных приложений, использующих EF. Использование возможностей EF для управления схемами сокращает усилия администрирования базы данных с сегментированных приложений, созданных на основе EF.
Развертывание схемы с помощью миграций EF лучше всего работает для неоткрытых подключений. Это отличается от сценария с маршрутизацией, зависящей от данных, основой которого выступает открытое подключение, предоставляемое клиентским API эластичной базы данных. Другим отличием является требование к непрерывности. Хотя желательно обеспечить согласованность всех подключений с маршрутизацией на основе данных для защиты от параллельно выполняемых операций с сопоставлениями сегментов, это не является проблемой при начальном развертывании схемы в новой базе данных, которая еще не зарегистрирована в сопоставлении сегментов и не выделена для размещения шардлетов. Таким образом для этих сценариев можно полагаться на обычные подключения к базе данных в противовес маршрутизации, зависящей от данных.
Это приводит к тому, что развертывание схемы с помощью миграций EF тесно связано с регистрацией новой базы данных в виде сегмента в карте сегментов приложения. При этом подразумевается выполнение следующих условий:
- База данных уже создана.
- База данных пуста — в ней нет пользовательской схемы и данных.
- База данных еще недоступна для маршрутизации на основе данных через клиентский API-интерфейс эластичной базы данных.
С учетом этих предварительных условий, вы можете создать обычный нераскрытый SqlConnection для инициирования миграции EF, необходимой для развертывания схемы. Этот подход показан в следующем примере кода.
// Enter a new shard - i.e. an empty database - to the shard map, allocate a first tenant to it
// and kick off EF initialization of the database to deploy schema
public void RegisterNewShard(string server, string database, string connStr, int key)
{
Shard shard = this.ShardMap.CreateShard(new ShardLocation(server, database));
SqlConnectionStringBuilder connStrBldr = new SqlConnectionStringBuilder(connStr);
connStrBldr.DataSource = server;
connStrBldr.InitialCatalog = database;
// Go into a DbContext to trigger migrations and schema deployment for the new shard.
// This requires an un-opened connection.
using (var db = new ElasticScaleContext<int>(connStrBldr.ConnectionString))
{
// Run a query to engage EF migrations
(from b in db.Blogs
select b).Count();
}
// Register the mapping of the tenant to the shard in the shard map.
// After this step, data-dependent routing on the shard map can be used
this.ShardMap.CreatePointMapping(key, shard);
}
В этом примере показан метод RegisterNewShard , который регистрирует сегмент в карте сегментов, развертывает схему с помощью миграций EF и сохраняет сопоставление ключа сегментирования с сегментом. Он использует конструктор подкласса DbContext (ElasticScaleContext в примере), который принимает строку подключения SQL в качестве входных данных. Код этого конструктора довольно прост, как показывает следующий пример.
// C'tor to deploy schema and migrations to a new shard
protected internal ElasticScaleContext(string connectionString)
: base(SetInitializerForConnection(connectionString))
{
}
// Only static methods are allowed in calls into base class c'tors
private static string SetInitializerForConnection(string connectionString)
{
// You want existence checks so that the schema can get deployed
Database.SetInitializer<ElasticScaleContext<T>>(
new CreateDatabaseIfNotExists<ElasticScaleContext<T>>());
return connectionString;
}
Можно использовать версию конструктора, унаследованную от базового класса. Однако при подключении код должен обеспечить использование для EF инициализатора по умолчанию. Поэтому присутствует вызов статического метода перед вызовом конструктора базового класса со строкой подключения. Регистрация сегментов должна выполняться в другом домене приложения или процессе, чтобы гарантировать, что параметры инициализатора для EF не конфликтуют.
Ограничения
Подходы, описанные в данном документе, имеют несколько ограничений:
- Приложения EF, использующие
LocalDbсначала необходимо перенести в обычную базу данных SQL Server перед использованием клиентской библиотеки эластичной базы данных. Масштабирование приложения через шардинг с использованием Elastic Scale невозможноLocalDb. Разработка все еще может использоватьLocalDb. - Все изменения в приложении, которые подразумевают изменение схемы базы данных, должны проходить через миграции EF на всех сегментах. В примере кода для этого документа не показано как делается. Рассмотрите возможность использования операции Update-Database с параметром ConnectionString для перебора всех сегментов. Или извлеките сценарий T-SQL для ожидающейся миграции с помощью Update-Database с параметром -Script и примените этот сценарий к сегментам.
- Предполагается, что вся обработка в базе данных для выполнения запроса ведется в пределах одного сегмента, который идентифицируется ключом сегментирования, предоставленным в запросе. Однако это предположение не всегда является верным. Например, в случае когда невозможно предоставить ключ сегментирования. Для решения этой проблемы клиентская библиотека предоставляет
MultiShardQueryкласс, реализующий абстракции подключения для запроса по нескольким сегментам. Обучение использованиюMultiShardQueryв сочетании с EF выходит за рамки этого документа
Заключение
Выполнив действия, описанные в этом документе, приложения EF могут использовать возможность клиентской библиотеки эластичной базы данных для маршрутизации на основе данных путем рефакторинга конструкторов подклассов DbContext используемых в приложении EF. Это ограничивает изменения, необходимые для тех мест, где DbContext уже существуют классы. Кроме того, приложения EF могут продолжать использовать преимущества автоматического развертывания схемы, объединив действия, вызывающие необходимые миграции EF, с регистрацией новых сегментов и сопоставлений в сопоставлении сегментов.
Связанный контент
Еще не используете средства эластичных баз данных? Ознакомьтесь с нашим руководством по началу работы. Возникшие вопросы вы можете задать нам на странице вопросов Microsoft Q&A по Базе данных SQL. Что касается запросов новых функций, вы можете поделиться новыми идеями или проголосовать за существующие на форуме отзывов по Базе данных SQL.