Fulltext Manager for SQL Express
If you missed the Storage node in the Object Explorer of your SSMS installation to manage your SQL Express fulltext catalogs, here is the solution!
Find below my new addin for both SSMS 2005 and SSMS 2008.
Once installed, you’ll find a new context menu entry when you select a database:

A click on the menu entry opens the management dialog:

Find the setup and sourcecode on my CodePlex project http://www.codeplex.com/FulltextManager.
Replikation hängt bei “uploading data changes”
Nachdem die Merge-Replikation bei einem unserer Kunden monatelang problemlos lief, ist sie plötzlich ausgefallen. Ein Blick ins Merge-Protokoll zeigte, dass sie nach dem Initialisieren im Status “uploading data changes to the Publisher” stehen geblieben ist. Einige Minuten später folgte dann ein Timeout. Als dieses Problem zum ersten Mal aufgetreten ist, half nur eine komplette Neuinitialisierung des Snapshots mit anschließenden Rebuild. Doch leider ist nach einigen Wochen dasselbe Problem wieder aufgetreten.
Nach längerer Recherche im Internet fand ich einen interessanten Thread über dieses Thema. Anscheinend ist wohl eine SP der Replikation fehlerhaft, was unter bestimmten Umständen zu einer Endlosschleife führt. Dieser Fehler bis zur SQL Server Version 9.0.3282 (Kumulatives Update-Paket 9) aber leider noch nicht behoben.
Ein Workaround besteht momentan im im Setzen des “generation_leveling_threshold” auf den Wert 0. Hierfür muss auf dem Subscriber folgendes Statement ausgeführt werden:
UPDATE sysmergepublications SET [generation_leveling_threshold] = 0
Laut Dokumentation soll der Wert “generation_leveling_threshold” eigentlich über die SP sp_changemergepublication gesetzt werden. Doch das hat in unserem Fall leider keinen Erfolg gebracht. Das korrekte Statement für sp_changemergepublication würde lauten:
EXEC sp_changemergepublication @publication = 'My Publication', @property = 'generation_leveling_threshold', @VALUE = '0'
Dieser Workaround hat in unserem Fall problemlos funktioniert und seid einigen Wochen läuft die Replikation wieder fehlerfrei. Ein großer Vorteil bei diesem Vorgehen ist, dass der Snapshot nicht neu erstellt werden muss und auch der Subscriber nicht erneut initialisiert werden muss. Nachdem man den Befehle ausgeführt hat läuft beim nächsten Abgleich alles wieder problemlos.
Security Error beim Aufruf eines Webservices
Seit dem Release der aktuellen Flash Player Version 9.0.124.0 von Adobe kann es beim Aufruf eines Webservices aus einer Flex-Anwendung heraus zu einer Verletzung der Sicherheitsrestriktionen des Flash Players kommen. Während der Aufruf in der Version 9.0.115.0 noch fehlerlos funktionierte, erhält man nun den folgenden Fehlercode im FaultEvent:
[FaultEvent fault=[RPC Fault faultString="Security error accessing url" ... ]]
Der Grund für den Fehler liegt in der unzureichenden Zugriffsdeklaration innerhalb der crossdomain.xml auf dem Server, der den Webservice anbietet. Das Problem wurde in etlichen Foren bereits diskutiert, der erste Lösungsansatz sah das Hinzuügen der folgenden Richtlinie in der crossdomain.xml vor:
<allow-http-request-headers-from domain="*" />Dieser Zusatz alleine reicht allerdings nicht aus. Um den Zugriff auf einen SOAP-Webservice aus der Flex-Anwendung heraus wieder zu ermöglichen, ist es erforderlich,das headers -Attributs zu setzen. Die korrekte Richtlinie sieht innerhalb der crossdomain.xml dann wie folgt aus:
<?xml version="1.0"?> <!DOCTYPE cross-domain-policy SYSTEM "http://www.adobe.com/xml/dtds/cross-domain-policy.dtd"> <cross-domain-policy> <allow-access-from domain="*" /> <allow-http-request-headers-from domain="*" headers="SOAPAction" /> </cross-domain-policy>
Unbedingt zu beachten ist, dass durch das Setzen des domain -Attributes auf die Wildcard “*” natürlich der Zugriff sämtlicher Domains erlaubt wird. Soll der Zugriff eingeschränkt werden, muss das Attribut auf die Domain des Servers, der die Flex-Anwendung hostet, gesetzt werden.
ExecuteScalar schneidet XML bei 2033 Zeichen ab
Seit dem SQL Server 2000 ist es einfach, XML Dokumente als Ausgabe zurückzuliefern. Hier muss man nur FOR XML [RAW|AUTO|EXPLICIT] dem SELECT-Statement anhängen und schon wird die Ausgabe nicht mehr als Recordset, sondern als XML-Dokument in die Anwendung zurückgegeben.
Als Beispiel soll eine Tabelle mit den 3 Spalten “ID”, “Titel”, “Autor” dienen.
Ein SELECT-Statement, welches den Inhalt der Tabelle als XML-Dokument zurückliefert, wäre
SELECT 1 AS Tag, NULL AS Parent, NULL AS [Buecher!1], NULL AS [Buch!2!ID], NULL AS [Buch!2!Titel], NULL AS [Buch!2!Autor] FROM Buecher UNION SELECT 2 AS Tag, 1 AS Parent, NULL AS [Buecher!1], ID AS [Buch!2!ID!], Titel AS [Buch!2!Titel], Autor AS [Buch!2!Autor] FROM Buecher FOR XML EXPLICIT
Das XML-Dokument hat folgende Struktur
<Buecher> <Buch ID="411B49C4-4F8F-4D31-B563-0AC140A5EBD5" Titel="Die Liga der aussergewöhnlichen Gentleman. Der Roman zum Film. 1. Aufl. 10.2003." Autor="Anderson, Kevin J" /> <Buch ID="F6900F6A-0E65-4F35-8FE3-148253551665" Titel="Die linkshändige Frau" Autor="Handke, Peter" /> <Buch ID="F6BCB885-B334-4905-BA78-25289BF810C1" Titel="Feindbild und Frieden" Autor="Böll, Heinrich" /> <Buch ID="EA915B82-4BF9-4735-8FEA-2ADD567FD53F" Titel="Antigones Verlangen" Autor="Butler, Judith" /> </Buecher>
Möchte man das XML-Dokument nun im eigenen Code z.B. mit Hilfe eines XmlDocument auswerten und liest die Rückgabe über ExecuteScalar ein, so wird die Ausgabe nach 2033 Zeichen abgeschnitten, das XML-Dokument wird damit ungültig und eine Reihe von Xml-Exceptions treten auf.\n\nDer Grund ist, dass der SQL Server XML Dokumente an den Client in Blöcken zu je 2033 Zeichen zurückgibt.\n\nDie Lösung für das o.g. Problem wäre also, nicht ExecuteScalar, sondern etwa folgendes Konstrukt zu verwenden:
connection.Open(); StringBuilder sb = new StringBuilder(); System.Xml.XmlReader reader = command.ExecuteXmlReader(); if (reader.Read()) { while (reader.ReadState != System.Xml.ReadState.EndOfFile) sb.Append(reader.ReadOuterXml()); } reader.Close(); connection.Close(); string xml = sb.ToString();
Solution for FindControl with MasterPages
Imagine you have the following declaration
<asp:Content ContentPlaceHolderID="plcMainContent" ID="cntMain" runat="server"> ... <asp:Panel id="pnlThumb1" runat="server" Visible="true"/> ... </asp:Content>
and you are calling in your CodeBeside
this.pnlThumb1.Visible = false;
it will simply work without any problem.
Now think about changing your call to
this.FindControl("pnlThumb1").Visible = false;
what happens? You will get a NullReferenceException!
What you can do is first get a reference to your Content and second make a FindControl call:
ContentPlaceHolder mainContent = (ContentPlaceHolder)this.Master.FindControl("plcMainContent"); mainContent.FindControl("pnlThumb1").Visible = false;
This works, but this workaround is not very elegant.
We can get more elegance to our code by using reflection and a simple extension class.
The basic idea behind the following code is, that ASP.NET generates protected Control declarations for every control in the page. So we can get references to the controls by reflecting into the class instead of using FindControl.
public static class PageExtensions { public static T GetControl<t>(this Page page, string name) where T : Control { FieldInfo field = page.GetType().GetField(name, System.Reflection.BindingFlags.IgnoreCase | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic); if (field != null) return (T)field.GetValue(page); return null; } }
The usage now is quite simple:
this.GetControl<panel>("pnlThumb1").Visible = false;
Eleganter Zugriff auf MasterPage-Controls
Will man von einer Content Page auf Controls der MasterPage zugreifen, so ist die erste Idee oft die Nutzung von MasterPage.FindControl. Klar, das funktioniert. Nur stellt sich die Frage, ob das nicht schöner geht? Bevor dieser Frage auf den Grund gegangen wird, soll das Problem kurz an einem Beispiel veranschaulicht werden:
MasterPage.master:
<%@ Master Language="C#" AutoEventWireup="true" CodeFile="MasterPage.master.cs" Inherits="MasterPage" %> <html> <body> <form id="form1" runat="server"> <asp:TextBox runat="server" ID="TextBoxMaster" /> <asp:ContentPlaceHolder ID="ContentPlaceHolderMain" runat="server"> </asp:ContentPlaceHolder> </form> </body> </html>
Das Ziel soll nun sein aus einer Content Pager heraus, möglichst elegant auf die TextBox “TextBoxMaster” zuzugreifen. Perfekt wäre also ungefähr so etwas:
protected void Page_Load(object sender, EventArgs e) { Master.TextBoxMaster.Text = "Hello world"; }
Leider funktioniert das so auf Anhieb nicht. Das erste Problem ist, dass hierfür der Compiler wissen müsste, welches der Typ der MasterPage ist, der von der Content Page referenziert wird. Da man aber die MasterPage zur Laufzeit zuweisen kann, kommt man hiermit nicht so recht weiter…
Oder? Da wurde doch in ASP.NET 2.0 eine neue Seiteneigenschaft MasterType eingeführt, die sollte doch genau das Problem lösen und zur Compilezeit den Typen der MasterPage bekannt machen!
<%@ Master Language="C#" MasterPageFile="~/MyMasterPage.master" AutoEventWireup="true" CodeFile="MyMasterPage.master.cs" Inherits="contentPage" %> <%@ MasterType VirtualPath="~/MyMasterPage.master" %> <asp:Content ID="Content1" ContentPlaceHolderID="ContentPlaceHolderMain" runat="Server"> Inhalt der Content Page </asp:Content>
Der Ansatz ist gar nicht so falsch. Jetzt weiß der Compiler uns auch IntelliSense schonmal, welche Eigenschaften und Methoden zur referenzierten MasterPage gehören.
Doch leider führt der Aufruf von Master.TextBoxMaster wieder ins Nirvana.
Der Grund hierfür ist, dass alle Controls einer Seite (der MasterPage eingeschlossen) standardmäßig die Sichtbarkeit protected tragen. Die gute Nachricht ist: das können wir ändern!
Ganz ohne Arbeit geht es leider nicht, doch die hält sich in Grenzen.
Schritt 1:
Entfernen der partial-class-Deklaration. Dies soll ASP.NET beibringen, dass nur noch eine Code-Datei für die MasterPage existiert und keine temporäre mehr für die Control-Deklarationen zur Compilezeit erzeugt werden muss.
Schritt 2:
Deklarieren der Controls mit der public-Sichtbarkeit.
public class MyMasterPage : System.Web.UI.MasterPage { public TextBox TextBoxMaster; }
Schritt 3:
Ändern der @Page Direktive: Tausche Inherits durch CodeBaseFileClass und verschiebe die CodeBehind-Datei in das Verzeichnis App_Code.
<%@ Master Language="C#" MasterPageFile="~/MyMasterPage.master" AutoEventWireup="true" CodeFileBaseClass="MyMasterPage" %>
Schritt 4:
Ausführen des lang ersehnten Codes in der Content Page
protected void Page_Load(object sender, EventArgs e) { Master.TextBoxMaster.Text = "Hello world"; }
Developing Addins for SSMS 2008
There are few good articles if you are interested in developing addins for SQL Server Management Studio 2005.
Basically the concepts are similar to writing an addin for Visual Studio. I linked two sites that deal with addins for SSMS 2005.
http://aspalliance.com/1374_Extend_Functionality_in_SQL_Server_2005_Management_Studio_with_Addins.all
http://jcooney.net/archive/2007/11/26/55358.aspx\
Even when SQL Server Management Studio 2008 CTP had been released, the development concepts for addins remained unchanged to those in SSMS 2005.
Surprisingly the final release of SSMS 2008 brought some significant changes to the addin interface. However, there is no need to panic if you have written an addin for SSMS 2005 and you now want to run it in SSMS 2008. Although the changes Microsoft made will break your addin, the work needed to reanimate it is kept to a minimum:
If you try running your SSMS 2005 addin in SSMS 2008 for the first time it will fail with a typecast exception.
Let’s take a look at the OnConnect-Method in your Connect class, where you typically stored a reference to the DTE2-Application object.
public class Connect : IDTExtensibility2 { private DTE2 _applicationObject; private AddIn _addInInstance; public void OnConnection(object application, ext_ConnectMode connectMode, object addInInst, ref Array custom) { _applicationObject = (DTE2)application; _addInInstance = (AddIn)addInInst; // do something with the application, e.g. reference the Commands Commands2 commands = (Commands2)_applicationObject.Commands } }
As you run this code in SSMS 2008, the typecast exception will occur in Line 8.
So what now? We need the Application reference to create toolbar items or windows. The application-object we get in SSMS 2008 is simply void, there are no properties, no methods, no implemented interface: just empty?!?!
This is a problem, but will not break our necks! One more time Reflector saves our lives: Using Reflector I figured that ServiceCache now has a new static property called ExtensibilityModel.
And this property provides all the functionality we formerly knew from the application object.
Now to convert your addin from SSMS 2005 to SSMS 2008, simply change the application-object calls to ServiceCache.ExtensibilityModel and it will work:
public class Connect : IDTExtensibility2, IDTCommandTarget { private AddIn _addInInstance = null; public void OnConnection(object application, ext_ConnectMode connectMode, object addInInst, ref Array custom) { _addInInstance = (AddIn)addInInst; // do something with the commands Commands2 commands = (Commands2)ServiceCache.ExtensibilityModel.Commands; } }
Although Microsoft changed the addin model to make our lives harder, we will still be able to develop useful addins for SQL Server Management Studio 2008.
Find some nice addins on my codeplex project SSMSAddins: http://www.codeplex.com/SSMSAddins.