Модель поставщика Entity Framework 6

Модель поставщика Entity Framework позволяет Entity Framework использовать с разными типами сервера баз данных. Например, один поставщик можно подключить, чтобы разрешить использовать EF для Microsoft SQL Server, а другой поставщик можно подключить к EF для использования с Microsoft SQL Server Compact Edition. Поставщики EF6, о которых мы знаем, можно найти на странице поставщиков Entity Framework.

Некоторые изменения были необходимы для взаимодействия EF с поставщиками, чтобы разрешить 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. В более старых версиях класса DBProviderServices был частью платформа .NET Framework и был найден в пространстве имен System.Data.Common. Начиная с EF6 этот класс теперь является частью EntityFramework.dll и находится в пространстве имен System.Data.Entity.Core.Common.

Дополнительные сведения об основных функциях реализации DbProviderServices можно найти на сайте MSDN. Однако обратите внимание, что на момент написания этой информации не обновляется для EF6, хотя большинство концепций по-прежнему действительны. Реализации SQL Server и SQL Server Compact для DbProviderService также проверяются в базе кода с открытым исходным кодом и могут служить полезными ссылками для других реализаций.

В более ранних версиях реализации 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.GetServices.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>

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

Поставщики упаковки

Поставщик упаковки — это поставщик 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, то для разрешения этой службы также потребуется.