2010-03-02 10 views
6

Przy użyciu programu SQL Server Express 2005 wyposażone użytkownika instancji w ciągu połączenia jak poniżej:Jak zatrzymać wystąpienie użytkownika serwera Sql? (SQL Express użytkownika instancji pliki bazy danych zablokowane, nawet po zatrzymaniu SQL Express Service)

<add name="Default" connectionString="Data Source=.\SQLExpress; 
    AttachDbFilename=C:\My App\Data\MyApp.mdf; 
    Initial Catalog=MyApp; 
    User Instance=True; 
    MultipleActiveResultSets=true; 
    Trusted_Connection=Yes;" /> 

Uważamy, że nie można skopiować pliki baz danych MyApp.mdf i MyApp_Log.ldf (ponieważ są zablokowane) nawet po zatrzymaniu usługi SqlExpress, i muszą uciekać się do ustawienia usługi SqlExpress od automatycznego do ręcznego trybu uruchamiania, a następnie ponownego uruchomienia komputera, zanim będziemy mogli następnie skopiować pliki.

To było moje zrozumienie, że zatrzymanie usługi SqlExpress powinno również zatrzymać wszystkie wystąpienia użytkowników, co powinno zwolnić blokady tych plików. Ale tak się nie dzieje - czy ktoś mógłby rzucić trochę światła na sposób zatrzymania instancji użytkownika, tak, że pliki bazy danych nie są już zablokowane?


Aktualizacja

OK, ja przestałem być leniwy i wystrzelił w górę Process Explorer. Zamek był w posiadaniu sqlserver.exe - ale istnieją dwie instancje serwera SQL:

sqlserver.exe PID: 4680 User Name: DefaultAppPool 
sqlserver.exe PID: 4644 User Name: NETWORK SERVICE 

plik jest otwarty przez sqlserver.exe przykład z PID: 4680

Zatrzymanie „SQL Server (SQLEXPRESS) "usługa, zgasiła proces z PID: 4644, ale opuściła PID: 4680 sam.

Widząc jako właściciela pozostałego procesu był DefaultAppPool, następną rzeczą, którą próbowałem, było zatrzymanie IIS (ta baza danych jest używana z aplikacji ASP.Net). Niestety to też nie zabiło procesu.

Ręczne usunięcie pozostałego procesu serwera sql powoduje usunięcie uchwytu pliku otwartego w plikach bazy danych, umożliwiając ich skopiowanie/przeniesienie.

Niestety, chciałbym skopiować/przywrócić te pliki w niektórych zadaniach instalacyjnych przedinstalacyjnych/pocztowych instalatora WiX - jako takie miałem nadzieję, że może być sposób na osiągnięcie tego poprzez zatrzymanie usługi Windows, a następnie konieczność wypakowania zabić wszystkich wystąpień sqlserver.exe jako że stwarza pewne problemy:

  1. zabijając wszystkich sqlserver.exe instancji może mieć niepożądane consequencies dla użytkowników z innych instancji serwera SQL na swoich maszynach.
  2. Nie można łatwo ponownie uruchomić tych instancji.
  3. Wprowadza dodatkowe komplikacje do instalatora.

Czy ktoś ma jakiekolwiek dalsze przemyślenia na temat zamykania wystąpień serwera sql powiązanego z określoną instancją użytkownika?

+0

Zastanawia się nad wszystkimi odpowiedziami - najbardziej użyteczną odpowiedzią było użycie SSEUtil.exe. Chociaż musimy wywołać to z instalatora WiX, widzimy, że będzie to trochę problematyczne, tzn. Jeśli użytkownik uruchamiający instalator nie ma uprawnień do wykonania sp_dettach_db, co jest problemem niezależnie od używanej przez nas metody - możemy więc wystarczy ręcznie wykonać kilka czynności przed instalacją, aby sami mogli sobie z tym poradzić za pomocą SSEUtil.exe. – Bittercoder

+0

Jestem ciekawy, czy SSEUtil.exe nie działa w związku z problemem uprawnień? Jeśli użytkownik musi sam sobie z tym poradzić za pomocą SSEUtil, to czy instalator nie powinien wywoływać SSEUtil? – AMissico

+0

W moich testach nie wydaje się, aby tak było - wierzę, że było tak dlatego, że instalator nie został podniesiony podczas wywoływania SSEUtil ... ale nie miałem czasu, aby to jeszcze zbadać. Na marginesie stwierdziliśmy, że SSEUtil nie będzie poprawnie wyświetlał wszystkich aktywnych instancji użytkowników na Windows7 x64 z zainstalowanymi SqlServer i SqlServer Express, nawet jeśli przekazujemy parametr -s. \ SqlExpress. Nadal mamy kilka skrajnych przypadków do opracowania :) – Bittercoder

Odpowiedz

7

Use "SQL Server Express Utility" (SSEUtil.exe) lub polecenie odłączenia bazy danych używanej przez SSEUtil.

SQL Server Express Utility, SSEUtil jest narzędziem, które pozwala na łatwą interakcję z SQL Server, http://www.microsoft.com/downloads/details.aspx?FamilyID=fa87e828-173f-472e-a85c-27ed01cf6b02&DisplayLang=en

Również domyślny limit czasu, aby zatrzymać usługę po ostatnim zamknięciu połączenia to jedna godzina. W polu programowania możesz zmienić to ustawienie na pięć minut (minimalne dozwolone).

Ponadto, możesz mieć otwarte połączenie poprzez połączenia danych Eksploratora serwera programu Visual Studio, więc pamiętaj, aby odłączyć się od dowolnej bazy danych.

H:\Tools\SQL Server Express Utility>sseutil -l 
1. master 
2. tempdb 
3. model 
4. msdb 
5. C:\DEV_\APP\VISUAL STUDIO 2008\PROJECTS\MISSICO.LIBRARY.1\CLIENTS\CORE.DATA.C 
LIENT\BIN\DEBUG\CORE.DATA.CLIENT.MDF 

H:\Tools\SQL Server Express Utility>sseutil -d C:\DEV* 
Failed to detach 'C:\DEV_\APP\VISUAL STUDIO 2008\PROJECTS\MISSICO.LIBRARY.1\CLIE 
NTS\CORE.DATA.CLIENT\BIN\DEBUG\CORE.DATA.CLIENT.MDF' 

H:\Tools\SQL Server Express Utility>sseutil -l 
1. master 
2. tempdb 
3. model 
4. msdb 

H:\Tools\SQL Server Express Utility> 

Przy użyciu .NET Refector następujące polecenie służy do odłączania bazy danych.

string.Format("USE master\nIF EXISTS (SELECT * FROM sysdatabases WHERE name = N'{0}')\nBEGIN\n\tALTER DATABASE [{1}] SET OFFLINE WITH ROLLBACK IMMEDIATE\n\tEXEC sp_detach_db [{1}]\nEND", dbName, str); 
+0

Dziękuję, muszę wywołać tę akcję z instalatora WiX na wielu komputerach klientów (którzy nie będą mieć zainstalowanego SSEUtil), ale będę eksplorować tę opcję, jak to możliwe . – Bittercoder

+1

SSEUtil to aplikacja .NET. Korzystanie z .NET Reflector, aby zobaczyć, jak oddziela bazę danych. – AMissico

+0

Proponuję wprowadzić wymóg instalatora WiX jako część twojego pełnego pytania. (Twoje ostatnie zdanie.) Łatwo jest tego nie zauważyć. – AMissico

2

I zostały z wykorzystaniem następującej metody pomocnika odłączyć plików MDF dołączone do SQL Server w testach jednostkowych (tak, że uwalniają SQ Server zamki na MDF i LDF plików i testów jednostkowych może posprzątać po sobie) ...

private static void DetachDatabase(DbProviderFactory dbProviderFactory, string connectionString) 
{ 
    using (var connection = dbProviderFactory.CreateConnection()) 
    { 
     if (connection is SqlConnection) 
     { 
      SqlConnection.ClearAllPools(); 

      // convert the connection string (to connect to 'master' db), extract original database name 
      var sb = dbProviderFactory.CreateConnectionStringBuilder(); 
      sb.ConnectionString = connectionString; 
      sb.Remove("AttachDBFilename"); 
      var databaseName = sb["database"].ToString(); 
      sb["database"] = "master"; 
      connectionString = sb.ToString(); 

      // detach the original database now 
      connection.ConnectionString = connectionString; 
      connection.Open(); 
      using (var cmd = connection.CreateCommand()) 
      { 
       cmd.CommandText = "sp_detach_db"; 
       cmd.CommandType = CommandType.StoredProcedure; 

       var p = cmd.CreateParameter(); 
       p.ParameterName = "@dbname"; 
       p.DbType = DbType.String; 
       p.Value = databaseName; 
       cmd.Parameters.Add(p); 

       p = cmd.CreateParameter(); 
       p.ParameterName = "@skipchecks"; 
       p.DbType = DbType.String; 
       p.Value = "true"; 
       cmd.Parameters.Add(p); 

       p = cmd.CreateParameter(); 
       p.ParameterName = "@keepfulltextindexfile"; 
       p.DbType = DbType.String; 
       p.Value = "false"; 
       cmd.Parameters.Add(p); 

       cmd.ExecuteNonQuery(); 
      } 
     } 
    } 
} 

Uwagi:

  • SqlConnection.ClearAllPools() był bardzo pomocny w eliminowaniu "niewidocznych" połączeń (gdy połączenie jest połączone, pozostanie aktywne, nawet jeśli zamkniesz() je, wyraźnie usuwając połączenia z puli, nie musisz martwić się ustawianiem flagi łączenia na false we wszystkich ciągach połączenia).
  • "magiczny składnik" "wywołanie systemu przechowywanej procedury sp_detach_db (Transact-SQL).
  • Moje ciągi połączeń włączone "AttachDBFilename", ale nie obejmują "User Instance = True", więc to rozwiązanie może nie mieć zastosowania do scenariusza
+0

Dzięki za kod - w moim przypadku muszę wywołać to z instalatora WiX (zamiast samej aplikacji, testowania itd.) - również niestety użytkownik db dla instancji użytkownika może nie mieć wystarczających uprawnień, aby wywołać sp_detach_db na wzorcu. Ale daje mi kilka innych opcji do wypróbowania. – Bittercoder

+0

Następująca strona dokumentacji MSDN http://msdn.microsoft.com/en-us/library/bb264564(SQL.90).aspx stwierdza: "Odłączenie bazy danych od instancji przez wywołanie sp_detach_db spowoduje zamknięcie pliku. metoda używana przez program Visual Studio w celu zapewnienia, że ​​plik bazy danych zostanie zamknięty, gdy IDE przełącza się między instancjami użytkownika. " - wydaje się, że samo Visual Studio używa sp_detach_db do zamykania instancji użytkownika :-). Zakładam, że narzędzie SSEUtil zasugerowane przez @AMissico również je wykorzystuje (i jest to przydatna forma exe, którą można zarezerwować od Wix). –

+0

Zaktualizowałem moją odpowiedź, aby uwzględnić faktyczne polecenie używane przez SSEUtil. – AMissico

-3

To nie może być to, czego szukasz, ale darmowe narzędzie Unlocker posiada interfejs wiersza poleceń, które mogą być uruchamiane z WIX. (Przez pewien czas korzystałem z odblokowania i stwierdziłem, że jest stabilny i bardzo dobry w tym, co robi najlepiej, odblokowując pliki.)

Unlocker może odblokować i przenieść/usunąć większość plików.

Wadą tego jest to, że aplikacje, które potrzebują blokady na pliku, nie będą go już miały. (Ale czasami nadal działa dobrze.) Zauważ, że to nie zabija procesu, który ma blokadę. Po prostu usuwa jego blokadę. (Może się okazać, że ponowne uruchomienie usługi SQL, które są zatrzymujące będzie wystarczające na to, aby ponownie zablokować i/lub działać nieprawidłowo.)

Można uzyskać Unlocker stąd: http://www.emptyloop.com/unlocker/

Aby wyświetlić wiersz polecenia Opcje uruchamiania unlocker -H Oto one dla wygody:

 
Unlocker 1.8.8 

Command line usage: 
    Unlocker.exe Object [Option] 
Object: 
    Complete path including drive to a file or folder 
Options: 
    /H or -H or /? or -?: Display command line usage 
    /S or -S: Unlock object without showing the GUI 
    /L or -L: Object is a text file containing the list of files to unlock 
    /LU or -LU: Similar to /L with a unicode list of files to unlock 
    /O or -O: Outputs Unlocker-Log.txt log file in Unlocker directory 
    /D or -D: Delete file 
    /R Object2 or -R Object2: Rename file, if /L or /LU is set object2 points to a text file containing the new name of files 
    /M Object2 or -M Object2: Move file, if /L or /LU is set object2 points a text file containing the new location of files

Zakładając, że celem było zastąpić C: \ My \ Dane aplikacji \ MyApp.mdf z pliku z instalatora, co chcesz coś podobnego unlocker C:\My App\Data\MyApp.mdf -S -D. Spowoduje to usunięcie pliku, dzięki czemu można go skopiować w nowym.

+0

Hmmmm, muszę to pokochać, kiedy uzyskasz głosowanie bez komentarza, dlaczego. – Vaccano

+1

Wyobrażam sobie, że głosowanie w dół było, ponieważ jeśli plik mdf jest zablokowany, jest to z jakiegoś powodu, a użycie programu odblokowującego może uszkodzić bazę danych lub coś gorszego. – AMissico

1

Nie mogę jeszcze komentować, ponieważ nie mam jeszcze wystarczająco wysokiego przedstawiciela. Czy ktoś może przenieść te informacje do drugiej odpowiedzi, abyśmy nie mieli duplikatu?

Właśnie użyłem tego posta, aby rozwiązać mój problem z odinstalowaniem WIX. Użyłem tej linii z odpowiedzi AMissico.

string.Format("USE master\nIF EXISTS (SELECT * FROM sysdatabases WHERE name = N'{0}')\nBEGIN\n\tALTER DATABASE [{1}] SET OFFLINE WITH ROLLBACK IMMEDIATE\n\tEXEC sp_detach_db [{1}]\nEND", dbName, str); 

Pracowałem całkiem dobrze podczas korzystania z WIX, tylko musiałem dodać jedną rzecz, aby to działało dla mnie.

Wyjąłem sp_detach_db, a następnie przywróciłem db online. Jeśli tego nie zrobisz, WIX opuści pliki mdf po odinstalowaniu. Po przywróceniu bazy danych w trybie online WIX poprawnie usunie pliki mdf.

Oto moja zmodyfikowana linia.

string.Format("USE master\nIF EXISTS (SELECT * FROM sysdatabases WHERE name = N'{0}')\nBEGIN\n\tALTER DATABASE [{0}] SET OFFLINE WITH ROLLBACK IMMEDIATE\n\tALTER DATABASE [{0}] SET ONLINE\nEND", dbName); 
Powiązane problemy