CiAgICA8IS0tIExpbmtlZEluIC0tPgogICAgPHNjcmlwdCB0eXBlPSJ0ZXh0L2phdmFzY3JpcHQiPgogICAgICAgIF9saW5rZWRpbl9wYXJ0bmVyX2lkID0gIjEyMzUwNzMiOwogICAgICAgIHdpbmRvdy5fbGlua2VkaW5fZGF0YV9wYXJ0bmVyX2lkcyA9IHdpbmRvdy5fbGlua2VkaW5fZGF0YV9wYXJ0bmVyX2lkcyB8fCBbXTsKICAgICAgICB3aW5kb3cuX2xpbmtlZGluX2RhdGFfcGFydG5lcl9pZHMucHVzaChfbGlua2VkaW5fcGFydG5lcl9pZCk7CiAgICA8L3NjcmlwdD48c2NyaXB0IHR5cGU9InRleHQvamF2YXNjcmlwdCI+CiAgICAgICAgKGZ1bmN0aW9uKCl7dmFyIHMgPSBkb2N1bWVudC5nZXRFbGVtZW50c0J5VGFnTmFtZSgic2NyaXB0IilbMF07CiAgICAgICAgICAgIHZhciBiID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgic2NyaXB0Iik7CiAgICAgICAgICAgIGIudHlwZSA9ICJ0ZXh0L2phdmFzY3JpcHQiO2IuYXN5bmMgPSB0cnVlOwogICAgICAgICAgICBiLnNyYyA9ICJodHRwczovL3NuYXAubGljZG4uY29tL2xpLmxtcy1hbmFseXRpY3MvaW5zaWdodC5taW4uanMiOwogICAgICAgICAgICBzLnBhcmVudE5vZGUuaW5zZXJ0QmVmb3JlKGIsIHMpO30pKCk7CiAgICA8L3NjcmlwdD4KICAgIDxub3NjcmlwdD4KICAgICAgICA8aW1nIGhlaWdodD0iMSIgd2lkdGg9IjEiIHN0eWxlPSJkaXNwbGF5Om5vbmU7IiBhbHQ9IiIgc3JjPSJodHRwczovL3B4LmFkcy5saW5rZWRpbi5jb20vY29sbGVjdC8/cGlkPTEyMzUwNzMmZm10PWdpZiIgLz4KICAgIDwvbm9zY3JpcHQ+CiAgICA8IS0tIEVuZCBMaW5rZWRJbiAtLT4KICAgIA==
Generic filters
Exact matches only
Search in title
Search in excerpt
Search in content

Aus dem Weg! – Auflösen von Repository-Locks

Den meisten Berichtsredakteuren wurden schon einmal die Schreibrechte verwehrt, da ein anderer Anwender schneller war oder vergessen hat, die Schreibrechte wieder freizugeben. Mit Hilfe einer über eine CustomApp gesteuerten Prozedur lässt sich in solchen Fällen der Schreibzugriff freigeben. Der Vorgang wird außerdem protokolliert, damit dieser im Falle von Konflikten nachvollzogen werden kann. Denn bei dem beschriebenen Prozess ist Vorsicht geboten: Hat ein Anwender nicht alle Änderungen gespeichert, so droht der Verlust dieser Änderungen.

Einleitung

Freitag, 18:00. Die Lichter in den Nachbarbüros sind schon aus. Nur ein fleißiger Berichtsredakteur hat kurz vor Feierabend noch die Anweisung bekommen, einen DeltaMaster-Bericht für Montag 8:00 vorzubereiten. Also Ärmel hochgekrempelt und los geht’s. Doch alle mühsam aufgebrachte Motivation wird binnen Sekundenbruchteilen zerstört, als folgende Meldung erscheint:


Der Kollege hat offenbar vergessen, vor Feierabend seinen Rechner ordnungsgemäß herunterzufahren und die DeltaMaster-Anwendung zu beenden. Die einzige Lösung die nun bleibt: Die Sitzung lokal abspeichern.

In einem Projekt gab es genau diese Problemstellung fast wöchentlich. Zwar ist es möglich, mit ent-sprechenden SQL- und Datenmodellkenntnissen einen Schreibzugriff auf das Repository aufzuheben. Die wenigsten Anwender haben allerdings die Werkzeuge oder Berechtigungen, um dies zu tun. Das war Ansporn genug, um das Aufheben des Schreibzugriffes in einer kleinen Anwendung zu verpacken.

Notwendige Repository-Information

Die Information, welche Anwendung von welchem Anwender im Schreibzugriff gehalten wird, kann der Tabelle Lock in der Repository-Datenbank entnommen werden:

 

Von Interesse sind primär die folgenden Spalten:

  • LockType: Gibt an, von welcher Art der Zugriff ist. Für den Anwendungsfall ist LockType 2 von Interesse. Dies sagt aus, dass ein Anwender die Anwendung exklusiv in Bearbeitung hat
  • EntityKey: Enthält einige Metainformationen zur Anwendung. Unter anderen befindet sich in dieser Spalte hinter der letzten Raute die FileID der geöffneten Anwendung
  • ClientID: Enthält Informationen zum aufrufenden Anwender, unter anderem die UserID

Die Aufgabenstellung ist also klar: Wir brauchen eine Prozedur, welche für eine bestimmte Anwendung Sperreinträge mit LockType 2 löscht.

Der Unterbau

Die dafür entwickelte Prozedur überprüft, ob für die ausgewählte Repository-Anwendung überhaupt ein Lock vorliegt. Wenn dem so ist, löscht sie den entsprechenden Eintrag und die Anwendung kann wieder bearbeitet werden:

CREATE PROC P_APP_Solve_Repository_Lock
(@FileID VARCHAR)
AS
/*
Prozedur zum Lösen eines Sperrkonflikts im Repository.

VORSICHT: Der "rausgeworfene" Benutzer verliert eventuell nicht gespeicherte Änderungen.

*/
BEGIN
    DECLARE @clientID VARCHAR(250) = '';
    DECLARE @EntityKey VARCHAR(250) = '';

    --Überprüfen, ob überhaupt ein Lock vorliegt
    IF EXISTS
    (
        SELECT LockID
        FROM DeltaMasterRepository6.dbo.Lock
        --Es wird der String nach der letzten # gesucht. Deshalb wird der String invertiert,
        --bis zur ersten Raute abgeschnitten
        --und dann wieder invertiert
        WHERE REVERSE(SUBSTRING(REVERSE(EntityKey), 0, CHARINDEX('#', REVERSE(EntityKey)))) = @FileID
        --Auf übergebene File überprüfen
              AND LockType = 2 --Nur Locktype = 2 ist von interesse
    )
    --Wenn Lock existiert
    BEGIN
        --Logging-Informationen wegschreiben
        SELECT @EntityKey = EntityKey,
               @clientID = ClientID
        --select *
        FROM DeltaMasterRepository6.dbo.Lock
        WHERE REVERSE(SUBSTRING(REVERSE(EntityKey), 0, CHARINDEX('#', REVERSE(EntityKey)))) = @FileID
              AND LockType = 2; 
        --Dann löschen
        DELETE FROM DeltaMasterRepository6.dbo.Lock
        WHERE REVERSE(SUBSTRING(REVERSE(EntityKey), 0, CHARINDEX>('#', REVERSE(EntityKey)))) = @FileID
              AND LockType = 2;
    END;

END;

In dem Projekt, in dem diese Anwendung entstanden ist, wird in der Prozedur im Delete-Statement zusätzlich auf bestimmte Anwender abgefragt:

WHERE (
                ClientID LIKE  '%UserA%' 
                OR
                ClientID LIKE  '%UserB%'
                )

So wird gewährleistet, dass nur Anwender rausgeworfen werden, die Kenntnis von diesem Vorgang haben.

Logging

Damit der hier beschriebene Prozess nicht als undurchsichtige Blackbox friedlich co-existiert und ein Berichtsredakteur nicht in ständiger „Angst vor dem Rauswurf“ arbeitet, sollte zusätzlich ein Logging implementiert werden. Dieses gibt Auskunft darüber, welcher Anwender in welcher Anwendung be-troffen war und von wem der Prozess initiiert wurde. In der bisher beschriebenen Prozedur wurden bereits die Felder EntityKey und ClientID in Variablen gesichert. Um die Informationen festzuhalten wird eine entsprechende Logging-Tabelle erstellt:

CREATE TABLE T_SYS_Repository_Lock_Log
(
	ExecutedAt DATETIME 
	,ExecutedBy VARCHAR(50)
	,EntityKey VARCHAR(MAX)
	,ClientID VARCHAR(MAX)
)

Die Prozedur P_APP_Solve_Repository_Lock muss dann nur noch um das folgende Statement erweitert werden:

--Update Log
INSERT INTO
    --select * from
    dbo.T_SYS_Repository_Lock_Log
(
    ExecutedAt,
    ExecutedBy,
    EntityKey,
    ClientID
)
VALUES
(GETDATE(),SYSTEM_USER, @EntityKey ,@clientID);

Anschließend kann diese Tabelle allen Anwendern über einen SQL-Durchgriff zugänglich gemacht werden. Hier ist selbstverständlich noch eine ansprechendere Aufbereitung der Felder EntityKey und ClientID denkbar.

Die Oberfläche

Als Bedienoberfläche bietet sich die DeltaMaster-CustomApp an. Hinweise zur Einrichtung dieser Komponente sind ausführlich in einem früheren Blogbeitrag beschrieben. Nach kurzer Zeit ist eine kleine Anwendung eingerichtet, mit deren Hilfe man die zu entsperrende Anwendung auswählen kann:


Damit die Drop-down-Liste die Anwendungsnamen und keine kryptischen FileIDs anzeigt, wurde fol-gende Prozedur für die Auswahl geschrieben:

CREATE PROC P_APP_SELECT_Locked_File
AS
/*
	Prozedur zur Anzeige aller gelockten Anwendungen im Repository

	Gesteuert wird über FIleID, angezeigt wird der Anwendungsname

*/
BEGIN
    SELECT a.AppName AS [NAME],
           af.FileID AS [ID]
    FROM dbo.Lock l
        --FileID aus Entity-Key lesen für Join
        INNER JOIN dbo.[File] d
            --Es wird der String nach der letzten # gesucht. Deshalb wird der String 
     --invertiert, bis zur ersten Raute abgeschnitten
            --und dann wieder invertiert
            ON d.FileID = REVERSE(SUBSTRING(REVERSE(l.EntityKey), 0, CHARINDEX('#', REVERSE(l.EntityKey))))
        INNER JOIN dbo.Application_File af
            ON af.FileID = d.FileID
        INNER JOIN dbo.[Application] a
            ON a.AppID = af.AppID
	WHERE l.LockType = 2
END;

In der Anwendung kann die zu entsperrende Anwendung ausgewählt und mittels Linksklick auf „Starten“ die im Folgenden beschriebene Prozedur ausgeführt werden.

Vorsicht ist geboten!

Bei der Bedienung dieses kleinen Tools müssen einige Punkte zwingend bedacht werden. Nach Entfernen eines Sperreintrages aus der Repository-Datenbank bekommt der betroffene Anwender kein direktes Feedback. Erst wenn er die Anwendung speichern möchte, erscheint der „Im Repository speichern als“-Dialog. Hier könnten dann in der Zwischenzeit getätigte Änderungen wieder überschrieben werden. Dies lässt sich an folgendem Szenario gut Beschreiben:

Anwender A hat mit Schreibrechten Änderungen vorgenommen, nicht gespeichert und geht in die Kaffeepause. Anwender B möchte jetzt auch Änderungen vornehmen, schmeißt Anwender A raus, ändert einige Berichte und verlässt ordnungsgemäß die Anwendung. Anwender A hat nach seiner Kaffeepause nun wieder die Möglichkeit, die Schreibrechte zurückzuerlangen. Tut er dies und speichert dann ab sind die Änderungen von Anwender B überschrieben.

Der rausgeworfene User ist also in jedem Fall zu informieren, so dass es hier nicht zu Missverständnissen kommen kann. Aus diesem Grund wurde auch im entsprechenden Projekt zusätzlich auf Anwender beschränkt, die überhaupt rausgeworfen werden können. Generell sollte aber immer gelten: Arbeit abgeschlossen? Anwendung schließen!