code it

Martins Tech Blog

LINQ to XML mit XML-Namespaces

Der Zugriff auf XML-Strukturen ist mit Linq-to-Xml recht einfach. Interessant wird es dann, wenn Namespaces mit im Spiel sind.

Nehmen wir mal diese XML-Datei an:

<library xmlns="http://schemas.mylibrary.com">
    <books>
        <book title="book 1" isbn="1234567890" />
        <book title="book 2" isbn="1234567891" />
        <book title="book 3" isbn="1234567892" />
    </books>
</library>

Ein Zugriffsversuch ohne Namespaceangabe bringt erwartungsgemäß keine Ergebnisse:

XDocument library = XDocument.Load("C:\\library.xml", LoadOptions.None);

var booktitles = from book in library.Descendants("book")
                 select book.Attribute("title").Value;

Lösung bietet die Angabe des Namespaces:

XDocument library = XDocument.Load("C:\\library.xml", LoadOptions.None);

var booktitles = from book in library.Descendants(XNamespace.Get("http://schemas.mylibrary.com") + "book")
                 select book.Attribute("title").Value;

Historie gelöschter Objekte im TFS ermitteln

Löscht man Objekte, die unter Source Control stehen, so hat man auf den ersten Blick keine Möglichkeit, diese wiederherzustellen oder sich die Historie anzuschauen, um so herauszufinden wer das Objekt mit welchem Changeset gelöscht hat.

Möchte man die gelöschten Objekte sehen, so muss man dies zunächst aktivieren. Die Option dazu findet man unter Tools -> Options -> Source Control -> Visual Studio Team Foundation Server. Dort aktiviert man den Haken "Show deleted items in Source Control Explorer".

 

Nun sieht man im Source Control Explorer auch gelöschte Dateien.

Jetzt noch sauberer!

Die Kombination aus professionellem Softwareentwickler und sauberem Code solle eigentlich eine Selbstverständlichkeit sein. Doch nicht immer trifft man im Alltag diese Kombination auch an. Das kann vielerlei Ursachen haben - sei es weil "saubereres Programmieren" (wie auch immer definiert) während der Ausbildung nicht beigebracht wurde, aus Unachtsamkeit oder weil es im Alltagsstress einfach untergegangen ist. Möchte man langfristig erfolgreich sein, ist es jedoch wichtig, wartbaren und lesbaren Code zu produzieren und sich einen gewissen Qualitätsstandard in der Arbeit anzueignen.

Mit Clean Code Developer haben Ralf Westphal und Stefan Lieser eine Initiative ins Leben gerufen, die sich genau mit diesem Thema auseinandersetzt. Durch ein Wertesystem mit gewissen Regeln, Praktiken und Prinzipien soll Softwareentwicklung professioneller und vertrauenswürdiger werden. In aufeinanderfolgenden farblich gekennzeichneten Graden wird versucht, eben diese Prinzipien zu beherzigen und zu vertiefen.

Und ja, auch ich mach mit bei CCD, denn es ist meiner Meinung nach ein sehr guter Ansatz.

Damit die CCD-Regeln und Prizipien des aktuellen Grades in der täglichen Arbeit nicht untergehen, gibt es von Alexander Zeitler ein Add-In für Visual Studio.

Kontextmenü von Smart Tags im Visual Studio anzeigen

Meiner Meinung nach sind die Smart Tags in Visual Studio ein sehr schönes Feature, die ich besonders häufig für das Umbenennen von Variablen oder für das Erzeugen von Methodenrümpfen beim Implementieren von Interfaces verwende.

In der täglichen Arbeit finde ich es allerdings etwas nervig, den doch etwas klein geratenen Smart Tag mit der Maus zu treffen.

Wie für so vieles gibt es auch hier eine Tastenkombination: Laut Tooltip kann das Smart Tag Menü mit Alt+Shift+F10 geöffnet werden. Was der Tooltip nicht sagt: Mit der viel einfacheren Tastenkombination Strg+. öffnet sich das Menü ebenfalls.

Verwaiste SQL-Logins auf einem SQL-Server ermitteln

Auf einem SQL-Server kann es vorkommen, dass es Logins gibt, zu denen es gar keine User-Zuordnung mehr in Datenbanken gibt. Das kann mehrere Ursachen haben. Erste Ursache kann sein, dass dem Login einfach der Zugriff auf eine Datenbank entzogen wurde. Zweite Ursache kann sein, dass die Datenbanken auf die der Nutzer mal Zugriff hatte, nun vom Server gelöscht wurden - entweder weil sie obsolet waren oder weil sie nun auf einem anderen Server liegen. Diese verwaisten Logins dümpeln nun noch auf dem Server herum (Security -> Logins).

Nun kann es ganz interessant und auch wichtig sein, herauszufinden, wie es um den Server in Bezug auf dieses Thema bestellt ist, um ggf. Aufräumaktionen zu starten. Besonders bei vielen Logins oder bei vielen Datenbanken kann es dann aber schon zeitaufwändig werden, bei jedem Login in die User-Mappings zu schauen und dort zu prüfen, ob eine Datenbank angehakt ist.

Für SQL-Logins kann das folgende Skript schnell eine Übersicht generieren:

CREATE TABLE #OrphanedLogins (sid varbinary(85) NOT NULL)

-- get all sql logins except of sa
INSERT INTO #OrphanedLogins (sid)
SELECT sid FROM sys.server_principals WHERE type = 'S' AND sid <> 0x01

-- iterate trough all databases and determine if one of the logins exists there
-- delete found sids from temp table
exec sp_msforeachdb 'DELETE #OrphanedLogins
FROM sys.database_principals dp INNER JOIN #OrphanedLogins ol 
ON dp.sid = ol.sid'

-- get the result
SELECT * FROM sys.server_principals sp
INNER JOIN #OrphanedLogins ol ON
sp.sid = ol.sid

Die Lösung für NT-Nutzer sieht so ähnlich aus. Aber Achtung: Logins können auch sysadmin-Funktionen haben und dann kann es gerechtfertigt sein, dass diese keine Datenbankzuordnung haben. Aus diesem Grund sollte anhand des Ergebnisses nicht blind gelöscht werden.

Exam 70-562: Microsoft .NET Framework 3.5 - ASP.NET Application Development

Als zweite Zertifizierungsprüfung in diesem Jahr war heute ASP.NET Application Development mit dem .NET Framework 3.5 dran. Mein Ergebnis ist: "Passed". Die Prüfung bestand aus 50 Multiple-Choice-Fragen aus den Bereichen:
  • Configuring and Deploying Web Applications
  • Consuming and Creating Server Controls
  • Working with Data and Services
  • Troubleshooting and Debugging Web Applications
  • Working with ASP.NET AJAX and Client-Side Scripting
  • Targeting Mobile Devices
  • Programming Web Applications

Ansicht für Elemente des aktuellen Nutzer programmatisch erstellen

Wie der erfahrene SharePoint-Anwender weiß, kann man mittels Platzhalter [Ich] in deutschen Systemen bzw. [Me] in allen Systemen auf den aktuellen Nutzer zugreifen, um so in Ansichten Filter für die eigenen Elemente zu erstellen.

Möchte man als Entwickler den gleichen Ansatz auch für die programmatische Erzeugung von Views verwenden wird man mit dem Ansatz

<Where><Eq><FieldRef Name='Author' /><Value Type='User'>[Me]</Value></Eq></Where>

zunächst scheitern, denn auf der Ebene von SPQuery-Objekten stehen diese Platzhalter in der von der Oberfläche bekannten Form nicht zur Verfügung.

Von Dave Hunter findet sich im MSDN-Forum ein guter Tipp, wie man die Entsprechungen in CAML ermitteln kann: Man erstellt eine neue Ansicht für eine Liste in der Benutzeroberfläche, in der man die gewünschten Filter definiert. Im Anschluss daran speichert man die Liste als Template und lädt sich die stp-Datei herunter. Nachdem man diese in eine cab-Datei umbenannt hat, findet man darin eine manifest-Datei, die die Liste und damit auch die Ansichten beschreibt. Mit einer Suche nach dem XML-Tag "Query" findet man das CAML für die Ansicht.

 

Und hier sieht man sehr schön die programmatische Entsprechung des definierten Filters. Um nach dem aktuellen Nutzer als Autor zu filtern, muss diese CAML-Query verwendet werden:

<Where><Eq><FieldRef Name='Author' /><Value Type="Integer"><UserID Type="Integer"/></Value></Eq></Where>

Mit diesem Wissen kann man nun ganz einfach selbst programmatisch SPView-Objekte erstellen, die auf den aktuellen Nutzer filtern können.

Duplikate in SQL-Server-Tabellen löschen

Ich bin grad über einen sehr interessanten Blog-Eintrag gestolpert, der sich mit dem Thema Löschen von Datensatzduplikaten aus SQL-Server-Tabellen beschäftigt. Weil ich den Ansatz wirklich cool finde, besonders weil er überraschend einfach ist, möchte ich ihn euch nicht vorenthalten.

Zunächst wird mal eben schnell eine Demo-Tabelle mit doppelten Datensätzen angelegt.

Und nun kommt der Trick: Mittels PARTITION und ROW_NUMBER() werden die jeweils identischen Datensätze durchnummeriert.

SELECT Column1,Column2,
ROW_NUMBER() OVER(PARTITION BY Column1,Column2 ORDER BY Column1) AS DuplicateCount
FROM DuplicateRecordTable

Mit diesem Wissen können nun alle die Datensätze gelöscht werden, deren laufende Nummer größer als 1 ist:

WITH CTE (Column1,Column2, DuplicateCount)
AS
(
SELECT Column1,Column2,
ROW_NUMBER() OVER(PARTITION BY Column1,Column2 ORDER BY Column1) AS DuplicateCount
FROM DuplicateRecordTable
)
DELETE
FROM CTE
WHERE DuplicateCount > 1

Gib Laut!

Besonders wenn man einen sehr zeitintensiven Build hat, bekommt man manchmal nicht mit, wenn das Build durchgelaufen ist. In diesen Fällen wäre es doch schön, wenn Visual Studio noch einen akustischen Hinweis liefern würde. 

Eine Einstellung in Options hab ich dafür nicht gefunden... aber es gibt ja noch Makros :)

Wenn man mag, legt man sich im Makro-Explorer(Alt+F8) ein neues Makro-Projekt an. Alternativ kann man die Einstellungen auch im MyMacros-Projekt hinzufügen.

Im Anschluss daran öffnet man die Macro-IDE(Alt+F11). Im eben erstellen Makro existiert bereits das Modul EnvironmentEvents. Falls nicht, kann man es sich aus dem Projekt Samples kopieren. Es sollte ungefähr so aussehen:

Option Strict Off
Option Explicit Off
Imports System
Imports EnvDTE
Imports EnvDTE80
Imports EnvDTE90
Imports System.Diagnostics

Public Module EnvironmentEvents

#Region "Automatically generated code, do not modify"
    'Automatically generated code, do not modify
    'Event Sources Begin
    <System.ContextStaticAttribute()> 
    Public WithEvents DTEEvents As EnvDTE.DTEEvents
    <System.ContextStaticAttribute()> 
    Public WithEvents DocumentEvents As EnvDTE.DocumentEvents
    <System.ContextStaticAttribute()> 
    Public WithEvents WindowEvents As EnvDTE.WindowEvents
    <System.ContextStaticAttribute()> 
    Public WithEvents TaskListEvents As EnvDTE.TaskListEvents
    <System.ContextStaticAttribute()>
    Public WithEvents FindEvents As EnvDTE.FindEvents
    <System.ContextStaticAttribute()>
    Public WithEvents OutputWindowEvents As EnvDTE.OutputWindowEvents
    <System.ContextStaticAttribute()>
    Public WithEvents SelectionEvents As EnvDTE.SelectionEvents
    <System.ContextStaticAttribute()>
    Public WithEvents BuildEvents As EnvDTE.BuildEvents
    <System.ContextStaticAttribute()>
    Public WithEvents SolutionEvents As EnvDTE.SolutionEvents
    <System.ContextStaticAttribute()>
    Public WithEvents SolutionItemsEvents As EnvDTE.ProjectItemsEvents
    <System.ContextStaticAttribute()>
    Public WithEvents MiscFilesEvents As EnvDTE.ProjectItemsEvents
    <System.ContextStaticAttribute()>
    Public WithEvents DebuggerEvents As EnvDTE.DebuggerEvents
    <System.ContextStaticAttribute()>
    Public WithEvents ProjectsEvents As EnvDTE.ProjectsEvents
    <System.ContextStaticAttribute()>
    Public WithEvents TextDocumentKeyPressEvents As EnvDTE80.TextDocumentKeyPressEvents
    <System.ContextStaticAttribute()>
    Public WithEvents CodeModelEvents As EnvDTE80.CodeModelEvents
    <System.ContextStaticAttribute()>
    Public WithEvents DebuggerProcessEvents As EnvDTE80.DebuggerProcessEvents
    <System.ContextStaticAttribute()>
    Public WithEvents DebuggerExpressionEvaluationEvents As EnvDTE80.DebuggerExpressionEvaluationEvents
    'Event Sources End
    'End of automatically generated code
#End Region

End Module

Nun wählt man noch im Dropdown "Class name" den Eintrag "BuildEvents" und im Dropdown "Method name" den Entrag "OnBuildDone" und schon ist der Eventhandler für den Build-Done-Event vorhanden. In diesen können nun die gewünschten Aktionen programmiert werden - im Beispiel wird die Methode Beep aufgerufen:

Private Sub BuildEvents_OnBuildDone(ByVal Scope As EnvDTE.vsBuildScope, ByVal Action As EnvDTE.vsBuildAction) Handles BuildEvents.OnBuildDone
    Beep()
End Sub

Das war's auch schon! So lange das Makro geladen ist, wird sich Visual Studio nun am Ende jeden Builds akustisch melden.

Was tun bei Fehler 4046 bei der Anmeldung am SQL-Server?

Eben war man noch am SQL-Server angemeldet und konnte mit seinen Datenbanken arbeiten und im nächsten Moment bekommt man bei der Anmeldung diese Meldung präsentiert:

Die Meldung ist recht eindeutig: Jemand - vielleicht sogar man selbst - hat die Default-Datenbank des Benutzers gelöscht. Wird keine andere Datenbank angegeben, so versucht SQL-Server den Benutzer an dieser Datenbank anzumelden. Diese Einstellungen kann man in den Eigenschaften des jeweiligen Logins vornehmen:

Welche Möglichkeiten bestehen nun, wieder auf den Server zu kommen, um zumindest mit den anderen Datenbanken zu arbeiten oder die fehlende Datenbank wiederherzustellen?

Möglichkeit 1
Die wohl einfachste Möglichkeit ist, den Datenbankadministrator (DBA) zu bitten, die fälschlicherweise gelöschte Datenbank aus einem Backup wiederherzustellen. Wichtig dabei ist, dass die wiederhergestellte Datenbank den gleichen Namen hat wie die zuvor gelöschte Datenbank, denn in den Eigenschaften des Logins wird der Datenbankname als Text gespeichert.

Möglichkeit 2
Sollte es kein Backup geben, so kann man auch eine neue Datenbank mit dem gleichen Namen erstellen und allen relevanten Logins wieder Zugriffsrechte auf dieser Datenbank einräumen. Das ist allerdings nur in Ausnahmefällen sinnvoll - man kann sich dann zwar wieder anmelden, hat aber eine leere Datenbank.

Möglichkeit 3
Hat der Benutzer noch auf andere Datenbanken Zugriffsberechtigungen, so kann auch im Login-Dialog selbst der Name der Datenbank angegeben werden zu der sich Managementstudio verbinden soll. Die Einstellung dazu findet man im Tab Connection Properties (dazu ggf. vorher auf Options klicken):

Ist man dann auch noch mit administrativen Rechten ausgestatten, kann man nach erfolgreichem Login die Einstellung unter Login Properties selbst auf einen korrekten Wert ändern.

Möglichkeit 4
Man kann auch einen netten Kollegen mit administrativen Rechten oder eben den DBA bitten, die Default-Datenbank unter Login Properties auf einen gültigen Wert zu ändern.

Möglichkeit 5
Hat man sich als Administrator selbst ausgesperrt, kann man die Login-Properties auch per sqlcmd setzen. Dazu verbindet man sich zunächst mit sqlcmd zu einer existierenden Datenbank (z.B. master) und ändert dann per ALTER-LOGIN-Statement die Eigenschaften seines Logins:

sqlcmd -d master -U sa -P sapassword

und dann

alter login sa with default_database = master

Wenn Applikationen versuchen, eine Datenbankverbindung aufzubauen, sollte der Hersteller by design schon darauf achten, im ConnectionString eine Datenbank anzugeben und sich nicht blind darauf zu verlassen, dass die Login-Properties korrekt gesetzt sind. Daher sollte diese Meldung auch nur beim Login über solche Tools wie Management Studio o.ä. vorkommen.