Zine.net online

Witaj na Zine.net online Zaloguj się | Rejestracja | Pomoc
w Szukaj

dev2dev

Zrób to sam – SSMS addin (ObjectExplorer, ConnectionString, Command)

ObjectExplorer jest kluczowym obiektem SSMS. To przy jego pomocy łączymy się do instancji SQL Server, przy jego pomocy zmieniamy kontekst bazy danych. Mając ten obiekt w naszej wtyczce jesteśmy w stanie dynamicznie reagować na zmianę domyślnej bazy danych lub serwera powodując dostosowanie widoków naszych okien do zmienionej sytuacji.

Odzyskanie ConnectionString dla bieżącego połączenia daje nam szerokie możliwości działania na obiektach bazy danych.

Musimy również odzyskać obiekt polecenia wykonania query execute aby podpinając się do zdarzenia after execute dać naszej wtyczce ponownie szansę na odświeżenie widoków naszych okien dostosowując je tym samym do sytuacji jaka zaistniała po jego wykonaniu.

1. ObjectExplorer

Do wtyczki dodajemy nowe regiony kodu i umiejscawiamy je tak jak widać na poniższym rysunku:

image

Rozwijamy interesujące nas regiony kodu:

#region Object Explorer fields
 

       IObjectExplorerEventProvider provider;
        Command cmdQueryExecute = default(Command);
        CommandEvents commandEventsQueryExecute = default(CommandEvents);       

 #endregion

#region Static fields

private static DbContext dbContext = new DbContext();  // będzie opisana poniżej

#endregion

#region Object explorer init code

IObjectExplorerService objectExplorer = ServiceCache.GetObjectExplorer();
provider = (IObjectExplorerEventProvider)objectExplorer.GetService(typeof(IObjectExplorerEventProvider));

provider.SelectionChanged += new NodesChangedEventHandler(provider_SelectionChanged);

Commands commands = applicationObject.Commands;

cmdQueryExecute = commands.Item("Query.Execute", 0);

commandEventsQueryExecute = applicationObject.Events.get_CommandEvents(cmdQueryExecute.Guid, cmdQueryExecute.ID);
commandEventsQueryExecute.AfterExecute+=new_dispCommandEvents_AfterExecuteEventHandler

(commandEventsQueryExecute_AfterExecute);

#endregion

Kluczowymi do osiągnięcia naszego celu są handlery obsługi zdarzeń provider_SelectionChanged (zmiana kontekstu ObjectExplorer) oraz commandEventsQueryExecute_AfterExecute (zakończyło się wykonanie query).

W uproszczeniu (nie wnikając w tej chwili co zawierają regiony Refesh…) kod tych handlerów wygląda następująco:

image

Zdarzenie SelectionChanged pochodzące od ObjectExplorer przekazuje w parametrze args nowy kontekst pracy SSMS.

Przy zmianie kontekstu serwera wygląda on następująco:

image

A po wybraniu określonej bazy danych wygląda tak:

image

Ponieważ kontekst zachowuje regularną strukturę warto stworzyć klasę, która będzie go parsować  do bardziej przydatnej formy. W tym celu tworzymy projekt NextAddin.Tools:

image

Referencje są identyczne z tymi, które wymieniłem w odcinku Start.

Klasa ObjectExplorerContext realizuje obsługę parsowania oraz porównywania dotychczasowego kontekstu ze zmienionym kontekstem.

public class DbContext
{
    public string Server = string.Empty;
    public string Database = string.Empty;
    public string ConnectionString = string.Empty;
}

/// <summary>
/// Context Object Explorer
/// </summary>
public static class ObjectExplorerContext
{
    public static DbContext Parse(string context)
    {
        DbContext dbContext = new DbContext();
        Regex re;

        char[] sep = new char[] { '/' };
        char[] equalSep = new char[] { '=' };
        char[] apostrSep = new char[] { '\'' };

        string[] items = context.Split(sep);
        string[] equalItems;
        string[] apostrItems;

        foreach (string item in items)
        {
            if (item.ToLower().StartsWith("server"))
            {
                re = new Regex(".*Server\\[@Name='(.*?)']?");
                if (re.Match(item).Success)
                {
                    equalItems = item.Split(equalSep);
                    apostrItems = equalItems[1].Split(apostrSep);

                    dbContext.Server = apostrItems[1];

                    dbContext.ConnectionString = UtilitySqlTools.Current.ConnectionString();
                }
            }
            else if (item.ToLower().StartsWith("database"))
            {
                re = new Regex(".*Database\\[@Name='(.*?)']?");
                if (re.Match(item).Success)
                {
                    equalItems = item.Split(equalSep);
                    apostrItems = equalItems[1].Split(apostrSep);

                    dbContext.Database = apostrItems[1];
                }
            }
        }

        return dbContext;
    }

    public static bool AreDifferent(DbContext older, DbContext newer)
    {
        if (older.Database != newer.Database || older.Server != newer.Server)
        {
            return true;
        }

        return false;
    }
}

 

2. ConnectionString

Klasa UtilitySqlTools umożliwia nam odzyskanie ConnectionString dla aktualnego połączenia do serwera.

 

public class UtilitySqlTools
{
    static UtilitySqlTools currentInstance = new UtilitySqlTools();

    private UIConnectionInfo currentUIConnection;
    private SqlConnectionInfo currentConnection;

    public static UtilitySqlTools Current
    {
        get { return currentInstance; }
    }

    private SqlConnectionInfo CreateSqlConnectionInfo(UIConnectionInfo connectionInfo)
    {
        SqlConnectionInfo sqlConnInfo = new SqlConnectionInfo();
        sqlConnInfo.ServerName = connectionInfo.ServerName;
        sqlConnInfo.UserName = connectionInfo.UserName;
        if (string.IsNullOrEmpty(connectionInfo.Password))
        {
            sqlConnInfo.UseIntegratedSecurity = true;
        }
        else
        {
            sqlConnInfo.Password = connectionInfo.Password;
        }

        return sqlConnInfo;
    }

    public SqlConnectionInfo GetActiveWindowConnection()
    {
        SqlConnectionInfo info = null;
        try
        {
            UIConnectionInfo connInfo = null;
            if (ServiceCache.ScriptFactory.CurrentlyActiveWndConnectionInfo != null)
            {
                connInfo = ServiceCache.ScriptFactory.CurrentlyActiveWndConnectionInfo.UIConnectionInfo;
            }
            if (connInfo != null)
            {
                if (connInfo == currentUIConnection)
                {
                    return currentConnection;
                }
                else
                {
                    info = CreateSqlConnectionInfo(connInfo);
                    currentConnection = info;
                    currentUIConnection = connInfo;
                }
            }
            if (info == null)
            { 
                INodeInformation[] nodes = GetObjectExplorerSelectedNodes();
                if (nodes.Length > 0)
                {
                    info = nodes[0].Connection as SqlConnectionInfo;
                }
            }
            return info;
        }
        catch (NullReferenceException)
        {
            return null;
        }
    }

    public string ConnectionString()
    {
        if (GetActiveWindowConnection() != null)
            return GetActiveWindowConnection().ConnectionString;
        else
            return null;
    }

    private INodeInformation[] GetObjectExplorerSelectedNodes()
    {
        IObjectExplorerService objExplorer = ServiceCache.GetObjectExplorer();
        int arraySize;
        INodeInformation[] nodes;
        objExplorer.GetSelectedNodes(out arraySize, out nodes);
        return nodes;
    }

    public static void EnableDisableBroker(string current_database, SqlConnection connection, bool enabled)
    {
        StringBuilder sb = new StringBuilder();

        sb.AppendLine("USE [" + current_database + "];");
        sb.Append("ALTER DATABASE [" + current_database + "]");
        if (enabled)
        {
            sb.Append(" SET ENABLE_BROKER");
        }
        else
        {
            sb.Append(" SET DISABLE_BROKER");
        }

        try
        {
            SqlCommand command = new SqlCommand();
            command.CommandText = sb.ToString();
            command.CommandTimeout = 0;
            command.CommandType = CommandType.Text;
            command.Connection = connection;
            command.ExecuteNonQuery();
        }
        catch (Exception exception)
        {
        }
    }

}

3. Command

Po utworzeniu New Query  z SSMS

image

i uruchomieniu go program wchodzi do obsługi zdarzenia AfterExecute

image

Parametry obsługi tego zdarzenia nie niosą nic interesującego z punktu zastosowań do naszej wtyczki. Istotne jest jedynie to, że mamy możliwość zadziałania bezpośrednio po wykonaniu query.

Linki:

do poprzedniego odcinka serii o tworzeniu wtyczki do SSMS

do następnego odcinka serii o tworzeniu wtyczki do SSMS

Opublikowane 13 października 2009 20:10 przez marekpow

Komentarze:

 

dotnetomaniak.pl said:

Dziękujemy za publikację - Trackback z dotnetomaniak.pl

października 13, 2009 21:04
Komentarze anonimowe wyłączone
W oparciu o Community Server (Personal Edition), Telligent Systems