Entity Framework 6程序化连接到Postgres

10

我正在努力通过Entity Framework 6以编程方式建立到PostgreSQL的连接。 我有这个类:

public class ClearspanDatabaseContext : DbContext

使用这个构造函数:
public ClearspanDatabaseContext()
    : base(buildConnectionString())
{
}

这是一个静态方法,用于程序化地生成连接字符串:

private static string buildConnectionString()
{
    RegisterDbProvider("Npgsql", ".Net Framework Data Provider for Postgresql", "Npgsql Data Provider", "Npgsql.NpgsqlFactory, Npgsql");
    EntityConnectionStringBuilder entityConnectionStringBuilder = new EntityConnectionStringBuilder();
    entityConnectionStringBuilder.Provider = "Npgsql";
    entityConnectionStringBuilder.ProviderConnectionString = "host=192.168.168.140;Port=5432;username=ClearspanDevLogin;password=*******;database=ClearspanWebServerDev";
    return entityConnectionStringBuilder.ToString();
}

下面是从这个来源中获取的将Npgsql注册为数据库提供程序的方法:

public static bool RegisterDbProvider(string invariant, string description, string name, string type)
{
    try
    {
        DataSet ds = ConfigurationManager.GetSection("system.data") as DataSet;
        foreach (DataRow row in ds.Tables[0].Rows)
        {
            if (row["InvariantName"].ToString() == invariant)
            {
                return true;
            }
        }
        ds.Tables[0].Rows.Add(name, description, invariant, type);
        return true;
    }
    catch
    {
    }
    return false;
}

这将生成一个类似于以下的字符串:
"provider=Npgsql;provider connection string=\"host=192.168.168.140;Port=5432;username=ClearspanDevLogin;password=********;database=ClearspanWebServerDev\""

但是我遇到了一个ArgumentException

关键字不受支持:'provider'。

我认为我已经接近程序化连接,但是缺少一些小东西。我该怎么做才能解决这个异常并正确地设置这个程序化连接?不要使用app.config答案,因为我正在使用类库工作,它忽略了app.config(请参见此问题的被接受答案的评论)。由于该程序用作插件,因此必须保持这种方式,它不会(也不应该)独立运行。提前致谢。


你尝试过使用 new DbContext(new System.Data.Entity.Core.EntityClient.EntityConnection(buildConnectionString()), true) 吗?这个 DbContext 构造函数可能不支持指定提供程序。 - jjj
2个回答

12

好的,这里有一个我验证过可以工作的示例。使用虚拟的 EF 6 Code-First 模型 + 自定义 DbConfiguration 类:

public class Enrollment {
    public int EnrollmentID { get; set; }
    public int CourseID { get; set; }
    public int StudentID { get; set; }
}

[DbConfigurationType(typeof (NpgsqlConfiguration))]
public class SchoolContext : DbContext {
    public SchoolContext(string cs) : base(cs) {
    }

    public DbSet<Enrollment> Enrollments { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder) {

    }
}

class NpgsqlConfiguration : System.Data.Entity.DbConfiguration
{
    public NpgsqlConfiguration()
    {
        SetProviderServices("Npgsql", Npgsql.NpgsqlServices.Instance);
        SetProviderFactory("Npgsql", Npgsql.NpgsqlFactory.Instance);
        SetDefaultConnectionFactory(new Npgsql.NpgsqlConnectionFactory());
    }
}

然后,不需要使用buildConnectionString()函数,只需在构造函数中传递postgre连接字符串:

using (var ctx = new SchoolContext("host=192.168.168.40;port=5432;...")) {                
            Console.WriteLine(ctx.Enrollments.ToArray());
        } 

就是这样。在此期间,配置文件完全为空,并且它可以正常工作。


这样做是否适用于我使用Code First?我不确定我要使用的.csdl和.sSDL文件在哪里。我认为它们是Database First的产物。 - Nick Gilbert
不完全是。使用Code-First,元数据将在第一次连接时(动态地)生成,因此所有那些.csdl文件仍然存在(尽管对您来说是透明的)。但这与您的问题并没有直接关系。在您的情况下 - 尝试直接使用提供程序连接字符串(而不是EF连接字符串),因此是原始的PostgreSQL连接字符串。您的错误是因为EF尝试解析您传递给它的连接字符串。它看到没有“metadata”参数,所以它不是EF字符串。因此,这是原始的PostgreSQL连接字符串。然后它看到“provider”选项并不知道它是什么。 - Evk
好的,如果元数据不是必需的,我不确定你建议我如何修改我的代码。 - Nick Gilbert
谢谢,但我遇到了一个错误: “在mscorlib.dll中发生了类型为'System.ArgumentException'的异常,但未在用户代码中处理。其他信息:参数'xmlReader'无效。必须提供至少一个.ssdl工件。” 我的元数据字符串最终变成了 “res://DatabaseConnection,Version=1.2.5731.17066,Culture=neutral,PublicKeyToken=null/” DatabaseConnection库实际上没有EF,所以我尝试使用Assembly.GetAssembly(typeof(Distance)),它确实使用了EF,但元数据字符串看起来很相似,不包含任何.ssdl内容。 - Nick Gilbert
让我们在聊天中继续这个讨论 - Evk
显示剩余3条评论

2
  1. Have you looked at Code-Based Configuration? Create a DbConfiguration class with a public parameterless constructor in the same assembly as your DbContext

    class MyConfiguration : System.Data.Entity.DbConfiguration
    {
        public MyConfiguration()
        {
            SetProviderServices("Npgsql", Npgsql.NpgsqlServices.Instance);
            SetProviderFactory("Npgsql", Npgsql.NpgsqlFactory.Instance);
        }
    }
    

    Now I think the DbContext should use that provider factory by default, and you can construct the DbContext with just the connection string. But if it's in a different assembly, then you have a bit more work to do, but that can be found in the link above.

  2. A potential problem with the above solution is that any configuration in the config file will take precedence, so maybe it would be safer to use the option described in here:

    var conn = DbProviderFactories.GetFactory("MY_CONN_PROVIDER").CreateConnection();
    conn.ConnectionString = "MY_CONN_STR";
    
    new DbContext(conn, true);
    

    where your provider is "Npgsql", which was registered in RegisterDbProvider above.

    Also see https://msdn.microsoft.com/en-us/library/dd0w4a2z(v=vs.110).aspx


谢谢,我正在尝试处理其中一些内容。在实现第二个选项时,我在这一行代码 var conn = DbProviderFactories.GetFactory("Npgsql").CreateConnection(); 上遇到了错误:“无法找到或加载已注册的 .Net Framework 数据提供程序。” 尽管似乎 RegisterDbProvider 方法是有效的。你有什么想法可能出了什么问题吗? - Nick Gilbert
@NickG:根据您之前提到的,这是在另一个应用程序中使用的库/插件中。也许您可以检查主应用程序本身是否知道在哪里找到npgsql DLL? - jjj
1
感谢您的帮助。我检查了这个过程,插件确实可以访问Npgsql.dll。我会点赞您的问题,因为它在概念上对我有所帮助,尽管我已经验证了被接受的答案作为回答我的问题的工作。 - Nick Gilbert

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