如何为EF Code First配置ProviderManifestToken

42
我有一个使用EF code-first的asp.net MVC3项目。在我的单元测试中,我一直使用SQL Server CE 4.0和SQL Server 2008 Express,并且两者都可以按预期生成数据库来与EF配合使用。
然而,当我在应用程序中使用连接字符串时遇到如下错误:
"ProviderIncompatibleException:提供程序未返回ProviderManifestToken字符串"
我已经阅读了MS文档,并发现这是EF模型生成的SqlVersion令牌。问题在于我正在使用Code First方法,因此我没有.edmx文件,也不知道应该将元数据信息指向何处,因为数据库还没有被生成。
我知道我的连接字符串的数据库名称、用户名和密码都是正确的,因为更改为错误的值会抛出预期的错误,但是不确定从哪里开始解决。
谢谢。
这是我的连接字符串:
<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>

5
您确定使用连接字符串了吗?它应该与您从DbContext派生的类名称相同。 - Ladislav Mrnka
9
是的,我发现了问题所在。有几个原因:(1)即使数据库是空的,也不应该预先在SqlServer中创建它,应该让EF完成这个任务。(2)如果在DbContext中没有声明,则应该包括一个初始目录名称。(3)我无法在Medium Trust下创建数据库。希望这能帮助到某些人。 - trevorc
1
@nameEquals... 你是如何解决上述第三个问题的? - Ash Machine
10个回答

13

如果您正在使用刚刚发布的EF 6,您有一个替代方案。

依赖项解析

您可以使用新的依赖解析功能来注册IManifestTokenResolver的实现(在这个预览文档中被描述为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);
        }
    }
}

在使用EF与ODP.NET时,是否需要像这样的任何东西,而不实际连接到数据库? - Oblivious Sage
2
@ObliviousSage 我从未使用过与Oracle的EF,但是从这篇文章来看,似乎需要像这样做。 该文章的作者发现在他的情况下"11g"是适当的令牌值。 - Olly

10

经过数小时的搜索和尝试,我找到了一种方法来实现它。原来 DbModelBuilder 类在其 Build 方法中使用 DbProviderInfo,因此我使用它来代替依赖于 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's 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);
}

显然这需要一些重新组织(例如缓存编译好的模型,这样您就不需要每次创建上下文时都重新构建它)。

对我来说,这完全解决了问题。享受吧!


1
太好了-终于有人回答了帖子标题中的问题!! - Shawson
对于那些必须连接到多个SQL实例的应用程序开发人员,我已经确认了这个解决方案可以在连接到多个SQL版本(2005和2008)时有效。 - RMart
不错 - 我希望我之前看到了这个!看看你如何在EF 6中更轻松地完成这个 - 请参阅下面的我的答案。 - Olly

6
在我的情况下,连接字符串的名称必须与上下文类的名称匹配。
连接字符串:
<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; }
    }
}

6

我刚遇到了这个问题,但是我发现是因为我的SQL Server服务没有运行。我刚重启了电脑,通常情况下,该服务会自动启动,但出于某种原因没有启动。


我也是。我刚刚检查了我的Sql Server配置管理器,发现由于某种原因Sql Server服务没有运行。一旦我启动了服务,它就完美地工作了。 - Phil Hale
对我来说也是一样的问题,但我的SQLEXPRESS服务没有运行。它不能与正在运行的普通SQL Server一起使用。 - Pieter

1

我在学习ASP.NET的MVC3教程时遇到了这个问题。

我的解决方案是使用(localhost)而不是命名数据源。这对于我的本地开发工作来说很好用,但如果数据库在另一台服务器上,则无法使用。


1

我发现,当我在连接字符串中提供明确的 "User Id=abcUser; Password=somePwd;" 时,我能够解决同样的错误。之前我使用的是 "Trusted_Connection=true;",这使我能够调试我的 Web 项目,但是一旦我添加了 Windows Azure 项目并尝试将我的 Web 项目作为 Web 角色添加到其中后,它就开始出现错误 - {"The provider did not return a ProviderManifestToken string."}。

希望它能帮助那些遇到类似情况的人。

谢谢, Vivek Bahl


1

connectionString中的数据源更改为localhost解决了我的问题。


1

Data Source=localhost 更改为我的情况下也适用于使用 MS SQL 2008 R2 Express


0

我看到一些关于Oracle的评论,所以这里是针对Oracle调整过的“EntityFrameworkDbConfiguration”代码:

internal sealed class EntityFrameworkDbConfiguration : DbConfiguration
{
    /// <summary>
    /// Initializes a new instance of the <see cref="EntityFrameworkDbConfiguration"/> class.
    /// </summary>
    public EntityFrameworkDbConfiguration()
    {
        this.AddDependencyResolver(Oracle.ManagedDataAccess.EntityFramework.EFOracleProviderServices.Instance);
    }
}

-2

这对我来说非常有帮助:

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

你的连接字符串有什么特别之处吗?我看不出来它有什么帮助。 - Tony_Henrich

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接