ASP.NET Core App Upgrade zur Finalen Version 1.0.0 und mit PostgreSQL verbinden

Einleitung

In meinen vorherigen Post habe ich beschrieben, wie man eine .NET Core Applikation auf Heroku zum laufen bekommt: http://ninja4.net/asp-net-core-einfache-net-core-anwendung-in-heroku-veroffentlichen/

Üblicherweise braucht jede ernst zu nehmende Applikation auch eine Anbindung an eine SQL Datenbank. Leider bietet Heroku keine Microsoft SQL Datenbank an, sondern nur eine kostenlose PostgreSQL Datenbank. Aber auch diese kann man natürlich mit dem Entity Framework verbinden.

Einrichten der PostgreSQL auf Heroku

Das Einrichten der PostgreSQL Datenbank ist sehr einfach und benötigt nur einen Klick innerhalb der App, um diese als AddOn hinzuzufügen:

Anschließend wählt man die kostenlose Variante aus:

Nach ein paar Sekunden ist das AddOn dann installiet:

Anschließend findet man unter "Settings" die Verbindungsdaten zur Postgres Datenbank:

Konfigurieren von SQL Client

Damit man sich die Daten in der Datenbank vernünftig ansehen und auswerten kann, verwende ich HeidiSQL, dass man unter folgenden Link herunter laden kann: http://www.heidisql.com/

Hier richtet man zunächst eine neue Verbindung ein:

Anschließend sind aus den Verbindungsdaten von Heroku die einzelnen Informationen in der Connection einzutragen (postgres://[User]:[Password]@[Hostname]:[Port]/[Database]):

Wenn alles klappt sieht man anschließend die folgenden Tabelleneinträge:

Upgrade auf 1.0.0

Zunächst müssen wir die Applikation auf 1.0.0 konfigurieren und das Entity Framework einfügen, sonst können wir abhängige Pakete für PostgreSQL nicht verwenden und es wird sonst noch BETA Code verwendet, dass sollte man nach Möglichkeit vermeiden.

Am besten ersetzt man einfach die "Project.json" Konfiguration mit dieser:

{
  "userSecretsId": "aspnet-dotnetbeispiel1-69ffca4e-9a8a-48f7-b72b-40b4fcb255f4",

  "dependencies": {
    "Microsoft.NETCore.App": {
      "version": "1.0.0",
      "type": "platform"
    },
    "Microsoft.AspNetCore.Authentication.Cookies": "1.0.0",
    "Microsoft.AspNetCore.Diagnostics": "1.0.0",
    "Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore": "1.0.0",
    "Microsoft.AspNetCore.Identity.EntityFrameworkCore": "1.0.0",
    "Microsoft.AspNetCore.Mvc": "1.0.0",
    "Microsoft.AspNetCore.Razor.Tools": {
      "version": "1.0.0-preview2-final",
      "type": "build"
    },
    "Microsoft.AspNetCore.Server.IISIntegration": "1.0.0",
    "Microsoft.AspNetCore.Server.Kestrel": "1.0.0",
    "Microsoft.AspNetCore.StaticFiles": "1.0.0",
    "Microsoft.EntityFrameworkCore.Sqlite": "1.0.0",
    "Microsoft.EntityFrameworkCore.Sqlite.Design": {
      "version": "1.0.0",
      "type": "build"
    },
    "Microsoft.EntityFrameworkCore.Tools": {
      "version": "1.0.0-preview2-final",
      "type": "build"
    },
    "Microsoft.Extensions.Configuration.EnvironmentVariables": "1.0.0",
    "Microsoft.Extensions.Configuration.Json": "1.0.0",
    "Microsoft.Extensions.Configuration.CommandLine": "1.0.0",
    "Microsoft.Extensions.Configuration.UserSecrets": "1.0.0",
    "Microsoft.Extensions.Logging": "1.0.0",
    "Microsoft.Extensions.Logging.Console": "1.0.0",
    "Microsoft.Extensions.Logging.Debug": "1.0.0",
    "Microsoft.Extensions.Options.ConfigurationExtensions": "1.0.0",
    "Microsoft.VisualStudio.Web.BrowserLink.Loader": "14.0.0",
    "Microsoft.VisualStudio.Web.CodeGeneration.Tools": {
      "version": "1.0.0-preview2-final",
      "type": "build"
    },
    "Microsoft.VisualStudio.Web.CodeGenerators.Mvc": {
      "version": "1.0.0-preview2-final",
      "type": "build"
    }
  },

  "tools": {
    "BundlerMinifier.Core": "2.0.238",
    "Microsoft.AspNetCore.Razor.Tools": "1.0.0-preview2-final",
    "Microsoft.AspNetCore.Server.IISIntegration.Tools": "1.0.0-preview2-final",
    "Microsoft.EntityFrameworkCore.Tools": "1.0.0-preview2-final",
    "Microsoft.Extensions.SecretManager.Tools": "1.0.0-preview2-final",
    "Microsoft.VisualStudio.Web.CodeGeneration.Tools": {
      "version": "1.0.0-preview2-final",
      "imports": [
        "portable-net45+win8"
      ]
    }
  },

  "frameworks": {
    "netcoreapp1.0": {
      "imports": [
        "dotnet5.6",
        "portable-net45+win8"
      ]
    }
  },

  "buildOptions": {
    "emitEntryPoint": true,
    "preserveCompilationContext": true
  },

  "runtimeOptions": {
    "configProperties": {
      "System.GC.Server": true
    }
  },

  "publishOptions": {
    "include": [
      "wwwroot",
      "Views",
      "Areas/**/Views",
      "appsettings.json",
      "web.config"
    ]
  },

  "scripts": {
    "precompile": [ "dotnet bundle" ],
    "prepublish": [ "bower install" ],
    "postpublish": [ "dotnet publish-iis --publish-folder %publish:OutputPath% --framework %publish:FullTargetFramework%" ]
  },

  "tooling": {
    "defaultNamespace": "dotnetbeispiel1"
  }
}

Anschließend müssen die Packages updated werden und der Build gestartet werden. Der Source-Code muss angepasst werden, wo er noch auf "Microsoft.AspNet." zeigt in "Microsoft.AspNetCore.".

Die Datei "Startup.cs" benötigt zusätzliche Parameter, damit diese mit Heroku dem Kestrel Webserver sagen kann mit welchem Port gearbeitet werden kann:

Ist alles durchgeführt, dann sollte es wie folgt aussehen:

Anschließend führen wir einen "Commit" und "Push" in GIT aus:

Der Build und das automatische Deployment schlägt allerdings fehl in Heroku. Das war allerdings auch zu erwarten, da hier noch nicht Core 1.0.0 verwendet wird:

Das Buildpack muss hierfür angepasst werden "https://github.com/noliar/dotnet-buildpack":

Anschließend ist das Deployment neu zu starten:

Anschließend ist die Applikation auf Version 1.0.0 upgradet:

Der aktuelle Code Stand ist in folgenden Branch festgehalten: BranchLink

Vorbereitung der Environment Variablen

Damit sich die DotNet Applikation verbinden kann, müssen die Verbindungsdaten in eine Environment Variable gespeichert werden, mit der DotNet umgehen kann.

Hierfür legen wir zunächst einen Eintrag in Heroku einen Eintrag mit folgenden Format an:

Server=ServerName;Port=5432;Database=DatenbankName;User Id=UserKennung;Password=Passwort;Timeout=15;SslMode=Require;Trust Server Certificate=true;  

ServerName, DatenbankName, UserKennung und Passwort nehmen wir von Heroku Eintrag "DATABASEURL" und übertragen diesen in einen für unsere Applikation "PGSSAMPLE1":

Unter dem gleichen Namen und gleichen Wert wird er Windows gespeichert. Wir gehen zunächst in die System und klicken auf Umgebungsvariablen:

Und machen hier einen neuen Eintrag:

Anschließend startet man Visual Studio Code neu und prüft, ob die Umgebungsvariable nun zur Verfügung steht:

Verbinden der Datenbank

Als nächster Schritt ist PostgreSQL einzubinden:

Als nächstes fügen wir die Abhängigkeit in "Startup.cs" ein:

Zusätzlich sagen wir in der ConfigureServices mit dem Dependancy Injector, dass wir eine Applikation einrichten wollen:

Nun ist ein neues Verzeichnis anzulegen "Entities" und dort die Klasse "ApplicationDbContext":

Zum ersten Test konfigurieren wir eine kleine Datenbank, die eine Liste von Studenten aufnehmen kann:

using Microsoft.EntityFrameworkCore;

namespace herokumxnet.Entities  
{
    public class ApplicationDbContext : DbContext
    {
        public ApplicationDbContext(DbContextOptions options) : base(options)
        {
        }

        public DbSet<Student> Students { get; set; }
    }

    public class Student {
        public int Id  { get; set; }

        public string FirstName  { get; set; }

        public string LastName  { get; set; }         
    }
}

Als nächstes die Migration hinzufügen:

Anschließend sind die Migrations angelegt unter dem Verzeichnis "Migrations":

Und anschließend Update Database:

Ist alles erfolgreich gelaufen, dann sehen wir den Eintrag und Students auch in HeidiQSL:

Damit Daten vorhanden sind füge ich zwei Zeilen ein:

In HeidiSQL wir können anschließend das Ergebnis sehen:

Erweiterung des Controller um eine Methode zur Datenanzeige

Zunächst erweitert ich den HomeController um eine Methode "Students". Dieser soll einen String "test" zurück geben:

Anschließend wird mit der URL http://localhost:5000/Home/Students der String auch angezeigt:

Umbau der Entities und Zugriff aus dem Controller herstellen

Damit der Aufbau ein wenig ordentlicher ist, verschieben wir nun die Studenten Klasse in eine eigene Datei:

Der Datenbank Kontext enthält anschließend nur eine Klassen:

Für den Zugriff richten wir eine Service Klasse in einem eigenen neuen Verzeichnis ein:

Die Datei "Student.cs" bekommt folgenden Inhalt:

using System.Collections.Generic;  
using System.Linq;  
using herokumxnet.Entities;

namespace herokumxnet.Services  
{
    public interface IStudentData {
        IEnumerable<Student> GetAll();
    }

    public class StudentData : IStudentData {

         private readonly ApplicationDbContext _context;

        public StudentData(ApplicationDbContext context)
        {
            _context = context;
        }

        public IEnumerable<Student> GetAll()
        {
            return _context.Students.ToList();
        }

    }
}

Die Definition der Instanz ist notwendig, damit wir mittels Dependancy Injection den Zugriff ermöglichen können.

In der "ConfigureService" Methode in der "Startup.cs" Klasse definieren wir diesen Server, so dass wir diesen für die Dependancy Injection verwenden können:

Anschließend können wir den Service mittels Injection im Konstruktor dem "HomeController.cs" hinzufügen:

Die Students Methode kann nun umgeschrieben werden und vom Service alle Studenten geholt werden:

Nun können wir den Ablauf testen:

Der Browser liefert anschließend das richtige JSON-Ergebnis:

Deployment auf HEROKU

Anschließend werden die Änderung in GIT Committed:

Anschließend mit Push ins GIT Repository:

Heroku startet automatisch das Deployment und nach ein paar Minuten ist auch schon die Funktion inkl. PostgreSql Anbindung live:

Fazit

Mit dem neuen DotNet Framework und ASP.Net 5 kann man jetzt auch einfach seine WebSeiten bei http://heroku.com hosten lassen.

Man benötigt nicht unbedingt einen Microsoft SQL Server, wie dieser beispielsweise bei Azure angeboten wird, sondern kann für einfachere Aufgaben auch die günstige PostgreSQL Datenbank verwenden, wie man sich bei Heroku als AddIn hinzufügen kann.

Der aktuelle Source-Code hierzu steht unter folgenden Link in meinen Git-Repository bereit: https://github.com/smoki99/dotnetsample/tree/samplev320160728