wake-up-neo.net

Verwenden von SQL LocalDB in einem Windows-Dienst

Ich habe eine sehr kleine Testanwendung, in der ich versuche, einen Windows-Dienst zu installieren und eine LocalDB-Datenbank während des Installationsvorgangs zu erstellen, und dann eine Verbindung zu dieser LocalDB-Datenbank herzustellen, wenn der Windows-Dienst ausgeführt wird.

Ich habe große Probleme mit der Verbindung zu einer LocalDB-Instanz von meinem Windows-Dienst.

Mein Installationsprozess ist genau so:

  1. Führen Sie eine .msi-Datei des Installationsprogramms aus, die den msiexec-Prozess als NT-AUTHORITY\SYSTEM-Konto ausführt.

  2. Führen Sie eine benutzerdefinierte Aktion aus, um SqlLocalDB.exe mit den folgenden Befehlen auszuführen:

    • sqllocaldb.exe erstellt MYINSTANCE
    • sqllocaldb.exe teilen MYINSTANCE MYINSTANCESHARE
    • sqllocaldb.exe startet MYINSTANCE
  3. Führen Sie eine benutzerdefinierte C # -Aktion mit ADO.NET (System.Data.SqlConnection) aus, um die folgenden Aktionen auszuführen:

    • Verbinden Sie sich mit der folgenden Verbindungszeichenfolge Data Source=(localdb)\MYINSTANCE; Integrated Security=true
    • CREATE DATABASE TestDB
    • USE TestDB
    • TABELLE ERSTELLEN ...
  4. Starten Sie den Windows-Dienst, bevor das Installationsprogramm abgeschlossen ist.

  5. Der Windows-Dienst ist auf dem LocalSystem-Konto installiert und wird daher auch als NT AUTHORITY\SYSTEM-Benutzerkonto ausgeführt.

  6. Der Dienst versucht, eine Verbindung mit derselben Verbindungszeichenfolge herzustellen, die oben verwendet wurde.

Wenn ich versuche, die Verbindung zu der obigen Verbindungszeichenfolge innerhalb des Windows-Diensts zu öffnen, erhalte ich regelmäßig die folgende Fehlermeldung:

System.Data.SqlClient.SqlException (0x80131904): Beim Herstellen einer Verbindung mit dem SQL Server ist ein netzwerkbezogener oder Instanzspezifischer Fehler aufgetreten. Der Server wurde nicht gefunden oder war nicht erreichbar. Stellen Sie sicher, dass Der Instanzname korrekt ist und dass SQL Server so konfiguriert ist, dass Remoteverbindungen zulässt. (Anbieter: SQL-Netzwerkschnittstellen, Fehler: 50 - Fehler bei der lokalen Datenbanklaufzeit. Die angegebene LocalDB-Instanz ist nicht vorhanden.

Dies ist frustrierend, da sowohl die benutzerdefinierte Aktion des msi-Installationsprogramms als auch der Windows-Dienst unter demselben Windows-Benutzerkonto ausgeführt werden (ich habe überprüft, dass beide NT-Berechtigung\System sind). Warum also das erste Werk und das zweite nicht funktioniert, liegt mir nicht.

Ich habe versucht, die in der benutzerdefinierten Aktion verwendeten Verbindungszeichenfolgen und den Windows-Dienst zu ändern, um den Freigabenamen (localdb)\.\MYINSTANCESHARE zu verwenden, und ich bekomme genau dieselbe Fehlermeldung vom Windows-Dienst.

Ich habe versucht, das Benutzerkonto zu ändern, bei dem sich der Windows-Dienst als mein Windows-Benutzerkonto anmeldet. Dies funktioniert, solange ich zum ersten Mal einen Befehl ausführen, um es den SQL Server-Anmeldungen für diese Instanz hinzuzufügen.

Ich habe auch versucht, eine Konsolenanwendung auszuführen und eine Verbindung mit der Verbindungszeichenfolge für den Freigabenamen herzustellen. Das funktioniert auch.

Ich habe auch versucht, eine Verbindung mit dem Freigabenamen von SQL Server Management Studio herzustellen, und das funktioniert auch.

Keine dieser Methoden löst jedoch wirklich mein Problem. Ich brauche einen Windows-Dienst, da er gestartet wird, sobald der Computer hochfährt (auch wenn sich kein Benutzer anmeldet) und unabhängig von dem angemeldeten Benutzerkonto gestartet wird.

Wie verbindet sich ein Windows-Dienst mit einer privaten Instanz von LocalDB?

Ich verwende SQL Server 2014 Express LocalDB.

13
Trevor Elliott

Ich konnte kürzlich ein ähnliches Problem in unserem WiX-Installationsprogramm lösen. Wir haben einen Windows-Dienst, der unter dem SYSTEM-Konto ausgeführt wird, und ein Installationsprogramm, bei dem der LocalDB-basierte Speicher eine der Optionen für die Datenbankkonfiguration ist. Produkt-Upgrades und -Service funktionierten einige Zeit (einige Jahre tatsächlich) einwandfrei, ohne Probleme mit LocalDB. Wir verwenden die standardmäßige v11.0-Instanz, die im SYSTEM-Profil in der Baumstruktur C:\Windows\System32\config erstellt wird, und eine über AttachDbFileName angegebene Datenbank, die in der Baumstruktur ALLUSERSPROFILE erstellt wird. Der DB-Provider ist für die Verwendung der Windows-Authentifizierung konfiguriert. Wir haben auch eine benutzerdefinierte Aktion im Installationsprogramm, die als verzögert/nicht anonymisiert geplant ist und die DB-Schemaaktualisierungen ausführt.

All das hat bis vor kurzem funktioniert. Nach einer weiteren Reihe von Datenbankaktualisierungen schlug unser neues Release nach einem Upgrade über den früheren Dienst fehl. Der Dienst konnte nicht gestartet werden und meldete infamous "Ein netzwerkbezogener oder instanzspezifischer Fehler beim Herstellen einer Verbindung zu SQL Server" (Fehler 50) ) Fehler.

Bei der Untersuchung dieses Problems stellte sich heraus, dass es sich bei WiX um benutzerdefinierte Aktionen handelt. Obwohl nicht imitierte CAs unter SYSTEM-Konto ausgeführt werden, bleiben das Registrierungsprofil und die Umgebung jenes des aktuellen Benutzers (Ich vermute, WiX lädt diese freiwillig, wenn sie an die Benutzersitzung angeschlossen wird). Dies führt dazu, dass ein falscher Pfad aus der LOCALAPPDATA-Variablen erweitert wird - der Dienst empfängt das SYSTEM-Profil, die Schema-Update-Zertifizierungsstelle arbeitet jedoch mit der des Benutzers.

Hier sind zwei mögliche Lösungen. Die erste ist einfach, aber für das System des Benutzers zu störend. Wenn cmd.exe über psexec gestartet wird, erstellen Sie die beschädigte Instanz unter dem Konto SYSTEM neu. Dies war keine Option für uns, da der Benutzer möglicherweise andere Datenbanken in der v11.0-Instanz erstellt hat, die öffentlich ist. Die zweite Option setzte viel Refactoring voraus, schadete aber nichts. Führen Sie die folgenden Schritte aus, um DB-Schemaaktualisierungen ordnungsgemäß mit LocalDB in WiX CA auszuführen:

  1. Konfigurieren Sie Ihre Zertifizierungsstelle als zurückgestellt/nicht anonymisiert (sollte unter dem Konto SYSTEM ausgeführt werden).
  2. Fixiere die Umgebung so, dass sie auf SYSTEM-Profilpfade verweist:

    var systemRoot = Environment.GetEnvironmentVariable("SystemRoot");
    Environment.SetEnvironmentVariable("USERPROFILE", String.Format(@"{0}\System32\config\systemprofile", systemRoot));
    Environment.SetEnvironmentVariable("APPDATA", String.Format(@"{0}\System32\config\systemprofile\AppData\Roaming", systemRoot));
    Environment.SetEnvironmentVariable("LOCALAPPDATA", String.Format(@"{0}\System32\config\systemprofile\AppData\Local", systemRoot));
    Environment.SetEnvironmentVariable("HOMEPATH", String.Empty);
    Environment.SetEnvironmentVariable("USERNAME", Environment.UserName);
    
  3. Laden Sie das SYSTEM-Kontoprofil. Ich habe LogonUser/LoadUserProfile native API-Methoden wie folgt verwendet:

    [DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    private static extern bool LogonUser(
        string lpszUserName,
        string lpszDomain,
        string lpszPassword,
        int dwLogonType,
        int dwLogonProvider,
        ref IntPtr phToken);
    
    [StructLayout(LayoutKind.Sequential)]
    struct PROFILEINFO
    {
        public int dwSize; 
        public int dwFlags;
        [MarshalAs(UnmanagedType.LPWStr)] 
        public String lpUserName; 
        [MarshalAs(UnmanagedType.LPWStr)] 
        public String lpProfilePath; 
        [MarshalAs(UnmanagedType.LPWStr)] 
        public String lpDefaultPath; 
        [MarshalAs(UnmanagedType.LPWStr)] 
        public String lpServerName; 
        [MarshalAs(UnmanagedType.LPWStr)] 
        public String lpPolicyPath; 
        public IntPtr hProfile; 
    }
    
    [DllImport("userenv.dll", SetLastError = true, CharSet = CharSet.Unicode)]
    [return: MarshalAs(UnmanagedType.Bool)]
    static extern bool LoadUserProfile(IntPtr hToken, ref PROFILEINFO lpProfileInfo);
    
    var hToken = IntPtr.Zero;
    var hProfile = IntPtr.Zero;
    bool result = LogonUser("SYSTEM", "NT AUTHORITY", String.Empty, 3 /* LOGON32_LOGON_SERVICE */, 0 /* LOGON32_PROVIDER_DEFAULT */, ref token);
    if (result)
    {
        var profileInfo = new PROFILEINFO();
        profileInfo.dwSize = Marshal.SizeOf(profileInfo);
        profileInfo.lpUserName = @"NT AUTHORITY\SYSTEM";
        if (LoadUserProfile(token, ref profileInfo))
            hProfile = profileInfo.hProfile;
    }
    

    Binden Sie dies in eine IDisposable-Klasse ein und verwenden Sie sie mit einer using-Anweisung, um einen Kontext zu erstellen.

  4. Das Wichtigste - Umgestalten Sie Ihren Code, um erforderliche DB-Aktualisierungen in einem untergeordneten Prozess durchzuführen. Dies kann ein einfacher Exe-Wrapper über Ihre Installer-DLL sein oder ein eigenständiges Dienstprogramm, sofern Sie bereits über ein solches Programm verfügen.

P.S. Alle diese Schwierigkeiten könnten vermieden werden, wenn nur Microsoft mithilfe der Befehlszeilenoption auswählen kann, wo LocalDB-Instanzen erstellt werden sollen. Wie die Dienstprogramme initdb/pg_ctl von Postgres.

3
denis.gz

Aus den Kommentaren der Frage können Sie einige Bereiche ablesen. Einige davon wurden bereits in diesen Kommentaren beantwortet, aber ich dokumentiere hier für andere, falls die Informationen hilfreich sein könnten.


  • Welche Version von .Net wird verwendet? Hier ist es 4.5.1 (gut), aber frühere Versionen konnten die bevorzugte Verbindungszeichenfolge nicht verarbeiten (d. H. @"(localdb)\InstanceName"). Das folgende Zitat stammt aus dem oben angegebenen Link:

    Wenn Ihre Anwendung eine .NET-Version vor 4.0.2 verwendet, müssen Sie eine direkte Verbindung mit der Named Pipe der LocalDB herstellen.

    Und entsprechend der MSDN-Seite für SqlConnection.ConnectionString :

    Ab .NET Framework 4.5 können Sie auch wie folgt eine Verbindung zu einer LocalDB-Datenbank herstellen:

    server=(localdb)\\myInstance

  • Pfade:

    • Instanzen: C:\Benutzer {Windows-Anmeldung}\AppData\Local\Microsoft\Microsoft SQL Server Lokale Datenbank\Instanzen

    • Datenbanken:

      • Erstellt über SSMS oder direkte Verbindung: C:\Benutzer {Windows Login}\Dokumente oder C:\Benutzer {Windows Login}
      • Erstellt über Visual Studio: C:\Benutzer {Windows-Anmeldung}\AppData\Local\Microsoft\VisualStudio\SSDT


  • Anfangsproblem
    • Symptome:
      • Datenbankdateien (.mdf und .ldf), die am erwarteten Speicherort erstellt wurden:
        C:\Windows\System32\config\systemprofile
      • Instanzdateien, die an einem unerwarteten Speicherort erstellt wurden:
        C:\Benutzer\{aktueller Benutzer}\AppData\Local\Microsoft\Microsoft SQL Server - Lokale Datenbank\Instanzen
    • Ursache (Hinweis von der oben verlinkten MSDN-Seite "SqlLocalDB Utility"; Hervorhebung von mir):

      Andere Vorgänge als Start können nur für eine Instanz ausgeführt werden, die zu aktuell angemeldeter Benutzer gehört.


  • Dinge zu versuchen:

    • Verbindungszeichenfolge, die die Datenbank angibt (möglicherweise eine lange Wartezeit, wenn der Fehler darin besteht, dass keine Verbindung zur Instanz hergestellt werden kann):
    • "Server=(LocalDB)\MYINSTANCE; Integrated Security=true ;AttachDbFileName=C:\Windows\System32\config\systemprofile\TestDB.mdf"
    • "Server=(LocalDB)\.\MYINSTANCESHARE; Integrated Security=true ;AttachDbFileName=C:\Windows\System32\config\systemprofile\TestDB.mdf"

    • Läuft der Dienst? Führen Sie an einer Eingabeaufforderung Folgendes aus:
      TASKLIST /FI "IMAGENAME eq sqlservr.exe"
      Es sollte wahrscheinlich unter "Konsole" für die Spalte "Sitzungsname" aufgeführt werden

    • Führen Sie an einer Eingabeaufforderung Folgendes aus:
      sqllocaldb.exe info MYINSTANCE
      Und vergewissern Sie sich, dass der Wert für "Owner" korrekt ist. Ist der Wert für "Shared name" so, wie er sein soll? Wenn nicht, heißt es in der Dokumentation: 

      Nur ein Administrator auf dem Computer kann eine gemeinsam genutzte Instanz von LocalDB erstellen

    • Fügen Sie als Teil des Setups das Konto NT AUTHORITY\System als Login zum System hinzu. Dies ist erforderlich, wenn dieses Konto nicht als "Eigentümer" der Instanz angezeigt wird:
      CREATE LOGIN [NT AUTHORITY\System] FROM WINDOWS; ALTER SERVER ROLE [sysadmin] ADD MEMBER [NT AUTHORITY\System];

    • Überprüfen Sie die folgende Datei auf Hinweise/Details:
      C:\Benutzer {Windows-Anmeldung}\AppData\Local\Microsoft\Microsoft SQL Server Lokale Datenbank\Instanzen\MYINSTANCE\error.log

    • Am Ende müssen Sie möglicherweise ein tatsächliches Konto erstellen, um die Instanz und die Datenbank zu erstellen und zu besitzen sowie Ihren Dienst auszuführen. LocalDB ist eigentlich für den Benutzermodus gedacht. Gibt es irgendwelche Nachteile, wenn Ihr Dienst über ein eigenes Login verfügt? Zu diesem Zeitpunkt müssten Sie die Instanz wahrscheinlich nicht teilen.

      In der Tat, wie von Microsoft auf der MSDN-Seite SQL Server YYYY Express LocalDB vermerkt:

      Eine Instanz von LocalDB , die sich im Besitz der integrierten Konten befindet, z. B. NT AUTHORITY\SYSTEM, kann aufgrund der Umleitung des Windows-Dateisystems Verwaltungsprobleme haben. Verwenden Sie stattdessen ein normales Windows-Konto als Eigentümer.

UPDATE (2015-08-21)

Basierend auf dem Feedback des OP kann die Verwendung eines regulären Benutzerkontos in bestimmten Umgebungen problematisch sein. UND unter Berücksichtigung des ursprünglichen Problems der LocalDB-Instanz, die im Ordner %LOCALAPPDATA% für den Benutzer erstellt wird, der das Installationsprogramm ausführt (und nicht. Im Ordner %LOCALAPPDATA% für NT AUTHORITY\System ) habe ich eine Lösung gefunden, die scheinbar einfach zu installieren ist (kein Benutzer muss erstellt werden) und sollte keinen zusätzlichen Code zum Laden des SYSTEM-Profils benötigen.

Versuchen Sie, eines der beiden integrierten Konten zu verwenden, nämlich nicht das Konto LocalSystem (das keine eigenen Registrierungsinformationen enthält. Verwenden Sie entweder:

Beide haben ihre Profilordner in: C:\Windows\ServiceProfiles

Während ich nicht über ein Installationsprogramm testen konnte, habe ich einen Dienst als NT AUTHORITY\NetworkService getestet, indem ich meine SQL Server Express 2014-Instanz so eingestellt habe, dass sie sich als dieses Konto anmeldet, und den SQL Server neu gestartet Bedienung. Ich lief dann folgendes:

EXEC xp_cmdshell 'sqllocaldb c MyTestInstance -s';

die Instanz wurde in folgendem Verzeichnis erstellt: C:\Windows\ServiceProfiles\NetworkService\AppData\Local\Microsoft\Microsoft SQL Server - Lokale Datenbank\Instanzen

Ich lief dann folgendes:

EXEC xp_cmdshell N'SQLCMD -S (localdb)\MyTestInstance -E -Q "CREATE DATABASE [MyTestDB];"';

und die Datenbank wurde erstellt unter: C:\Windows\ServiceProfiles\NetworkService

13
Solomon Rutzky

Trevor, das Problem, das Sie haben, sind die benutzerdefinierten MSI-Aktionen. Sie müssen sie mit "Impersonate = false" konfigurieren. Andernfalls werden die benutzerdefinierten Aktionen im aktuellen Benutzerkontext ausgeführt.

Übrigens, mit welchem ​​Tool erstellen Sie den Installer? Könnten Sie je nach verwendetem Tool Screenshots oder Code-Snippets Ihrer benutzerdefinierten Aktionskonfiguration bereitstellen? 

Die akzeptierte Antwort aus diesem Beitrag enthält einige zusätzliche Informationen zu den verschiedenen Ausführungsalternativen für benutzerdefinierte Aktionen: Führen Sie ExeCommand in customAction als Administratormodus im Wix Installer aus .

Weitere Informationen zum Identitätswechsel finden Sie hier: http://blogs.msdn.com/b/rflaming/archive/2006/09/23/768248.aspx

0
Rolo

Ich schlage vor, ein anderes Benutzerkonto und nicht das Systemkonto zu verwenden, indem Sie folgende Schritte ausführen: -

  • erstellen Sie ein neues Konto auf dem Computer und legen Sie das Konto fest, unter dem der Windows-Dienst ausgeführt wird. Es ist nicht empfehlenswert, das Systemkonto Nur zum Ausführen einer Anwendung zu verwenden, da die Berechtigungen Zu groß sind.
  • stellen Sie sicher, dass die Berechtigungen für die LocalDB-Dateien so festgelegt sind, dass das Benutzerkonto auf die Datenbank zugreifen kann (und fahren Sie daher fort mit Integrierte Sicherheit.)
  • stellen Sie sicher, dass es funktioniert, indem Sie versuchen, eine Verbindung mit der DB (einmal installiert) unter demselben Benutzerkonto herzustellen, indem Sie sqlcmd oder Management Studio im Kontext dieses Benutzers ausführen und dann eine Verbindung mit Integrated Security to herstellen Stellen Sie sicher, dass es funktioniert.

Einige andere Dinge zum Ausprobieren/Überlegen:

  • haben Sie das Windows-Ereignisprotokoll auf Ereignisse geprüft, die für Diagnosezwecke nützlich sein könnten?
  • Stellen Sie sicher, dass bei anderen Versionen von SQL Server (insbesondere vor 2012), dass% PATH% für die Befehlszeilentools nicht auf eine ältere Toolversion eingestellt ist. Ältere Tools unterstützen LocalDB nicht.
  • Alternativ kann LocalDB auch für andere Benutzer eingerichtet werden. Dazu müssen Sie die Instanz freigeben und dann anderen Benutzern Zugriff gewähren. Informationen hierzu finden Sie im Abschnitt "Probleme bei der Freigabe" in diesem Artikel: Problembehandlung bei SQL Server 2012 Express LocalDB .

Es gibt auch einen weiteren SO -Artikel , der einige nützliche Informationen in den Links enthalten kann (ändern Sie die Sprache in der URL von Polnisch in Englisch, indem Sie pl-pl in en-us ändern). Seine Problemumgehung verwendet SQL Server-Konten, die in Ihrem Fall möglicherweise nicht in Ordnung sind. 

Dies kann auch nützlich sein, da es sich auf die Ablehnung von Sicherheitsberechtigungen und mögliche Lösungen bezieht: https://dba.stackexchange.com/questions/30383/cannot-start-sqllocaldb-instance-with-my-windows-account

0
CJBS

Ich würde die Datenbank nicht unter der localdb-Instanz des Systems erstellen. Ich würde es unter dem aktuellen Benutzer erstellen, der das Produkt installiert. Dies erleichtert Ihnen das Leben, wenn Sie die Datenbank löschen oder verwalten müssen. Sie können dies durch das SQL Management Studio tun. Ansonsten müssen Sie psexc oder etwas anderes verwenden, um einen Prozess unter dem SYSTEM-Konto zu starten, um ihn zu verwalten.

Sobald die Datenbank erstellt wurde, verwenden Sie die von Ihnen erwähnte Freigabeoption. Das SYSTEM-Konto kann dann über den Freigabenamen auf die Datenbank zugreifen.

sqllocaldb share MSSqlLocalDb LOCAL_DB

Bei der Freigabe habe ich festgestellt, dass Sie die lokale Datenbankinstanz neu starten müssen, um tatsächlich über den Freigabenamen auf die Datenbank zuzugreifen:

sqllocaldb stop MSSQLLocalDB

sqllocaldb startet MSSQLLocalDB

Möglicherweise müssen Sie auch das SYSTEM-Konto als Datenbankleser und -schreiber zur Datenbank hinzufügen ...

EXEC sp_addrolemember db_datareader, 'NT AUTHORITY\SYSTEM'

0
Lars Fiedler