让EF Power Tools CTP1识别自定义数据库连接串

近在学习Entity Framework:Code-First,安装了Entity Framework Power Tools CTP1后,在实际使用过程中遇到了一个问题,即我在app.config中自定义的数据库连接串不能被EF Power Tools识别,使我无法查看由代码生成的实体数据模型。


统环境:Windows 7 64位旗舰版,Visual Studio 2010旗舰版,Entity Framework 4.2,Entity Framework Power Tools CTP1

这是一个做练习用的Console项目,只有简单的2个文件:POCO.cs里是我Code-First的类,Program.cs里是主程序Main()。刚开始使用的是本地的SQL Server 2008 Express作的数据库。由于EF会自动使用和连接本地的SQL Server Express,因此之前的练习很顺利。每次右键点击POCO.cs,均能正常查看生成的数据模型图,并在数据库正确地生成所有的表。

view model

随后,我为了尝试远程连接,因此在项目中添加了如下内容的app.config

app.config

于是问题来了。当我需要查看实体数据模型时,总是在长时间的等待后,得到如下的错误提示。

Error

尽管如此,我运行这个简单的控制台程序,仍能正确地更新和生成数据库中的表。只是无法经由EF Power Tools显示实体数据模型而已。

我尝试了在DbContext的派生类的构造子中传入连接串,尝试了在Main()里设置默认的连接工厂(参见上图背景中第4行代码),也尝试了改为安装Entity Framework 4.1,均未能解决问题。无奈之下,只得四处求助。


终,我在StackOverflow上的问题得到了Devart的热情解答,给了我一个他们团队的Blog链接 Using Entity Framework Power Tools CTP1 with Oracle, MySQL, PostgreSQL, and SQLite,使得问题得到了较为圆满的解决。在该文中,他们介绍了4种解决方案:

1. 把连接串写入.NET Framework 4.0安装目录下的machine.config里;

2. 把连接串写入Visual Studio 2010的配置文件devenv.exe.config里;

3. 实现一个IDbConnectionFactory的自定义连接工厂,设置项目的缺少连接工厂(就象我此前尝试的一样,但我没放对地方);

4. 使用特定数据库的连接串,并将其传递给DbContext派生类构造子(我之前也尝试了,但我传递的是连接串本身,而不是连接对象)。

过思考,我最终选择了方案3:自定义连接工厂,并在DbContext派生类的静态构造子中设置项目的数据库缺省连接工厂。

· 自定义的连接工厂

public class PocoConnectionFactory : IDbConnectionFactory
{
    public DbConnection CreateConnection(string nameOrConnectionString)
    {
        // connectionString="Data Source=CASPER-PC\SQL_Abbey;Database=BreakAway;Integrated Security=True"
        var builder = new SqlConnectionStringBuilder
        {
            DataSource = @"CASPER-PC\SQL_Abbey",
            InitialCatalog = @"BreakAway",
            IntegratedSecurity = true,
            MultipleActiveResultSets = true
        };

        return new SqlConnection(builder.ToString());
    }
}

· 在DbContext派生类的静态构造子中设置数据库缺省的连接工厂

public class BreakAwayContext : DbContext
{
    // ... ...

    static BreakAwayContext()
    {
        Database.DefaultConnectionFactory = new PocoConnectionFactory();
    }
}

Devart给出的那篇文章里,选择了更为灵活的工厂实现方式,而不是我为了做练习而使用的硬编码方式。 期待EF Power Tools的下个完善版本。

public class DevartConnectionFactory : IDbConnectionFactory
{
    public DevartConnectionFactory(string providerInvariantName, string configFileName)
    {
        this.ProviderInvariantName = providerInvariantName;
        this.ConfigFileName = configFileName;
    }

    public DbConnection CreateConnection(string nameOrConnectionString)
    {
        if (String.IsNullOrWhiteSpace(nameOrConnectionString))
            throw new ArgumentNullException("nameOrConnectionString");

        DbProviderFactory providerFactory = DbProviderFactories.GetFactory(ProviderInvariantName);
        if (providerFactory == null)
            throw new InvalidOperationException(String.Format("The '{0}' provider is not registered on the local machine.",
                                                                ProviderInvariantName));

        DbConnection connection = providerFactory.CreateConnection();

        if (nameOrConnectionString.Contains("="))
            connection.ConnectionString = nameOrConnectionString;
        else
        {
            string dbContextClassName = nameOrConnectionString;
            if (dbContextClassName.Contains('.'))
            {
                int classNameFrom = nameOrConnectionString.LastIndexOf('.') + 1;
                int classNameLength = nameOrConnectionString.Length - classNameFrom;
                dbContextClassName = nameOrConnectionString.Substring(classNameFrom, classNameLength);
            }

            ExeConfigurationFileMap map = new ExeConfigurationFileMap();
            map.ExeConfigFilename = this.ConfigFileName;
            Configuration assemblyConfig = ConfigurationManager.OpenMappedExeConfiguration(map, ConfigurationUserLevel.None);

            ConnectionStringSettings connectionSettings = assemblyConfig.ConnectionStrings.ConnectionStrings[dbContextClassName];

            if (connectionSettings == null)
                throw new InvalidOperationException(String.Format("Can't find the '{0}' connection string in the config file.",
                                                                    dbContextClassName));

            if (connectionSettings.ProviderName != ProviderInvariantName)
                throw new InvalidOperationException(
                    String.Format(
                        "The '{0}' connection string was expected to be defined for the '{1}' provider, but it is defined for the '{2}' provider.",
                        dbContextClassName, ProviderInvariantName, connectionSettings.ProviderName));

            connection.ConnectionString = connectionSettings.ConnectionString;
        }

        return connection;
    }

    public string ProviderInvariantName { get; set; }
    public string ConfigFileName { get; set; }
}

转载请注明出处及作者,谢谢!
原文地址:https://www.cnblogs.com/Abbey/p/2343347.html