【问题标题】:Using Entity Framework, ASP.NET MVC Core, and PostgreSQL with Heroku将 Entity Framework、ASP.NET MVC Core 和 PostgreSQL 与 Heroku 一起使用
【发布时间】:2018-08-03 08:35:13
【问题描述】:

我正在尝试在 .NET core 2.0 上运行的 ASP.NET MVC 应用程序中使用 Heroku 管理的 PostgreSQL 数据库。我希望能够使用实体框架轻松读取和写入数据库。我对所有这些东西都非常陌生,除了 ASP.NET,这可能很明显,并且以前出于同样的目的使用本地 SQLite 服务器,我几乎不了解 PostgreSQL 如何与 Heroku 和实体框架一起工作。

我已将Npgsql extension 安装到实体框架中。对于此特定设置以及如何将其与 Heroku 一起使用,我被困在实体​​框架的连接字符串上。 Heroku 提供了一个DATABASE_URL 变量(记录为here),这是必须使用的,因为database connection credentials are subject to change 和Heroku 在变量发生变化时会自动更新。

//This method gets called by the runtime. Use this method to add services to   
//the container.
public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc();

    string connection = "???";
    services.AddDbContext<MyDbContext>(options => options.UseNpgsql(connection));
}

我对@9​​87654326@ 使用什么值,这样它才能通过 Heroku 的 URL 连接到数据库,而与当前凭据无关?另外,如何确保在数据库中创建与MyDbContext 模型匹配的表?

我的目标只是让已部署的 Heroku 网站能够访问(读取和写入)数据库。如果我也可以出于开发目的在本地访问数据库会很好,但我唯一的要求是这项工作在托管网站上并且数据库由 Heroku 管理(我使用 PostgreSQL,Kafka 和 Redis 也可以通过 Heroku 获得)。

【问题讨论】:

    标签: asp.net asp.net-mvc postgresql entity-framework heroku


    【解决方案1】:

    这里的解决方案是“解析” Heroku 提供的DATABASE_URL环境变量的内容,并使用它来构建 Npgsql 期望的格式的连接字符串。

    要获得快速而肮脏的解决方案,您可以按照以下解决方案进行操作:.net core - database url parser

    对于我的项目,我决定更进一步,并为此创建了一个基于其他连接字符串构建器(用于 MS SQL、Mongo 等)的类:

    public enum SslMode
    {
        Require, 
        Disable,
        Prefer
    }
    
    public class PostgreSqlConnectionStringBuilder : DbConnectionStringBuilder
    {
        private string _database;
        private string _host;
        private string _password;
        private bool _pooling;
        private int _port;
        private string _username;
        private bool _trustServerCertificate;
        private SslMode _sslMode;
    
        public PostgreSqlConnectionStringBuilder(string uriString)
        {
            ParseUri(uriString);
        }
    
        public string Database
        {
            get => _database;
            set
            {
                base["database"] = value;
                _database = value;
            }
        }
    
        public string Host
        {
            get => _host;
            set
            {
                base["host"] = value;
                _host = value;
            }
        }
    
        public string Password
        {
            get => _password;
            set
            {
                base["password"] = value;
                _password = value;
            }
        }
    
        public bool Pooling
        {
            get => _pooling;
            set
            {
                base["pooling"] = value;
                _pooling = value;
            }
        }
    
        public int Port
        {
            get => _port;
            set
            {
                base["port"] = value;
                _port = value;
            }
        }
    
        public string Username
        {
            get => _username;
            set
            {
                base["username"] = value;
                _username = value;
            }
        }
    
        public bool TrustServerCertificate
        {
            get => _trustServerCertificate;
            set
            {
                base["trust server certificate"] = value;
                _trustServerCertificate= value;
            }
        }
    
        public SslMode SslMode
        {
            get => _sslMode;
            set
            {
                base["ssl mode"] = value.ToString();
                _sslMode = value;
            }
        }
    
        public override object this[string keyword]
        {
            get
            {
                if (keyword == null) throw new ArgumentNullException(nameof(keyword));
                return base[keyword.ToLower()];
            }
            set
            {
                if (keyword == null) throw new ArgumentNullException(nameof(keyword));
    
                switch (keyword.ToLower())
                {
                    case "host":
                        Host = (string) value;
                        break;
    
                    case "port":
                        Port = Convert.ToInt32(value);
                        break;
    
                    case "database":
                        Database = (string) value;
                        break;
    
                    case "username":
                        Username = (string) value;
                        break;
    
                    case "password":
                        Password = (string) value;
                        break;
    
                    case "pooling":
                        Pooling = Convert.ToBoolean(value);
                        break;
    
                    case "trust server certificate":
                        TrustServerCertificate = Convert.ToBoolean(value);
                        break;
    
                    case "sslmode":
                        SslMode = (SslMode) value;
                        break;
    
                    default:
                        throw new ArgumentException(string.Format("Invalid keyword '{0}'.", keyword));
                }
            }
        }
    
        public override bool ContainsKey(string keyword)
        {
            return base.ContainsKey(keyword.ToLower());
        }
    
        private void ParseUri(string uriString)
        {
            var isUri = Uri.TryCreate(uriString, UriKind.Absolute, out var uri);
    
            if (!isUri) throw new FormatException(string.Format("'{0}' is not a valid URI.", uriString));
    
            Host = uri.Host;
            Port = uri.Port;
            Database = uri.LocalPath.Substring(1);
            Username = uri.UserInfo.Split(':')[0];
            Password = uri.UserInfo.Split(':')[1];
        }
    }
    

    然后,在我的Startup.cs 中,在Configuration 方法中,我有:

    var builder = new PostgreSqlConnectionStringBuilder(Configuration["DATABASE_URL"])
    {
        Pooling = true,
        TrustServerCertificate = true,
        SslMode = SslMode.Require
    };
    
    services.AddEntityFrameworkNpgsql()
            .AddDbContext<TTRDbContext>(options => options.UseNpgsql(builder.ConnectionString));
    

    如果您从 Heroku 网络外部(例如本地环境)访问数据库,则需要添加 SSL ModeTrust Server Certificate

    希望对你有帮助

    【讨论】:

    • 这太完美了,非常感谢!我也没有意识到我必须打电话给services.AddEntityFrameworkNpgsql()
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-01-01
    • 1970-01-01
    • 2019-11-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-08-05
    相关资源
    最近更新 更多