Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Модель поставщика Entity Framework позволяет Entity Framework использовать с разными типами сервера баз данных. Например, одного поставщика можно подключить, чтобы использовать EF с Microsoft SQL Server, а другого поставщика — для использования EF с Microsoft SQL Server Compact Edition. Поставщики EF6, о которых мы знаем, можно найти на странице поставщиков Entity Framework.
Некоторые изменения были необходимы для того, чтобы предоставить EF возможность взаимодействия с поставщиками и быть выпущенным в рамках лицензии с открытым исходным кодом. Эти изменения требуют перестройки поставщиков EF на сборки EF6, а также внедрения новых механизмов для регистрации поставщиков.
Перестройка
Начиная с версии EF6, основной код, который ранее был частью .NET Framework, теперь поставляется в виде внеосновных сборок (OOB). Сведения о том, как создавать приложения для EF6, можно найти на странице "Обновление приложений для EF6 ". Провайдеры также нужно будет перестроить с помощью этих инструкций.
Общие сведения о типах поставщиков
Поставщик EF — это действительно коллекция служб, специфичных для поставщика, определяемых типами CLR, которые эти службы расширяют (для базового класса) или реализуют (для интерфейса). Две из этих служб являются фундаментальными и необходимыми для работы EF вообще. Другие являются необязательными и должны быть реализованы только в том случае, если требуются определенные функциональные возможности и (или) реализации этих служб по умолчанию не работают для конкретного целевого сервера базы данных.
Основные типы поставщиков
DbProviderFactory
EF зависит от типа, производного от System.Data.Common.DbProviderFactory, для выполнения всех низкоуровневых операций доступа к базе данных. DbProviderFactory на самом деле не является частью EF, а представляет собой класс .NET Framework, который служит точкой входа для поставщиков ADO.NET, которые могут использоваться EF, другими O/RMs или непосредственно приложением для получения экземпляров соединений, команд, параметров и других абстракций ADO.NET в провайдер-независимом формате. Дополнительные сведения об DbProviderFactory см. в документации MSDN по ADO.NET.
DbProviderServices
EF зависит от типа, производного от DbProviderServices, для предоставления дополнительных функций, необходимых EF на основе функций, уже предоставляемых поставщиком ADO.NET. В более старых версиях EF класс DbProviderServices был частью платформы .NET Framework и находился в пространстве имен System.Data.Common. Начиная с EF6 этот класс теперь является частью EntityFramework.dll и находится в пространстве имен System.Data.Entity.Core.Common.
Дополнительные сведения об основных функциях реализации DbProviderServices можно найти на сайте MSDN. Однако обратите внимание, что на момент написания эта информация устарела для EF6, хотя большинство концепций всё ещё остаются актуальными. Реализации SQL Server и SQL Server Compact для DbProviderService также проверяются в базе кода с открытым исходным кодом и могут служить полезными ссылками для других реализаций.
В более ранних версиях EF реализация DbProviderServices, которую необходимо использовать, получалась непосредственно от провайдера ADO.NET. Это было сделано путем приведения DbProviderFactory к IServiceProvider и вызова метода GetService. EF поставщик был тесно связан с DbProviderFactory. Эта связь блокировала перемещение EF за пределы платформы .NET Framework, и по этой причине для EF6 эта жесткая связь была удалена, и реализация DbProviderServices теперь регистрируется непосредственно в файле конфигурации приложения или в конфигурации на основе кода, как подробно описано ниже в разделе Registering DbProviderServices.
Дополнительные услуги
Помимо основных служб, описанных выше, также существует множество других служб, используемых EF, которые всегда или иногда зависят от поставщика. Реализации этих служб, зависящие от поставщика, по умолчанию могут быть предоставлены реализацией DbProviderServices. Приложения также могут переопределять реализации этих служб или предоставлять свои собственные реализации, если тип DbProviderServices не предоставляет реализацию по умолчанию. Это подробно описано в разделе "Разрешение дополнительных служб " ниже.
Ниже перечислены дополнительные типы служб, которые могут заинтересовать поставщика. Дополнительные сведения о каждом из этих типов служб см. в документации по API.
IDbExecutionStrategy
Это необязательная служба, которая позволяет поставщику реализовать повторные попытки или другое поведение при выполнении запросов и команд в базе данных. Если реализация не указана, EF просто исполнит команды и распространит любое выброшенное исключение. Для SQL Server эта служба используется для предоставления политики повторных попыток, которая особенно полезна при выполнении на облачных серверах баз данных, таких как SQL Azure.
IDbConnectionFactory
Это необязательная служба, которая позволяет поставщику создавать объекты DbConnection по соглашению, если задано только имя базы данных. Обратите внимание, что хотя эта служба может быть разрешена реализацией DbProviderServices, она присутствует с EF 4.1 и может быть явно задана в файле конфигурации или в коде. Поставщик получит возможность урегулировать эту службу, только если он зарегистрирован в качестве поставщика по умолчанию (см. ниже Поставщик по умолчанию) и если фабрика подключений по умолчанию не была установлена в другом месте.
DbSpatialServices
Это необязательные службы, которые позволяют поставщику добавлять поддержку географических и геометрических пространственных типов. Для того чтобы приложение могло использовать EF с пространственными типами, необходимо предоставить реализацию данного сервиса. DbSpatialServices запрашивается двумя способами. Во-первых, пространственные службы, относящиеся к поставщику, запрашиваются с помощью объекта DbProviderInfo (который содержит инвариантное имя и маркер манифеста) в качестве ключа. Во-вторых, dbSpatialServices можно запросить без ключа. Это используется для разрешения "глобального пространственного поставщика", используемого при создании автономных типов DbGeography или DbGeometry.
MigrationSqlGenerator
Это необязательная служба, которая позволяет использовать миграции EF для генерации SQL, используемого при создании и изменении схем баз данных с помощью Code First. Для поддержки миграций требуется реализация. Если предоставляется реализация, она также будет использоваться при создании баз данных с помощью инициализаторов баз данных или метода Database.Create.
Func<DbConnection, string, HistoryContextFactory>
Это необязательная служба, которая позволяет поставщику настроить сопоставление HistoryContext с таблицей __MigrationHistory , используемой EF Migrations. HistoryContext — это Code First DbContext и может быть настроен с помощью обычного API fluent для изменения таких элементов, как имя таблицы и спецификации сопоставления столбцов. Реализация по умолчанию этой службы, возвращаемой EF для всех поставщиков, может работать для данного сервера базы данных, если все сопоставления таблиц и столбцов по умолчанию поддерживаются этим поставщиком. В таком случае поставщику не нужно предоставлять реализацию этой службы.
IDbProviderFactoryResolver
Это необязательная служба для получения корректного DbProviderFactory из заданного объекта DbConnection. Реализация этого сервиса по умолчанию, предоставляемая EF для всех поставщиков, предназначена для работы с ними. Однако при работе с .NET 4 объект DbProviderFactory не доступен напрямую из одного из его DbConnections. Поэтому EF использует некоторые эвристики для поиска зарегистрированных поставщиков, чтобы найти совпадение. Возможно, что для некоторых поставщиков эти эвристики могут не сработать, и в таких ситуациях поставщики должны предоставить новую реализацию.
Регистрация DbProviderServices
Реализация DbProviderServices для использования может быть зарегистрирована в файле конфигурации приложения (app.config или web.config) или с помощью конфигурации на основе кода. В любом случае регистрация использует "инвариантное имя" поставщика в качестве ключа. Это позволяет регистрировать и использовать несколько поставщиков в одном приложении. Инвариантное имя, используемое для регистрации EF, совпадает с инвариантным именем, используемым для регистрации поставщика ADO.NET и строк подключения. Например, для SQL Server используется инвариантное имя System.Data.SqlClient.
Регистрация файла конфигурации
Тип DbProviderServices, используемый, зарегистрирован в качестве элемента поставщика в списке поставщиков раздела entityFramework файла конфигурации приложения. Например:
<entityFramework>
<providers>
<provider invariantName="My.Invariant.Name" type="MyProvider.MyProviderServices, MyAssembly" />
</providers>
</entityFramework>
Строка типа должна быть именем типа с указанием сборки, представляющим реализацию DbProviderServices, которую следует использовать.
Регистрация на основе кода
Начиная с поставщиков EF6 также можно зарегистрировать с помощью кода. Это позволяет использовать поставщика EF без каких-либо изменений в файле конфигурации приложения. Чтобы использовать конфигурацию на основе кода, приложение должно создать класс DbConfiguration, как описано в документации по конфигурации на основе кода. Конструктор класса DbConfiguration должен вызывать SetProviderServices для регистрации поставщика EF. Например:
public class MyConfiguration : DbConfiguration
{
public MyConfiguration()
{
SetProviderServices("My.New.Provider", new MyProviderServices());
}
}
Решение вопросов с дополнительными службами
Как упоминалось выше в разделе обзора типов поставщиков, класс DbProviderServices также можно использовать для разрешения дополнительных служб. Это возможно, так как DbProviderServices реализует IDbDependencyResolver, и каждый зарегистрированный тип DbProviderServices добавляется как «резолвер по умолчанию». Механизм IDbDependencyResolver подробно описан в разделе "Разрешение зависимостей". Однако не обязательно понимать все понятия, описанные в этой спецификации, чтобы предоставлять дополнительные услуги с помощью поставщика.
Наиболее распространенным способом разрешения дополнительных служб является вызов DbProviderServices.AddDependencyResolver для каждой службы в конструкторе класса DbProviderServices. Например, SqlProviderServices (поставщик EF для SQL Server) имеет код, аналогичный этому для инициализации:
private SqlProviderServices()
{
AddDependencyResolver(new SingletonDependencyResolver<IDbConnectionFactory>(
new SqlConnectionFactory()));
AddDependencyResolver(new ExecutionStrategyResolver<DefaultSqlExecutionStrategy>(
"System.data.SqlClient", null, () => new DefaultSqlExecutionStrategy()));
AddDependencyResolver(new SingletonDependencyResolver<Func<MigrationSqlGenerator>>(
() => new SqlServerMigrationSqlGenerator(), "System.data.SqlClient"));
AddDependencyResolver(new SingletonDependencyResolver<DbSpatialServices>(
SqlSpatialServices.Instance,
k =>
{
var asSpatialKey = k as DbProviderInfo;
return asSpatialKey == null
|| asSpatialKey.ProviderInvariantName == ProviderInvariantName;
}));
}
Этот конструктор использует следующие вспомогательные классы:
- SingletonDependencyResolver: предоставляет простой способ разрешения служб Singleton, то есть таких служб, для которых при каждом вызове GetService возвращается один и тот же экземпляр. Транзиентные службы часто регистрируются как синглтон-фабрика, которая будет использоваться для создания временных экземпляров по запросу.
- ExecutionStrategyResolver: резольвер, специфичный для возврата реализаций IExecutionStrategy.
Вместо использования DbProviderServices.AddDependencyResolver можно также переопределить DbProviderServices.GetService и напрямую решить вопрос с дополнительными службами. Этот метод вызывается, если EF требует службы, определенной типом, и в некоторых случаях для заданного ключа. Метод должен возвращать службу, если это возможно, или возвращать значение NULL, чтобы отказаться от возврата службы и позволить другому классу разрешить его. Например, чтобы устранить фабрику подключений по умолчанию, код в GetService может выглядеть примерно так:
public override object GetService(Type type, object key)
{
if (type == typeof(IDbConnectionFactory))
{
return new SqlConnectionFactory();
}
return null;
}
Заказ на регистрацию
При регистрации нескольких реализаций DbProviderServices в файле конфигурации приложения они будут добавлены в качестве вторичных сопоставителей в том порядке, в который они перечислены. Так как резолверы всегда добавляются в начало вторичной цепочки резолверов, это означает, что поставщик в конце списка первым получит возможность разрешать зависимости. (Это может сначала показаться немного неинтуитивным, но это имеет смысл, если вы представите, что убираете каждого поставщика из списка и ставите его над существующими поставщиками.)
Обычно порядок не имеет значения, так как большинство служб поставщика характерны для него и идентифицируются по инвариантному имени поставщика. Однако для служб, которые не имеют ключа, определённого инвариантным именем поставщика или другим ключом, специфичным для поставщика, служба будет разрешена на основе этого порядка. Например, если оно не задано явно в другом месте, фабрика подключений по умолчанию будет получать значение от самого верхнего поставщика в цепочке.
Дополнительные регистрации файлов конфигурации
Можно явно зарегистрировать некоторые из дополнительных служб поставщика, описанных выше, непосредственно в файле конфигурации приложения. После этого регистрация в файле конфигурации будет использоваться вместо всего, возвращенного методом GetService реализации DbProviderServices.
Регистрация фабрики подключений по умолчанию
Начиная с EF5, пакет EntityFramework NuGet автоматически регистрирует либо фабрику подключений SQL Express, либо фабрику подключений LocalDb в конфигурационном файле.
Например:
<entityFramework>
<defaultConnectionFactory type="System.Data.Entity.Infrastructure.SqlConnectionFactory, EntityFramework" >
</entityFramework>
Тип — это квалифицированное название типа сборки для фабрики соединений по умолчанию, которая должна реализовывать IDbConnectionFactory.
При установке рекомендуется, чтобы пакет NuGet поставщика настраивал стандартную фабрику подключений таким образом. Ниже приведены пакеты NuGet для поставщиков .
Дополнительные изменения поставщика EF6
Изменения пространственного поставщика
Поставщики, поддерживающие пространственные типы, теперь должны реализовать некоторые дополнительные методы для классов, производных от DbSpatialDataReader:
public abstract bool IsGeographyColumn(int ordinal)public abstract bool IsGeometryColumn(int ordinal)
Существуют также новые асинхронные версии существующих методов, которые рекомендуется переопределить, так как реализации по умолчанию делегируют вызовы синхронным методам и поэтому не выполняются асинхронно.
public virtual Task<DbGeography> GetGeographyAsync(int ordinal, CancellationToken cancellationToken)public virtual Task<DbGeometry> GetGeometryAsync(int ordinal, CancellationToken cancellationToken)
Встроенная поддержка Enumerable.Contains
EF6 представляет новый тип выражения DbInExpression, который был добавлен для решения проблем с производительностью при использовании Enumerable.Contains в запросах LINQ. Класс DbProviderManifest имеет новый виртуальный метод SupportsInExpression, который вызывается EF, чтобы определить, обрабатывает ли поставщик новый тип выражения. Для совместимости с существующими реализациями поставщика метод возвращает значение false. Чтобы внедрить это улучшение, поставщик EF6 может добавить код для обработки DbInExpression и переопределить SupportsInExpression, чтобы возвращать значение true. Экземпляр DbInExpression можно создать, вызвав метод DbExpressionBuilder.In. Экземпляр DbInExpression состоит из dbExpression, обычно представляющего столбец таблицы, и список DbConstantExpression для проверки соответствия.
Пакеты NuGet для поставщиков
Одним из способов сделать провайдер EF6 доступным является выпустить его как пакет NuGet. Использование пакета NuGet имеет следующие преимущества:
- С помощью NuGet можно добавить регистрацию поставщика в файл конфигурации приложения.
- Дополнительные изменения можно внести в файл конфигурации, чтобы задать фабрику подключений по умолчанию таким образом, чтобы подключения, сделанные по соглашению, использовали зарегистрированного поставщика.
- NuGet обрабатывает добавление перенаправлений привязок, чтобы поставщик EF6 продолжал работать, даже после того как будет выпущен новый пакет EF.
Примером этого является пакет EntityFramework.SqlServerCompact, включенный в базу кода открытый код. Этот пакет предоставляет хороший шаблон для создания пакетов NuGet поставщика EF.
Команды PowerShell
Когда пакет NuGet EntityFramework установлен, он регистрирует модуль PowerShell, содержащий две команды, которые очень полезны для пакетов поставщиков:
- Add-EFProvider добавляет новую сущность для поставщика в файл конфигурации целевого проекта и гарантирует, что он находится в конце списка зарегистрированных поставщиков.
- Add-EFDefaultConnectionFactory добавляет или обновляет регистрацию defaultConnectionFactory в файле конфигурации целевого проекта.
Обе эти команды позаботятся о добавлении раздела entityFramework в файл конфигурации и добавлении коллекции поставщиков при необходимости.
Предполагается, что эти команды будут вызваны из скрипта NuGet install.ps1. Например, install.ps1 для поставщика SQL Compact выглядит примерно так:
param($installPath, $toolsPath, $package, $project)
Add-EFDefaultConnectionFactory $project 'System.Data.Entity.Infrastructure.SqlCeConnectionFactory, EntityFramework' -ConstructorArguments 'System.Data.SqlServerCe.4.0'
Add-EFProvider $project 'System.Data.SqlServerCe.4.0' 'System.Data.Entity.SqlServerCompact.SqlCeProviderServices, EntityFramework.SqlServerCompact'</pre>
Дополнительные сведения об этих командах можно получить, используя команду get-help в окне консоли диспетчера пакетов.
Поставщики упаковки
Поставщик упаковки — это поставщик EF и (или) ADO.NET, который упаковывает существующий поставщик для расширения его с другими функциями, такими как профилирование или возможности трассировки. Поставщики оболочек могут быть зарегистрированы обычным образом, но зачастую удобнее настроить поставщика оболочки во время выполнения, перехватив разрешение служб, связанных с поставщиком. Для этого можно использовать статическое событие OnLockingConfiguration класса DbConfiguration.
OnLockingConfiguration вызывается после того, как EF определил источник получения всей конфигурации EF для домена приложения, но до того, как она будет заблокирована для использования. При запуске приложения (до использования EF) приложение должно зарегистрировать обработчик событий для этого события. (Мы рассматриваем добавление поддержки регистрации этого обработчика в файле конфигурации, но это еще не поддерживается.) Затем обработчик событий должен вызвать ReplaceService для каждой службы, которая должна быть упакована.
Например, чтобы упаковать IDbConnectionFactory и DbProviderService, обработчик должен быть зарегистрирован следующим образом:
DbConfiguration.OnLockingConfiguration +=
(_, a) =>
{
a.ReplaceService<DbProviderServices>(
(s, k) => new MyWrappedProviderServices(s));
a.ReplaceService<IDbConnectionFactory>(
(s, k) => new MyWrappedConnectionFactory(s));
};
Служба, которая была разрешена и теперь должна быть упакована вместе с ключом, который использовался для разрешения службы, передается обработчику. Затем обработчик может обернуть эту службу и заменить возвращённую службу её обернутой версией.
Разрешение DbProviderFactory с помощью EF
DbProviderFactory является одним из основных типов поставщиков, необходимых EF, как описано в разделе обзора типов поставщиков выше. Как уже упоминалось, это не тип EF и регистрация обычно не является частью конфигурации EF, но вместо этого является обычной регистрацией поставщика ADO.NET в файле конфигурации machine.config и /или файла конфигурации приложения.
Несмотря на то, что EF по-прежнему использует свой обычный механизм разрешения зависимостей при поиске DbProviderFactory для использования. Резольвер по умолчанию использует стандартную регистрацию ADO.NET в файлах конфигурации, поэтому это обычно незаметно. Но поскольку используется стандартный механизм разрешения зависимостей, это означает, что IDbDependencyResolver можно применять для разрешения DbProviderFactory, даже если обычная регистрация ADO.NET не была проведена.
Разрешение DbProviderFactory таким образом имеет несколько последствий:
- Приложение, использующее конфигурацию на основе кода, может добавлять вызовы в класс DbConfiguration для регистрации соответствующего DbProviderFactory. Это особенно полезно для приложений, которые не хотят (или не могут) использовать любую конфигурацию на основе файлов.
- Служба может быть упакована или заменена с помощью ReplaceService, как описано в разделе поставщиков упаковки выше
- Теоретически реализация DbProviderServices может определить DbProviderFactory.
Важно отметить, что выполнение любой из этих операций повлияет только на поиск DbProviderFactory с помощью EF. Другой код, не связанный с EF, тем не менее, может ожидать, что поставщик ADO.NET будет зарегистрирован обычным способом и может не работать, если регистрация не найдена. По этой причине обычно лучше регистрировать DbProviderFactory обычным способом ADO.NET.
Связанные службы
Если EF используется для разрешения DbProviderFactory, он также должен разрешать службы IProviderInvariantName и IDbProviderFactoryResolver.
IProviderInvariantName — это служба, используемая для определения инвариантного имени поставщика для заданного типа DbProviderFactory. Реализация этой службы по умолчанию использует регистрацию поставщика ADO.NET. Это означает, что если поставщик ADO.NET не зарегистрирован обычным способом, поскольку DbProviderFactory обрабатывается EF, то потребуется также разрешить эту службу. Обратите внимание, что резолвер для этой службы автоматически добавляется при использовании метода DbConfiguration.SetProviderFactory.
Как описано в разделе "Обзор типов поставщиков", IDbProviderFactoryResolver используется для получения правильного DbProviderFactory из заданного объекта DbConnection. Реализация этой службы по умолчанию при запуске в .NET 4 использует регистрацию поставщика ADO.NET. Это означает, что если поставщик ADO.NET не зарегистрирован обычным способом, поскольку DbProviderFactory обрабатывается EF, то потребуется также разрешить эту службу.