web-dev-qa-db-ja.com

WindowsサービスでのSQLLocalDBの使用

非常に小さなテストアプリケーションがあり、インストールプロセス中にWindowsサービスをインストールしてLocalDBデータベースを作成し、Windowsサービスの実行時にそのLocalDBデータベースに接続しようとしています。

WindowsサービスからLocalDBインスタンスに接続する際に大きな問題が発生しています。

私のインストールプロセスは次のとおりです。

  1. NT AUTHORITY\SYSTEMアカウントとしてmsiexecプロセスを実行するインストーラー.msiファイルを実行します。

  2. 次のコマンドを使用して、カスタムアクションを実行し、SqlLocalDB.exeを実行します。

    • sqllocaldb.exe create MYINSTANCE
    • sqllocaldb.exe共有MYINSTANCEMYINSTANCESHARE
    • sqllocaldb.exe start MYINSTANCE
  3. ADO.NET(System.Data.SqlConnection)を使用してカスタムC#アクションを実行し、次のアクションを実行します。

    • 次の接続文字列に接続します。Data Source=(localdb)\MYINSTANCE; Integrated Security=true
    • CREATE DATABASE TestDB
    • TestDBを使用する
    • CREATE TABLE.。
  4. インストーラーが終了する前にWindowsサービスを開始します。

  5. WindowsサービスはLocalSystemアカウントにインストールされるため、NT AUTHORITY\SYSTEMユーザーアカウントとしても実行されます。

  6. サービスは、上記で使用したものと同じ接続文字列を使用して接続を試みます。

Windowsサービス内から上記の接続文字列への接続を開こうとすると、常に次のエラーが発生します。

System.Data.SqlClient.SqlException(0x80131904):SQLServerへの接続の確立中にネットワーク関連またはインスタンス固有のエラーが発生しました。サーバーが見つからないか、アクセスできませんでした。インスタンス名が正しいこと、およびSQLServerがリモート接続を許可するように構成されていることを確認してください。 (プロバイダー:SQLネットワークインターフェイス、エラー:50-ローカルデータベースランタイムエラーが発生しました。指定されたLocalDBインスタンスは存在しません。

MsiインストーラのカスタムアクションとWindowsサービスの両方が同じWindowsユーザーアカウントで実行されているため、これはイライラします(確認したところ、どちらもNT AUTHORITY\Systemです)。したがって、最初の機能が機能し、2番目の機能が機能しない理由は私を超えています。

カスタムアクションとWindowsサービスで使用される接続文字列を共有名(localdb)\.\MYINSTANCESHAREを使用するように変更しようとしましたが、Windowsサービスからまったく同じエラーが発生します。

WindowsサービスがログオンするユーザーアカウントをWindowsユーザーアカウントに変更しようとしました。これは、最初にコマンドを実行してそのインスタンスのSQLサーバーログインに追加する限り機能します。

また、コンソールアプリケーションを実行して、共有名の接続文字列に接続してみましたが、それも機能します。

また、SQL Server Management Studioから共有名に接続してみましたが、それでも機能します。

しかし、これらの方法のどれも私の問題を本当に解決しません。 Windowsサービスが必要なのは、コンピューターが起動するとすぐに起動し(ユーザーがログオンしていなくても)、どのユーザーアカウントにログインしていても起動するためです。

WindowsサービスはLocalDBプライベートインスタンスにどのように接続しますか?

SQL Server 2014 ExpressLocalDBを使用しています。

13
Trevor Elliott

最近、WiXインストーラーで同様の問題を解決することができました。 SYSTEMアカウントで実行されるWindowsサービスと、LocalDBベースのストレージがデータベース構成のオプションの1つであるインストーラーがあります。しばらくの間(実際には数年)、製品のアップグレードとサービスは非常にうまく機能し、LocalDBに関連する問題はありませんでした。 C:\ Windows\System32\configツリーのSYSTEMプロファイルに作成されたデフォルトのv11.0インスタンスと、ALLUSERSPROFILEツリーに作成されたAttachDbFileNameを介して指定されたデータベースを使用しています。 DBプロバイダーは、Windows認証を使用するように構成されています。また、DBスキーマの更新を実行する、遅延/非偽装としてスケジュールされたカスタムアクションがインストーラーにあります。

最近まで、これはすべてうまくいきました。別のDB更新の束の後、以前のリリースをアップグレードした後、新しいリリースが失敗し始めました-サービスを開始できず、悪名高い「SQLServerへの接続の確立中にネットワーク関連またはインスタンス固有のエラーが発生しました」(エラー50 )障害。

この問題を調査したところ、WiXがカスタムアクションを実行する方法に問題があることが明らかになりました。偽装されていないCAはSYSTEMアカウントで実行されますが、レジストリプロファイルと環境は現在のユーザーのもののままです(ユーザーのセッションに接続するときにWiXがこれらを任意にロードすると思われます)。これにより、LOCALAPPDATA変数から誤ったパスが展開されます。サービスはSYSTEMプロファイル1を受け取りますが、スキーマ更新CAはユーザーのパスで機​​能します。

したがって、ここに2つの可能な解決策があります。最初のものは単純ですが、ユーザーのシステムに煩わしすぎます-cmd.exeをpsexec経由で開始し、SYSTEMアカウントで壊れたインスタンスを再作成します。ユーザーが公開されているv11.0インスタンスで作成された他のデータベースを持っている可能性があるため、これは私たちにとってオプションではありませんでした。 2番目のオプションは、多くのリファクタリングを想定していましたが、何も害はありませんでした。 WiXCAのLocalDBでDBスキーマの更新を適切に実行するために行うことは次のとおりです。

  1. CAを遅延/非偽装として構成します(SYSTEMアカウントで実行する必要があります)。
  2. SYSTEMプロファイルパスを指すように環境を修正します。

    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. SYSTEMアカウントプロファイルをロードします。次のように、LogonUser/LoadUserProfileネイティブAPIメソッドを使用しました。

    [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;
    }
    

    これをIDisposableクラスでラップし、usingステートメントとともに使用してコンテキストを構築します。

  4. 最も重要なのは、コードをリファクタリングして、子プロセスで必要なDB更新を実行することです。これは、インストーラーDLLに対する単純なexeラッパー、またはスタンドアロンユーティリティ(既にある場合)の場合があります。

P.S. Microsoftだけがコマンドラインオプションを使用してLocalDBインスタンスを作成する場所を選択できるようにすれば、これらすべての問題を回避できます。たとえば、Postgresのinitdb/pg_ctlユーティリティのように。

3
denis.gz

質問へのコメントからピックアップして、ここに見るべきいくつかの領域があります。これらのいくつかはすでにそれらのコメントで回答されていますが、情報が役立つかもしれない場合に備えて、他の人のためにここに文書化しています。

  • SQL Server Express LocalDBに関する優れた情報源については、こちらを確認してください。


  • どのバージョンの.Netが使用されていますか?ここでは4.5.1(良好)ですが、以前のバージョンでは優先接続文字列(つまり、@"(localdb)\InstanceName")を処理できませんでした。次の引用は、上記のリンクから抜粋したものです。

    アプリケーションが4.0.2より前のバージョンの.NETを使用している場合は、LocalDBの名前付きパイプに直接接続する必要があります。

    そして、MSDNページによると SqlConnection.ConnectionString

    .NET Framework 4.5以降、次のようにLocalDBデータベースに接続することもできます。

    server=(localdb)\\myInstance

  • パス:

    • インスタンス:C:\ Users {Windows Login}\AppData\Local\Microsoft\Microsoft SQL Server Local DB\Instances

    • データベース:

      • SSMSまたは直接接続を介して作成:C:\ Users {Windows Login}\DocumentsまたはC:\ Users { Windowsログイン}
      • Visual Studio経由で作成:C:\ Users {Windows Login}\AppData\Local\Microsoft\VisualStudio\SSDT


  • 初期の問題
    • 症状:
      • 予想される場所に作成されたデータベースファイル(_.mdf_および_.ldf_):
        C:\ Windows\System32\config\systemprofile
      • 予期しない場所に作成されたインスタンスファイル:
        C:\ Users\{current user}\AppData\Local\Microsoft\Microsoft SQL Server Local DB\Instances
    • 原因(上記にリンクされている「SqlLocalDBUtility」MSDNページからのメモ。私の強調):

      Start以外の操作は、現在ログインしているユーザーに属するインスタンスでのみ実行できます。


  • 試すべきこと:

    • データベースを指定する接続文字列(ただし、エラーがインスタンスに接続できないことに関するものである場合は、ロングショットになる可能性があります)。
    • "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"

    • サービスは実行されていますか?コマンドプロンプトから以下を実行します。
      _TASKLIST /FI "IMAGENAME eq sqlservr.exe"_
      「セッション名」列の「コンソール」の下にリストされているはずです。

    • コマンドプロンプトから以下を実行します。
      _sqllocaldb.exe info MYINSTANCE_
      そして、「所有者」の値が正しいことを確認します。 「共有名」の値はどうあるべきですか?そうでない場合、ドキュメントには次のように記載されています。

      LocalDBの共有インスタンスを作成できるのは、コンピューターの管理者のみです。

    • セットアップの一部として、_NT AUTHORITY\System_アカウントをシステムへのログインとして追加します。これは、このアカウントがインスタンスの「所有者」として表示されていない場合に必要です。
      _CREATE LOGIN [NT AUTHORITY\System] FROM WINDOWS; ALTER SERVER ROLE [sysadmin] ADD MEMBER [NT AUTHORITY\System];_

    • 手がかり/詳細については、次のファイルを確認してください。
      C:\ Users {Windows Login}\AppData\Local\Microsoft\Microsoft SQL Server Local DB\Instances\MYINSTANCE\error.log

    • 最終的には、インスタンスとデータベースを作成して所有し、サービスを実行するために、実際のアカウントを作成する必要がある場合があります。 LocalDBは実際にはユーザーモードであることが意図されており、サービスに独自のログインを持たせることの欠点はありますか?そして、おそらくその時点でインスタンスを共有する必要はないでしょう。

      実際、Microsoftが SQL Server YYYY Express LocalDB MSDNページで指摘しているように:

      NT AUTHORITY\SYSTEMなどの組み込みアカウントが所有するLocalDBのインスタンスでは、Windowsファイルシステムのリダイレクトが原因で管理性の問題が発生する可能性があります。代わりに、所有者として通常のWindowsアカウントを使用してください。

更新(2015-08-21)

通常のユーザーアカウントの使用は特定の環境で問題になる可能性があるというOPからのフィードバックに基づいており、インストーラーを実行しているユーザーの_%LOCALAPPDATA%_フォルダーに作成されるLocalDBインスタンスの元の問題を念頭に置いてください(およびnotNT AUTHORITY\System)の_%LOCALAPPDATA%_フォルダー、私は次のような解決策を見つけました簡単なインストール(ユーザーが作成しない)を目的としており、SYSTEMプロファイルをロードするために追加のコードを必要としないようにする必要があります。

notLocalSystem アカウント(独自のレジストリ情報を保持しない)である2つの組み込みアカウントのいずれかを使用してみてください。次のいずれかを使用してください。

どちらにもプロファイルフォルダがあります:C:\ Windows\ServiceProfiles

インストーラーを介してテストすることはできませんでしたが、SQL Server Express 2014インスタンスを設定して、NT AUTHORITY\NetworkServiceとしてログオンしているサービスをテストしました。このアカウントとしてログオンし、SQLServerサービスを再起動します。次に、以下を実行しました。

_EXEC xp_cmdshell 'sqllocaldb c MyTestInstance -s';
_

そして、インスタンスを次の場所に作成しました:C:\ Windows\ServiceProfiles\NetworkService\AppData\Local\Microsoft\Microsoft SQL Server Local DB\Instances

次に、以下を実行しました。

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

そしてそれはデータベースを作成しました:C:\ Windows\ServiceProfiles\NetworkService

13
Solomon Rutzky

Trevor、あなたが抱えている問題はMSIカスタムアクションにあります。 「Impersonate = false」を使用して構成する必要があります。そうしないと、カスタムアクションが現在のユーザーコンテキストで実行されます。

ところで、インストーラーの作成にどのツールを使用していますか?使用するツールに応じて、カスタムアクション構成のスクリーンショットまたはコードスニペットを提供していただけますか?

この投稿から受け入れられた回答は、さまざまなカスタムアクション実行の選択肢に関する追加情報を提供します: Wixインストーラーの管理者モードとしてcustomActionでExeCommandを実行

偽装に関する追加情報はここにあります: http://blogs.msdn.com/b/rflaming/archive/2006/09/23/768248.aspx

0
Rolo

次の手順を実行して、システムアカウントではなく、別のユーザーアカウントを使用することをお勧めします。

  • マシン上に新しいアカウントを作成し、それをWindowsサービスが実行されるアカウントに設定します。とにかく、アクセス許可が過剰であるため、アプリケーションを実行するためだけにシステムアカウントを使用することはお勧めできません。
  • localDBファイルのアクセス許可が、当該ユーザーアカウントがデータベースにアクセスできるように設定されていることを確認します(したがって、統合セキュリティを引き続き使用します)
  • 同じユーザーアカウントでDB(インストール後)に接続して、そのユーザーのコンテキストでsqlcmdまたはManagementStudioを実行し、Integrated Securityに接続して、機能することを確認します。

試して/考慮すべき他のいくつかのこと:

  • 診断目的に役立つ可能性のあるイベントがないか、Windowsイベントログを確認しましたか?
  • 他のバージョンのSQLServer(特に2012より前)がある場合は、コマンドラインツールの場合、%PATH%が古いツールバージョンを最初に検索するように設定されていないことを確認してください。古いツールはLocalDBをサポートしていません。
  • (代替として)LocalDBを他のユーザーと共有するように設定することも可能です。これには、インスタンスを共有してから、他のユーザーにアクセスを許可することが含まれます。この記事の「共有の問題」セクションを参照してください: SQL Server 2012 Express LocalDBのトラブルシューティング

別のSO記事 内のリンクにいくつかのより有用な情報が含まれている可能性があります(pl-plをに変更してURLの言語をポーランド語から英語に変更します)もありますen-us)。彼の回避策はSQL Serverアカウントを使用することですが、あなたの場合は問題ないかもしれません。

これは、拒否されているセキュリティ権限と考えられる解決策に関連しているため、役立つ場合もあります。 https://dba.stackexchange.com/questions/30383/cannot-start-sqllocaldb-instance-with-my-windows -アカウント

0
CJBS

システムのlocaldbインスタンスの下にデータベースを作成しません。製品をインストールしている現在のユーザーの下で作成します。これにより、データベースを削除または管理する必要がある場合に、作業がはるかに簡単になります。彼らはSQLManagementStudioを介してこれを行うことができます。それ以外の場合は、psexcなどを使用して、SYSTEMアカウントでプロセスを起動して管理する必要があります。

データベースが作成されたら、前述の共有オプションを使用します。その後、SYSTEMアカウントは、共有名を介してデータベースにアクセスできます。

sqllocaldb共有MSSqlLocalDbLOCAL_DB

共有するときに、共有名を使用して実際にデータベースにアクセスするには、ローカルのデータベースインスタンスを再起動する必要があることに気付きました。

sqllocaldbはMSSQLLocalDBを停止します

sqllocaldb start MSSQLLocalDB

また、データベースにデータベースリーダーおよびライターとしてSYSTEMアカウントを追加する必要がある場合があります...

EXEC sp_addrolemember db_datareader、 'NT AUTHORITY\SYSTEM'

0
Lars Fiedler