Используя localdb в SQL в службе Windows

у меня есть очень маленькое тестовое приложение, в котором я пытаюсь установить службу Windows и создать базу данных LocalDB во время процесса установки, а затем подключиться к этой базе данных LocalDB при запуске службы Windows.

я сталкиваюсь с огромными проблемами при подключении к экземпляру LocalDB из моей службы Windows.

мой процесс установки точно такой:

  1. выполнить установки .msi-файл, который запускает процесс msiexec как NT AUTHORITYсистемная учетная запись.

  2. запустите пользовательское действие для выполнения SqlLocalDB.exe со следующими командами:

    • sqllocaldb.exe создать MYINSTANCE
    • sqllocaldb.exe поделиться MYINSTANCE MYINSTANCESHARE
    • sqllocaldb.exe запустить MYINSTANCE
  3. запустите пользовательское действие C#, используя ADO.NET (система.Данные.SqlConnection) для выполнения следующих действий:

    • подключиться к следующая строка подключения,Data Source=(localdb)MYINSTANCE; Integrated Security=true
    • создать базу данных TestDB
    • использовать TestDB
    • СОЗДАТЬ ТАБЛИЦУ ...
  4. запустите службу Windows до завершения установки.

  5. служба Windows устанавливается в учетную запись LocalSystem и также запускается как учетная запись пользователя NT AUTHORITYSYSTEM.

  6. служба пытается подключиться, используя ту же строку подключения выше.

Я постоянно получаю следующую ошибку при попытке открыть соединение с вышеуказанной строкой соединения из службы Windows:

5 ответов


недавно я смог решить аналогичную проблему в нашем установщике WiX. У нас есть служба Windows, работающая под системной учетной записью, и установщик, где LocalDB-хранилище является одним из вариантов конфигурации базы данных. В течение некоторого времени (на самом деле пару лет) обновления и обслуживание продукта работали довольно хорошо, без проблем, связанных с LocalDB. Мы используем V11 по умолчанию.0 экземпляр, созданный в системном профиле в C:\Windows\System32\config дерево и база данных, указанная через AttachDbFileName, созданный в дереве ALLUSERSPROFILE. Поставщик БД настроен на использование проверки подлинности Windows. У нас также есть пользовательское действие в installer, запланированное как отложенное / не олицетворение, которое запускает обновления схемы БД.

все это работало нормально до недавнего времени. После еще одной связки обновлений БД наш новый выпуск начал терпеть неудачу после обновления по сравнению с прежним-служба не смогла начать, сообщая о печально известной " сетевой или конкретной ошибке, связанной с экземпляром установка соединения с SQL Server " (ошибка 50) ошибка.

при исследовании этой проблемы стало очевидно, что проблема заключается в том, что WiX выполняет пользовательские действия. Хотя не олицетворенные CA-s работают под системной учетной записью, профиль реестра и среда остаются текущим пользователем (я подозреваю, что WiX загружает их добровольно при подключении к сеансу пользователя). Это приводит к неправильному расширению пути из переменной LOCALAPPDATA-служба получает системный профиль один, но ЦС обновления схемы работает с ЦС пользователя.

так вот два возможных решения. Первый прост, но слишком навязчив для системы пользователя - с cmd.exe запускается через psexec, воссоздает сломанный экземпляр под системной учетной записью. Это не вариант для нас, так как пользователь может иметь другие базы данных, созданные в версии 11.0 экземпляр, который является публичным. Второй вариант предполагал много рефакторинга, но ничего не болит. Вот что нужно сделать, чтобы правильно запустить обновления схемы DB с LocalDB в WiX CA:

  1. настройте свой ЦС как отложенный / не олицетворяющий (должен работать под системной учетной записью);
  2. исправить среду, чтобы указать пути системного профиля:

    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. загрузить профиль учетной записи системы. Я использовал 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. самое главное - рефакторинг кода для выполнения необходимых обновлений БД в дочернем процессе. Это может быть простая exe-оболочка над DLL установщика или автономная утилита, если у вас уже есть один.

P.S. всех этих трудностей можно было бы избежать, если бы только Microsoft разрешила использовать выбор места для создания экземпляров LocalDB с помощью опции командной строки. Как, например, утилиты initdb/pg_ctl Postgres.


подбирая из комментариев по вопросу, вот некоторые области, чтобы посмотреть. Некоторые из них уже ответили в этих комментариях, но я документирую здесь для других, если информация может быть полезной.


  • какая версия .Net используется? Здесь это 4.5.1 (хорошо), но более ранние версии не могли обрабатывать предпочтительную строку соединения (т. е. @"(localdb)\InstanceName"). Следующая цитата взята из ссылки, указанной выше:

    если приложение использует версию .NET до версии 4.0.2, необходимо подключиться непосредственно к именованному каналу На localdb.

    и согласно странице MSDN для объект sqlconnection.Параметр connectionString:

    начиная с .NET Framework 4.5, вы также можете подключиться к базе данных LocalDB следующим образом:

    server=(localdb)\myInstance

  • пути:

    • случаях: C:\Users{вход в систему Windows}\AppData\Local\Microsoft\Microsoft SQL Server локальный DB\Instances

    • базы данных:

      • создано через SSMS или прямое соединение:C:\Users{Вход В Систему Windows}\Documents или C:\Users{Вход В Систему Windows}
      • создано с помощью Visual Studio:C:\Users{вход в систему Windows}\AppData\Local\Microsoft\VisualStudio\SSDT


  • начальный Проблема
    • симптомы:
      • файлы базы данных (.mdf и .ldf) создано в ожидаемом месте:
        C:\Windows\System32\config\systemprofile
      • файлы экземпляров, созданные в неожиданном месте:
        C:\Users\{текущий пользователь}\AppData\Local\Microsoft\Microsoft SQL Server локальная БД\экземпляры
    • причина (Примечание взято из страницы MSDN" Sqllocaldb Utility", которая связана выше; акцент мой):

      операции, отличные от 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
      И убедитесь, что значение "владелец" является правильным. Является ли значение "Shared name" тем, чем оно должно быть? Если нет, то документация гласит:

      только администратор на компьютере может создать общий экземпляр LocalDB

    • в рамках настройки добавьте NT AUTHORITY\System учетная запись в качестве входа в систему, которая требуется, если эта учетная запись не отображается как "владелец" экземпляра:
      CREATE LOGIN [NT AUTHORITY\System] FROM WINDOWS; ALTER SERVER ROLE [sysadmin] ADD MEMBER [NT AUTHORITY\System];

    • Проверьте следующий файл для подсказок / деталей:
      C:\Users{Windows Логин}\папка AppData\местные\Майкрософт\корпорация Майкрософт SQL сервер локальной БД\инстансы\\МОЙЭКЗЕМПЛЯР ошибки.log

    • в конце концов вам может потребоваться создать фактическую учетную запись для создания и владения экземпляром и базой данных, а также запустить службу. LocalDB действительно предназначен для пользовательского режима, и есть ли какой-либо недостаток в том, что ваш сервис имеет свой собственный логин? И вам, вероятно, не нужно будет делиться экземпляром в этот момент.

      и на самом деле, как отметил Microsoft на SQL Server гггг Экспресс LocalDB страница MSDN:

      экземпляр localdb с принадлежащий встроенным учетным записям, таким как NT AUTHORITY\SYSTEM, может иметь проблемы с управляемостью из-за перенаправления файловой системы windows; вместо этого используйте обычную учетную запись windows в качестве владельца.

обновление (2015-08-21)

на основе отзывов от O. P., что с помощью обычной учетной записи Пользователя может быть проблематичным в определенных средах и иметь в виду исходную проблему экземпляра LocalDB, создаваемого в %LOCALAPPDATA% папка для пользователя, запускающего установщик (и не the на NT AUTHORITY\System), я нашел решение, которое, похоже, поддерживает цель простой установки (нет пользователя для создания) и не должно требовать дополнительного кода для загрузки системного профиля.

попробуйте использовать один из двух встроенных аккаунты то есть не the LocalSystem учетная запись (которая не поддерживает свою собственную информацию реестра. Использовать:

оба имеют свои папки профиля в: C:\Windows\ServiceProfiles

хотя я не смог протестировать через установщик, я тестировал вход в систему службы как НТ\орган записи networkservice установив экземпляр SQL Server Express 2014 для входа в систему под этой учетной записью и перезапустив службу SQL Server. Затем я пробежал следующее:

EXEC xp_cmdshell 'sqllocaldb c MyTestInstance -s';

и он создал экземпляр в:C:\Windows\ServiceProfiles\NetworkService\AppData\Local\Microsoft\Microsoft SQL Server локальные DB\экземпляры

затем я запустил следующее:

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

и он создал базу данных в: C:\Windows\ServiceProfiles\NetworkService


Я предлагаю использовать другую учетную запись пользователя, а не системную учетную запись, выполнив следующие действия: -

  • создайте новую учетную запись на машине и установите ее в качестве учетной записи под которым работает служба Windows. Это не лучшая практика, чтобы использовать системная учетная запись просто для запуска приложения, в любом случае, как разрешения являются чрезмерными.
  • убедитесь, что разрешения на файлы LocalDB установлены, чтобы позволить указанной учетной записи пользователя получить доступ к базе данных (и, таким образом продолжать используйте интегрированную безопасность)
  • убедитесь, что он работает, пытается подключиться к БД (после установки) под той же учетной записью пользователя, под управлением sqlcmd или студия управления в контексте указанного пользователя, затем подключение с помощью Integrated Безопасность для обеспечения его работы.

некоторые другие вещи, чтобы попробовать/подумать:

  • вы проверили журнал событий Windows, для любых событий, которые могут быть полезны для диагностических целей?
  • убедитесь, что если у вас есть какие-либо другие версии SQL Server (особенно до 2012 года), которые для средств командной строки, %PATH% не настроен на поиск более старой версии средств. Старые инструменты не поддерживают LocalDB.
  • можно также (в качестве альтернативы) настроить LocalDB для совместного использования с другими пользователями. Это включает совместное использование экземпляра, а затем предоставление доступа другим пользователям. См. раздел "проблемы с общим доступом" в этой статье:Устранение неполадок SQL Server 2012 Express Localdb с.

там же еще одна так статья это может содержать более полезную информацию в ссылках внутри (измените язык в URL с польского на английский, изменив pl-pl на en-us). Его работа вокруг использует учетные записи SQL Server, которые могут быть не в порядке в вашем случае.

Это также может быть полезно, поскольку это относится к запрещенным разрешениям безопасности и возможным разрешениям: https://dba.stackexchange.com/questions/30383/cannot-start-sqllocaldb-instance-with-my-windows-account


Тревор, проблема у вас есть с пользовательскими действиями MSI. Вы должны настроить их с помощью "Impersonate=false", иначе пользовательские действия будут выполняться в контексте текущего пользователя.

кстати, какой инструмент вы используете для создания установщика? В зависимости от используемого инструмента, не могли бы вы предоставить скриншоты или фрагменты кода конфигурации пользовательских действий?

принятый ответ с этого поста даст вам дополнительную информацию о различные варианты выполнения настраиваемых действий: запустите ExeCommand в customAction в режиме администратора в установщике Wix

дополнительную информацию о олицетворении вы найдете здесь: http://blogs.msdn.com/b/rflaming/archive/2006/09/23/768248.aspx


Я бы не создавал базу данных под экземпляром localdb системы. Я бы создал его под текущим пользователем, устанавливающим продукт. Это сделает жизнь намного проще, если вам нужно удалить или управления базой данных. Они могут сделать это через SQL management studio. В противном случае вам придется использовать psexc или что-то еще для запуска процесса под системной учетной записью для управления им.

после создания БД используйте указанную Вами опцию share. Тогда система счета может доступ к базе данных через сетевое имя.

sqllocaldb поделиться MSSqlLocalDb LOCAL_DB

при совместном использовании я заметил, что вам придется перезапустить локальный экземпляр БД, чтобы фактически получить доступ к БД через имя общего ресурса:

sqllocaldb остановить MSSQLLocalDB

sqllocaldb запустить MSSQLLocalDB

кроме того, вам может потребоваться добавить системную учетную запись в качестве устройства чтения и записи БД в базу данных ...

exec для db_datareader для процедуры sp_addrolemember , 'NT AUTHORITY\SYSTEM'