前回ローカルでDB接続できたので、Ubuntuにデプロイしてみます。

肝は環境の切り替えとDBマイグレーションです。

接続文字列はappsettings.{ENV}.jsonで管理

ASP.NET Coreでは接続文字列はappsettings.jsonに書くのが良いみたいです。

さらに環境設定はappsettings.{env.EnvironmentName}.jsonで手軽に切り替えられるようです。

まずはローカル開発環境で動くようにする

実行時の環境は環境変数ASPNETCORE_ENVIRONMENTを参照するみたいです。

VSではプロジェクトのプロパティのDebugセクションに定義してあります。

ということで、appsettings.Development.jsonを

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
{
  "Logging": {
    "IncludeScopes": false,
    "LogLevel": {
      "Default": "Debug",
      "System": "Information",
      "Microsoft": "Information"
    }
  },
  "ConnectionStrings": {
    "BloggingDatabase": "Host=localhost;Database=aspnetdb_dev;Username=vagrant"
  }
}

な感じに書き換えます。

DbContextをdependency injection

BloggingContext.csはからは直接appsettings.json内の値を参照できないので、DIするように設定します。

BloggingContext.csにコンストラクタを追加。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
....
    public class BloggingContext : DbContext
    {
        public BloggingContext(DbContextOptions<BloggingContext> options) : base(options) { } //追記
        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) {} //空に

        public DbSet<Blog> Blogs { get; set; }
        public DbSet<Post> Posts { get; set; }

    }
....

Startup.csのConfigureServicesにAddDbContextを追記します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
using Microsoft.EntityFrameworkCore;  //追記
using WebApplication3.Models;  //追記
....
        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddDbContext<BloggingContext>(options => options.UseNpgsql(Configuration.GetConnectionString("BloggingDatabase")));  //追記
            // Add framework services.
            services.AddMvc();
        }
....

ControllerにInjectされるようコンストラクタを新設して、DIされたオブジェクトを使用するよう変更。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
....
    public class HomeController : Controller
    {
        private Models.BloggingContext db;
        public HomeController(Models.BloggingContext context)
        {
            this.db = context;
        }
        public IActionResult Index()
        {
            var blogs = db.Blogs.OrderBy(b => b.Url).ToList();
            return View(blogs);
        }

        public IActionResult About()
        {
            ViewData["Message"] = "Your application description page.";
            var blog = new Models.Blog { Url = "http://sample.com/"+DateTime.UtcNow.ToString("yyyyMMdd_hhmmssms") };
            db.Blogs.Add(blog);
            db.SaveChanges();
            return View();
        }
...

ひとまずデバッグ実行でちゃんと動くか確認。

Production用のappsettings作成

無事ローカルで動いたら、appsettings.Development.jsonをコピーしてappsettings.Production.jsonを作成。db名をaspnetdbに変更。

1
"BloggingDatabase": "Host=localhost;Database=aspnetdb;Username=vagrant"

Productionにdeploy

例によって

1
2
3
>dotnet restore
>dotnet build
>dotnet publish --configuration Release -r ubuntu.16.04-x64

scpなりftpなりでコピー。

Ubuntu環境をProduction扱いにする

環境変数をセットします。

1
vagrant@vagrant:~/src/publish$ export ASPNETCORE_ENVIRONMENT=Production

必要なら.bash_profileに書いとく。

実行とエラー対応

1
vagrant@vagrant:~/src/publish$ dotnet WebApplication3.dll

で実行。

postgersユーザーのパスワード

こんなエラーが出ました

1
No password has been provided but the backend requires one (in MD5)

空パスワードはダメらしいので設定します。

1
2
3
vagrant@vagrant:~/src/publish$ sudo -i -u postgres
postgres@vagrant:~$ psql
postgres=# alter user vagrant WITH PASSWORD 'test';

合わせてconnection stringを書き換え。

1
"BloggingDatabase": "Host=localhost;Database=aspnetdb;Username=vagrant;Password=test"

ubuntuでdb migration

続いてこのエラー

1
Npgsql.PostgresException: 42P01: relation "Blogs" does not exist

Udate-Databaseしてないからねー。

こちらを参考に

WebApplication3.csprojを編集

1
2
3
4
<ItemGroup>
    <DotNetCliToolReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Tools" Version="1.0.1" />
    <DotNetCliToolReference Include="Microsoft.EntityFrameworkCore.Tools.DotNet" Version="1.0.1" /><!--追記-->
  </ItemGroup>

してpublish して dotnet efしても

1
No executable found matching command "dotnet-ef"

と怒られます。

いろいろ調べたんですが、まともな解決法は見つからず。

stackoverflow先生によると、

SQLを生成して実行か、dbContext.Database.Migrate()を実行だそうです。

今回はSQL実行します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
c:\usr\vstest\WebApplication3\WebApplication3>dotnet ef migrations script

Build succeeded.
    0 Warning(s)
    0 Error(s)

Time Elapsed 00:00:02.65
CREATE TABLE "__EFMigrationsHistory" (
    "MigrationId" varchar(150) NOT NULL,
    "ProductVersion" varchar(32) NOT NULL,
    CONSTRAINT "PK___EFMigrationsHistory" PRIMARY KEY ("MigrationId")
);

CREATE TABLE "Blogs" (
    "BlogId" serial NOT NULL,
    "Rating" int4 NOT NULL,
    "Url" text,
    CONSTRAINT "PK_Blogs" PRIMARY KEY ("BlogId")
);

CREATE TABLE "Posts" (
    "PostId" serial NOT NULL,
    "BlogId" int4 NOT NULL,
    "Content" text,
    "Title" text,
    CONSTRAINT "PK_Posts" PRIMARY KEY ("PostId"),
    CONSTRAINT "FK_Posts_Blogs_BlogId" FOREIGN KEY ("BlogId") REFERENCES "Blogs" ("BlogId") ON DELETE CASCADE
);

CREATE INDEX "IX_Posts_BlogId" ON "Posts" ("BlogId");

INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion")
VALUES ('20170615065858_InitialCreate', '1.1.2');

吐かれたSQLをpgAdminで実行。

動きます

無事エラーが解決ができたら、http://localhost:5050/にアクセスすると、ちゃんと動いている様子。

コンソールに吐かれるログは

1
2
3
4
5
6
7
8
9
dbug: Npgsql.NpgsqlConnection[3]
      Opening connection to database 'aspnetdb' on server 'tcp://localhost:5432'.
info: Microsoft.EntityFrameworkCore.Storage.IRelationalCommandBuilderFactory[1]
      Executed DbCommand (16ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
      SELECT "b"."BlogId", "b"."Rating", "b"."Url"
      FROM "Blogs" AS "b"
      ORDER BY "b"."Url"
dbug: Npgsql.NpgsqlConnection[4]
      Closing connection to database 'aspnetdb' on server 'tcp://localhost:5432'.

こんな感じで、EFも動いているようです。

次回はDBマイグレーション関係を試してみます。