Как настроить ProviderManifestToken для EF Code First

У меня есть проект asp.net MVC3 с использованием EF-кода. Для моего модульного тестирования я использую SQL Server CE 4.0 и SQL Server 2008 Express. Оба отлично работали с EF, генерируя мою базу данных, как ожидалось.

Однако, когда я запускаю приложение за пределами unit test и указываю его на строки подключения, я получаю сообщение об ошибке

ProviderIncompatibleException: поставщик не вернул строку ProviderManifestToken

Я прочитал документацию MS по этому вопросу, и, похоже, это токен SqlVersion, который генерирует EF-модель. Проблема в том, что я использую первый подход кода, поэтому у меня нет файла .edmx, и я не знаю, где указать информацию о метаданных, потому что db еще не создан.

Я знаю, что строки подключения до имени db, имени пользователя и пароля правильны, потому что изменение их на неправильные значения вызывает ожидаемую ошибку. Не уверен, с чего начать.

Спасибо.

Вот моя строка подключения:

<connectionStrings>
  <add
    name="SqlConnection"
    providerName="System.Data.SqlClient"
    connectionString="Data Source=WORKSTATION\SQLEXPRESS;Initial Catalog=CodeFirst;Integrated Security=False;
    Persist Security Info=False;User ID=CodeFirst_user;Password=password1;Connect Timeout=120;MultipleActiveResultSets=True;"/>
</connectionStrings>

Ответ 1

После нескольких часов поиска и вождения я нашел способ сделать это. Оказывается, класс DbModelBuilder принимает DbProviderInfo в своем методе Build, поэтому я использую это вместо того, чтобы полагаться на EF для вызова OnModelCreated:

// 'Entities' is my DbContext subclass, the "container" in EF terms.
public static Entities GetNewContext()
{
    // Get a connection, for example:
    var connection = new SqlConnection(GetConnectionString());

    // Create a DbModelBuilder
    var modelBuilder = new DbModelBuilder();
    // Configure the model builder.
    // I changed my DbContext subclass - added a public version of OnModelCreated and called it ConfigureModelBuilder
    Entities.ConfigureModelBuilder(modelBuilder);

    // Here where the magic happens.
    // Build the model and pass the ProviderManifestToken (I use 2005 to avoid a bug in precision of sql datetime columns when using concurrency control)
    var model = modelBuilder.Build(new System.Data.Entity.Infrastructure.DbProviderInfo("System.Data.SqlClient", "2005"));
    // Compile the model
    var compiledModel = model.Compile();

    // Create the container (DbContext subclass). Ideally all the previous stuff should be cached.
    return new Entities(connection, compiledModel, true);
}

Очевидно, что это требует некоторой реорганизации (например, кэша скомпилированной модели, поэтому вам не нужно повторно строить ее каждый раз, когда создается контекст).

Для меня это полностью решило проблему. Наслаждайтесь!

Ответ 2

Если вы используете EF 6 (только что выпущенный), у вас есть альтернатива.

Разрешение зависимостей

Вы можете использовать новую функцию разрешение зависимостей для регистрации реализации IManifestTokenResolver (описанной в этой документации предварительного просмотра as IManifestTokenService).

В этой статье дается более подробная информация о том, как использовать DbConfiguration. Самый простой способ использовать его:

DbConfigurationType(typeof(EntityFrameworkDbConfiguration))]
public class MyContextContext : DbContext
{
}

В этом примере избегается любая поездка в базу данных при создании метаданных для подключений SQL Server и автоматически указывается совместимость SQL Server 2005.

using System.Data.Common;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Data.Entity.Infrastructure.DependencyResolution;
using System.Data.SqlClient;

/// <summary>
/// A configuration class for SQL Server that specifies SQL 2005 compatability.
/// </summary>
internal sealed class EntityFrameworkDbConfiguration : DbConfiguration
{
    /// <summary>
    /// The provider manifest token to use for SQL Server.
    /// </summary>
    private const string SqlServerManifestToken = @"2005";

    /// <summary>
    /// Initializes a new instance of the <see cref="EntityFrameworkDbConfiguration"/> class.
    /// </summary>
    public EntityFrameworkDbConfiguration()
    {
        this.AddDependencyResolver(new SingletonDependencyResolver<IManifestTokenResolver>(new ManifestTokenService()));
    }

    /// <inheritdoc />
    private sealed class ManifestTokenService : IManifestTokenResolver
    {
        /// <summary>
        /// The default token resolver.
        /// </summary>
        private static readonly IManifestTokenResolver DefaultManifestTokenResolver = new DefaultManifestTokenResolver();

        /// <inheritdoc />
        public string ResolveManifestToken(DbConnection connection)
        {
            if (connection is SqlConnection)
            {
                return SqlServerManifestToken;
            }

            return DefaultManifestTokenResolver.ResolveManifestToken(connection);
        }
    }
}

Ответ 3

У меня просто была эта точная проблема, но я проследил ее до того, что моя служба SQL Server не была запущена. Я только что перезапустил свой компьютер, и обычно он начинается сам по себе, но не по какой-то причине.

Ответ 4

В моем случае имя строки подключения должно соответствовать имени класса контекста.

Строка подключения:

<connectionStrings>
  <add name="NunuContext" connectionString="Data Source=|DataDirectory|Nunu.sdf" providerName="System.Data.SqlServerCe.4.0" />
</connectionStrings>

Класс контекста:

using System.Data.Entity;
namespace Nunu.Models
{
    public class NunuContext : DbContext
    {
        System.Data.Entity.DropCreateDatabaseIfModelChanges<Nunu.Models.NunuContext>());

        public DbSet<Nunu.Models.NunuFirst> NunuFirsts { get; set; }

        public DbSet<Nunu.Models.NunuLast> NunuLasts { get; set; }
    }
}

Ответ 5

У меня была эта проблема при работе с учебником MVC3 по ASP.NET.

Моим решением оказалось использование (localhost) вместо имени источника данных. Это отлично работает на моем ящике, для локальной работы с разработчиками, но не помогло бы, если бы база данных находилась на отдельном сервере.

Ответ 6

Я обнаружил, когда я указал явный "User Id = abcUser; Password = somePwd;" в моей строке подключения я могу решить ту же ошибку. Раньше я использовал "Trusted_Connection = true;", что позволило мне отладить мой веб-проект, но начал давать мне ошибку - { "Поставщик не вернул строку ProviderManifestToken." }, Как только я добавил проект azure Windows и попыталась отладить проект Azure после добавления моего веб-проекта в качестве веб-роли под ним.

Надеюсь, что это поможет кому-то испытать подобную ситуацию.

Спасибо, Vivek Bahl

Ответ 7

Переход на Источник данных = localhost работал у меня также с использованием MS SQL 2008 R2 Express

Ответ 8

Изменение источника данных на localhost в connectionString решило мою проблему.

Ответ 9

Это оказалось полезным для меня:

<connectionString="Data Source=WORKSTATION\SQLEXPRESS;Initial Catalog=CodeFirst;User ID=CodeFirst_user;Password=********"/> 
</connectionStrings>