Zugriff auf MSSQL mit dotnet unter Linux

Seit Microsoft Dotnet Core auch unter Linux zur Verfügung stellt, ist es ein leichtest Applikationen mit C# auch auf dieser Plattform laufen zu lassen. Möchte man eine bestehende Applikation allerdings portieren stösst man auf verschiedene Hindernisse, welche nicht so offensichtlich sind.

Viele C#-Applikationen verwenden eine CRUD-Architektur, sie verwenden Applikationslogik und greifen auf eine SQL-Server Datenbank zu. Innerhalb einer Windows-Domäne kommt dabei häufig Integrated Security zum Einsatz. Dies hat den Vorteil, dass in der Applikation keine Passwörter für den Zugriff auf Ressourcen aller Art hinterlegt werden müssen.

Dasselbe Konzept klappt auch unter Linux und damit auch innerhalb von Docker-Container, wenn man es richtig anstellt. Das folgende Beispiel zeigt, wie man in einer C#-Consolen Applikation auf eine SQL-Server Datenbank zugreift.

Rahmenbedingungen

Auf der Linux-Kiste wird ein Dotnet Core SDK benötigt um das Projekt erstellen, builden und laufen lassen zu können. Letzteres benötigt kein SDK, man kann eine Dotnet-Core Applikation auch standalone unter Linux laufen lassen.

Dotnet-Projekt erstellen

dotnet new console -lang "C#" -n SqlClientTest
cd SqlClientTest

Sicherstellen, dass im csproj-File die LangVersion-Einstellung vorhanden ist:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>netcoreapp2.2</TargetFramework>
    <LangVersion>latest</LangVersion>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="System.Data.SqlClient" Version="4.6.0" />
  </ItemGroup>
</Project>

Beispielprogramm

using System;
using System.Threading.Tasks;
using System.Data.SqlClient;

class Program
{
	private const string TestConnectionString =
		"data source=db.beispiel.local;Initial Catalog=Test;MultipleActiveResultSets=True;App=SqlClientTest;Integrated Security=true";
	private const string ProdConnectionString =
		"data source=db.beispiel.local;Initial Catalog=Test;MultipleActiveResultSets=True;App=SqlClientTest;Integrated Security=true";

	static async Task Main(string[] args)
	{
		var useProd = args != null && args.Length > 0 && args[0].Equals("prd", StringComparison.InvariantCultureIgnoreCase);
		var connectionString = useProd ? ProdConnectionString : TestConnectionString;
		
		try {
			Console.WriteLine("Query SQL-Server Edition");
			await RunQuery(connectionString);
		} catch(Exception ex) {
			Console.Error.WriteLine(ex.Message);
		}
	}

	static async Task RunQuery(string connectionString)
	{
		Console.WriteLine(connectionString);
		using (var db = new SqlConnection(connectionString)) {
			await db.OpenAsync();

			var sqlQuery = "select serverproperty('MachineName') as  MachineName, serverproperty('Edition') as Edition, serverproperty('ProductVersion') as ProductVersion";
			var cmd = new SqlCommand(sqlQuery, db);
			var rdr = await cmd.ExecuteReaderAsync();
			while (await rdr.ReadAsync()) {
				Console.WriteLine($"MachineName={rdr[0]}, Edition={rdr[1]}, Version={rdr[2]}");
			}
		}
	}
}

Nuget-Package System.Data.SqlClient hinzufügen

dotnet add package System.Data.SqlClient -s http://packages.argus.local/nuget/Argus/

Projekt kompilieren

dotnet build

Projekt laufen lassen

user@testhost:~/thomy/SqlClientTest$ dotnet run
Query SQL-Server Edition
data source=db.beispiel.local;Initial Catalog=Test;MultipleActiveResultSets=True;App=SqlClientTest;Integrated Security=true
MachineName=dbserver, Edition=Standard Edition (64-bit), Version=13.0.4466.4
a

Preisfrage: Was fehlt, damit genau das läuft? Ohne weiteres Zutun wird da eher eine Fehlermeldung kommen wie:

Cannot authenticate using Kerberos. Ensure Kerberos has been initialized on the client with 'kinit' and a Service Principal Name has been registered for the SQL Server to allow Kerberos authentication.

Die Fehlermeldung liefert auch schon die Antwort: Kerberos installieren und mit kinit ein Ticket vom KDC abholen.

Wenn nicht bereits vorhanden, stellt man sicher, dass das Package krb5-user auf dem System installiert ist.

apt install krb5-user

Bei der Installation wird eine Beispiel-Konfiguration in /etc/krb5.conf angelegt, welche man aber besser noch etwas anpasst. Ich verwende folgende Konfiguration:

# /etc/krb5.conf -- Kerberos V5 general configuration.
#

[appdefaults]
    default_lifetime      = 25hrs
    krb4_convert          = false
    krb4_convert_524      = false

[libdefaults]
    default_realm         = BEISPIEL.LOCAL
    ticket_lifetime       = 25h
    renew_lifetime        = 7d
    forwardable           = true
    noaddresses           = true
    allow_weak_crypto     = true
    rdns                  = false

[realms]
     BEISPIEL.LOCAL = {
        kdc            = ads1.beispiel.local
        kdc            = ads2.beispiel.local
        default_domain = beispiel.local
    }

[domain_realm]
    argus.local    = ARGUS.LOCAL

[logging]
    kdc          = SYSLOG:NOTICE
    admin_server = SYSLOG:NOTICE
    default      = SYSLOG:NOTICE

Mit kinit kann man sich nun ein Ticket vom KDC abholen, danach funktioniert auch die SQL-Server-Connection

user@testhost:~/thomy/SqlClientTest$ kinit vorname.nachname
Password for vorname.nachname@BEISPIEL.LOCAL:
Speichere in deinen Favoriten diesen permalink.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert

301 Moved Permanently

Moved Permanently

The document has moved here.