SQL Server Reporting Services – Mehrere Datasets in einer Tabelle nutzen 16.09.2014

Viktor Ewert
Viktor Ewert, Principal BI eXpert

Nutzt man SQL Server Reporting Services für Dashboards, hat man oft die Anforderung mehrere Datasets in einer Steuerelement mit Datenbereich (Liste, Tabelle, Matrix, Diagramm, …) anzuzeigen. In Reporting Services wird aber per Default nur ein Dataset pro Datenbereich unterstützt. Es gibt trotzdem jedoch mehrere Möglichkeiten um Daten aus unterschiedlichen Datasets in einer Tabelle zusammenzufassen

Befinden sich die Datensets in einer Datenquelle, wie z. B. SQL Server, so kann man diese mit SQL Server-Bordmitteln (Unterabfragen, Joins, Stored Procedure) in ein Dataset zusammenführen. Sind die Datenquellen über verschiedene Systeme verteilt, kann man die Daten mit einer Linked-Server-Verknüpfung dennoch miteinander verknüpfen.Doch auch Reporting Services stellen einige Mittel zur Verknüpfung der Daten zur Verfügung.

Subreports

Eine Möglichkeit ist der Einsatz von Subreports bei der Verwendung von Liste, Tabelle oder Matrix. Dafür fügt man in einer Zelle des Steuerelements einen Subreport ein. In diesem Subreport kann man wiederrum alle möglichen Inhalte darstellen z.B. Tabellen, Textboxen usw. Diesen Tabellen kann man dann wiederrum die anderen Datasets als DataSource zuweisen. Der Nachteil dabei ist, dass man sich dann um Parameterübergabe usw. beim Aufruf des Subreports kümmern muss. Um nur bestimmte Informationen aus einem zweiten Dataset dazu zu mischen, viel zu viel Aufwand.

Lookup() 

Möchte man nur einen Wert aus einem anderen Dataset als dem in der Tabelle festgelegten anzeigen, kann man die Lookup Funktion http://msdn.microsoft.com/de-de/library/ee210531.aspx verwenden um Daten aus einem anderen Dataset einzubinden.

In meinem Beispiel werden in folgender Tabelle zwei Datasets benötigt, da das erste Dataset die Stammdaten und Audit-Informationen aus der Datenbank (dsDefault) holt und das zweite Dataset auf einen Analysis Services Cube (dsData) zugreift um die Kennzahlen zu selektieren.

image

Um die beiden Datasets zu verknüpfen wird das erste Dataset ganz normal in der Tabelle als Datasource hinterlegt. Bei den Zellen, die die Kennzahlen beinhalten ist dann folgende Formel hinterlegt:

   1: =Lookup
   2: (
   3:     Fields!CityCode.Value
   4:     ,Fields!City.Value
   5:     ,Fields!Count.Value
   6:     ,"dsData"
   7: )

Hinweis: Bekommt man folgenden Fehler beim Rendern des Reports liegt es daran, dass die beiden Felder nicht den selben Datentypen haben und nicht verglichen werden können.

Warning 44 [rsRuntimeErrorInExpression] The Value expression for the textrun ‘Textbox.Paragraphs[0].TextRuns[0]’ contains an error: Exception of type  'Microsoft.ReportingServices.ReportProcessing.ReportProcessingException_ComparisonError' was thrown.

 

Hier hilft eine Konvertierung der beiden Felder in den selben Datentyp und schon funktioniert es.

   1: =Lookup
   2: (
   3:     CStr(Fields!CityCode.Value)
   4:     ,CStr(Fields!City.Value)
   5:     ,Fields!FactCount.Value
   6:     ,"dsData”
   7: ")

Andere Möglichkeiten sind:

· die Verwendung von Custom Code in einen Bericht (beispiel siehe http://sqlserverbiblog.wordpress.com/2011/09/29/using-custom-code-to-synchronize-different-datasets)

· Data Processing Extension (Beispiel siehe: http://sqlmag.com/database-development/creating-custom-data-processing-extension)

Share |

Top 10 Fehler–#13: Missbrauch von String-Operationen 11.09.2014

Alexander Jung
Alexander Jung, Chief eXpert

Dies ist Teil 14 der kleinen Serie, die ich als Kommentar zum Blog-Beitrag Top 10 Mistakes that C# Programmers Make begonnen habe.

Wir sind bei den Themen angekommen, die mir in der Liste noch fehlen. Heute ein Evergreen Zwinkerndes Smiley

 

AJ’s Common Mistake #13: Misusing string operations

Es ist Grundwissen für jeden .NET-Entwickler: Ein String kann nicht verändert werden → wenn ich mehrere Strings verknüpfe muss eine neuer String gebaut werden mache ich das exzessiv, dann entstehen unnötig temporäre Objekte, die die Speicherverwaltung unter Druck setzen also bitte StringBuilder nutzen…

Und so heißt es dann auch in Improving .NET Application Performance and Scalability:

Excessive string concatenation results in many allocation and deallocation operations, because each time you perform an operation to change the string, a new one is created and the old one is subsequently collected by the garbage collector.

Auf der andere Seite gibt es dann noch String.Format (immer an die CultureInfo denken Zwinkerndes Smiley), das sehr gerne benutzt wird, um komplexer formatierte Strings aufzubereiten.

Theoretisch alles kein Problem.

 

Und die Praxis?

Da findet man Aussagen wie folgende:

“The StringBuilder.Append() method is much better than using the + operator.” (so)

“When concatenating more than three dynamic string values, use StringBuilder.” (so)

“String type in C# being immutable therefore the preferred way of string manipulation is through StringBuilder. […] Of course for simple usage where performance isn't too much of a concern use + or concat.” (so)

“string concatenation is extremely slow, because .NET creates extra copies of your string variables between the concat operations, in this case: two extra copies plus the final copy for the assignment.” (so)

I propose String.Concat uses StringBuilder so I think that performance argument fails here” (so)

Oder solche Perlen (aus einem Review, das ich kürzlich gemacht habe, auf das Notwendige reduziert):

   1: string.Format("{0}{1}", s1, s2))
   2:  

Oder Hilfsmethoden wie diese (aus dem Gedächtnis zitiert):

   1: string StringFormat(string format, string[] args)
   2: {
   3:     StringBuilder sb = new StringBuilder();
   4:     sb.AppendFormat(format, args);
   5:     return sb.ToString();
   6: }

Und wenn man dann nach dem Sinn fragt, kommt oft genug das (falsch verstandene!) Performance-Argument (das zudem nie belegt werden kann – wie lautet das Mantra jeder Optimierung? Messen! Messen! Messen!).

 

Die Fakten…

Solange eine Verkettung von Strings in einem Ausdruck passiert gilt:

  • Konstante Literale (also “Vorne ” + ” Hinten”) werden vom Compiler zusammengefasst, Laufzeitauswirkung gleich null.
  • Wenn Variablen ins Spiel kommen ist die Verwendung von “+” die effizienteste Variante. Der Compiler macht daraus String.Concat-Aufrufe, diese Methode direkt zu verwenden macht also technisch keinen Unterschied, schadet aber i.d.R. der Lesbarkeit. Und String.Concat kommt in diversen optimierten Varianten, die sowohl bzgl. Performance, als auch bzgl. Speicherbedarf das Optimum herausholen.

Die Verwendung von StringBuilder ist absolut sinnvoll, wenn komplexe Strings aus vielen Einzelbestandteilen in mehreren Aufrufen zusammengebaut werden. Anders ausgedrückt: Für eine Hand voll Verknüpfungen ist das Overkill. Wenn der Einsatz eines StringBuilder tatsächlich Sinn macht, sollte man ihm dann aber auch unter die Arme greifen, und ihm von Anfang an eine sinnvolle Kapazität spendieren.

Der folgende Hinweis unter Performance Considerations ist übrigens überholt:

“New data is appended to the buffer if room is available; otherwise, a new, larger buffer is allocated, data from the original buffer is copied to the new buffer, and the new data is appended to the new buffer.“

Das war in frühen .NET-Versionen so, mittlerweile nutzt StringBuilder eine verkettete Liste von Buffern und vermeidet so das Kopieren und den LOH.

String.Format schlussendlich macht absolut Sinn wenn man Texte aufbereiten muss die separat im Code abgelegt oder in Ressourcen ausgelagert sind. Ebenso, wenn die Platzhalter zusätzlich mit Formatanweisungen versehen sind.

Wenn man andererseits einfach nur Textfragmente aneinander hängt ist String.Format gegenüber “+” laufzeitmäßig deutlich im Nachteil. “Deutlich” sollte man aber relativ sehen. Bei einzelnen Aufrufen würde ich der Lesbarkeit immer den Vorzug geben. Erst wenn es um häufige Aufrufe geht, also z.B. in Schleifen, würde ich mir darüber Gedanken machen.

Wo String.Format zum echten Problem werden kann, sind Aufruf zum Logging. Zum einen sind diese Aufrufe dann sehr häufig, zum anderen sehe ich oft Code der den Aufruf von String.Format macht, selbst wenn das Logging gerade abgeschaltet ist. (Das sehen andere genauso.)

 

Zusammenfassend…

In den allermeisten Fällen macht man wenig falsch, wenn man der Lesbarkeit des Codes den Vorzug gibt. Also keine Angst von “+”. Davon abgesehen liegen die typischen Anwendungsgebiete für StringBuilder und String.Format eigentlich auf der Hand.

Wer hingegen mit dem Performance-Argument kommt, der sollte zumindest wissen, wie es um die Performance tatsächlich bestellt ist. Fakten statt Halbwissen ist das eine, Zahlen das andere (z.B. hier oder hier). Die beste Zusammenfassung dazu findet sich (IMHO) übrigens – wie so oft – bei Jeff: It. Just. Doesn't. Matter!

Share |

Top 10 Fehler–#12: Naiver Einsatz von Reflection 04.09.2014

Alexander Jung
Alexander Jung, Chief eXpert

Dies ist Teil 13 der kleinen Serie, die ich als Kommentar zum Blog-Beitrag Top 10 Mistakes that C# Programmers Make begonnen habe.

Und mittlerweile bin ich bei den Themen angekommen, die mir in der Liste noch fehlen. Ich zähle also munter weiter ;-)…

 

AJ’s Common Mistake #12: Naiver Einsatz von Reflection

Ich habe vor Jahren eine frühe Version von Spring.NET als DI-Container eingesetzt (Unity gab es damals noch nicht). Objekte wurden dort per Activator.CreateObject erzeugt – die dokumentierte, offensichtliche und nicht zu Letzt die langsamste Variante der Objekterzeugung. (Disclaimer: Ich weiß nicht, wie das heute in Spring.NET gelöst ist.)

Ich habe bei einem Kunden an einem Framework mitgearbeitet, das für die Beschreibung von HOST-Daten Attribute verwendete und die jedes mal via Type.GetCustomAttributes ausgelesen hat. Auch nicht die schnellste Variante.

Ein Kollege verwendete in seinem O/R-Mapper die üblichen Methoden GetValue, etc. um Datenstrukturen generisch zu lesen und zu schreiben. Suboptimal.

Die Beispiele habe ich ausgewählt, weil sie Reflection in wiederverwendbaren Komponenten an zentraler Stelle einsetzten. Das bringt natürlich entsprechende – im Bezug auf Performance leider negative – Auswirkungen mit sich. Die Liste ließe sich problemlos fortsetzen, insbesondere wenn man noch anwendungsspezifische Einsatzszenarien hinzunimmt.

 

Reflection ist ein sehr mächtiges Werkzeug, das sehr generische – und damit besser wiederverwendbare – Lösungen ermöglicht. Damit verknüpft ist aber ein Preisschild, das leider oft übersehen wird: Performance.

Bei Performance-Analysen, die ich immer mal wieder mache, ist Reflection (wenn es denn zum Einsatz kommt) eigentlich immer ein Kandidat für Optimierungen.

Die wichtigste Empfehlung ist zunächst, dass man sich über die Kosten von Reflection bewusst sein sollte. Im Debugger oder im UnitTests sieht das Ergebnis immer gut aus. Erst ein Performancetest, der den Aufruf 10.000 mal durchführt, wird das Zeitverhalten offensichtlich werden lassen.

Natürlich sollte man an dieser Stelle immer hinterfragen, wie oft der besagte Code in der Anwendung aufgerufen wird. Sporadische Aufrufe haben sicher kaum Einfluss auf das Laufzeitverhalten der Anwendung insgesamt.

Hat man ein Performance-Bottleneck identifiziert, gibt es je nach konkretem Fall verschiedene Lösungsansätze:

  • Andere Sprachmittel können zum Einsatz kommen. Gerade älterer Code kann womöglich durch Generics ohne Reflection gelöst werden. Auch der Einsatz von dynamic kann je nach Implementierung Vorteile bringen und kommt etwa bei ASP.NET MVC zum Einsatz.
  • Code-Generierung ist ein valider Ansatz, der z.B. von ASP.NET und Entity Framework ausgiebig genutzt wird. Mit T4-Templates kein Problem, auch für andere Themen.
  • Patterns können Sonderfälle effizienter abbilden, z.B. Factory-Ansätze wie Prototyp für die Objekterzeugung.
  • Für Meta-Informationen (etwa custom attributes) bietet sich Caching der Informationen an.
  • Dynamische Code-Generierung zur Laufzeit – von .NET etwas intern für regular expressions genutzt – ist dank dynamic methods auch ohne CodeDOM einfach verwendbar.

Weiterführende Informationen:

Beachten muss man dabei allerdings auch, dass Microsoft über die verschiedenen .NET-Versionen hinweg natürlich auch immer an der Performanceschraube gedreht hat. Es gilt also grundsätzlich das Mantra jeder Optimierung: Messen! Messen! Messen!

Share |

Offlinefähigkeit in mobilen Applikationen 01.09.2014

Lukasz Momot
Lukasz Momot, .NET Expert

Die Anzahl von mobilen Applikationen wächst heutzutage immer schneller an. Diese Feststellung gilt sowohl für die sog. Store-Apps (die öffentlich den Kunden zur Verfügung gestellt werden) als auch für Enterprise Applikationen, die in dem Backend des Unternehmens intergiert werden. Viele von diesen Anwendungen funktionieren aber nur dann, wenn eine Verbindung mit dem Internet besteht.

Manchmal, selbst im Fall von kurzen Verbindungsabbrüchen, ist die weitere Arbeit mit der Anwendung unmöglich. Dies kann allerdings in manchen Geschäftsszenarien nicht akzeptabel sein. Um die Benutzererfahrung zu verbessern und dieses Problem zu lösen, bieten viele Anwendungen Offlinefähigkeit an. Wenn man sich diese mobilen Anwendungen genauer anschaut, scheint die Verwendung des Begriffs der Offlinefähigkeit inkonsistent zu sein. Es gibt keine einheitliche Implementierung von Offlinefähigkeit.

Man kann folgende Varianten von Offlinefähigkeit unterschieden:

Varianten der Offlinefähigkeit

  • Variante 0: Keine Offlinefähigkeit

Die Anwendung hört auf zu funktionieren oder stürzt einfach ab, wenn sie die Verbindung verliert.

  • Variante 1: Erkennung von Verbindungsabbrüchen

Bei dieser Variante erkennt die Anwendung lediglich Verbindungsabbrüche. Die weitere Arbeit ist nicht möglich, aber die Applikation wird weiterhin ausgeführt und stürzt nicht ab. Der Benutzer wird aber eventuell eine Fehlermeldung bekommen.

  • Variante 2: Überwindung von kurzen Verbindungsabbrüchen

Die Applikation funktioniert trotz kurzfristiger Verbindungsabbrüche. In dieser Variante gibt es aber keine lokale Persistenz von Daten und Synchronisation mit dem Server. Die Anwendung kann beispielweise die Verbindung automatisch wiederherstellen. Daten hingegen werden zumindest teilweise auch in Voraus geladen werden, damit die App auch offline funktioniert.

  • Variante 3: „Echte“ Offlinefähigkeit

Die Anwendung ist ohne Internetverbindung funktionsfähig. Der Benutzer kann nicht nur Daten einsehen, sondern diese auch hinzufügen und löschen. Diese werden lokal auf dem Endgerät zwischengespeichert. Dazu müssen sie aber auch mit dem Server synchronisiert werden. Der Benutzer kann kontinuierlich arbeiten, ohne darauf zu achten, ob er gerade online oder offline ist. Diese letzte Variante ist die Schwierigste, wenn es um die praktische Umsetzung geht.

Bei der Implementierung der Offlinefähigkeit muss man sich also zunächst die Frage stellen, um welche Art von Offlinefähigkeit es sich handeln soll.

Sollte man sich für Variante 3 entscheiden, steht man vor einigen Herausforderungen:

  • Persistenz der lokalen Daten – unterschiedliche Plattformen bieten diverse Möglichkeiten der Datenpersistenz. Die können sowohl in relationaler, wie beispielsweise bei WebSQL oder SQLLite, als auch in unstrukturierter Form (z. B. im Filesystem) vorliegen.
  • Art und Weise der Integration mit dem bestehenden Backend – das heißt wie die Endanwendung auf dem Server integriert wird.
  • Synchronisierung von Daten – die Daten von sämtlichen Endgeräten müssen mit dem Server synchronisiert werden. Sämtliche Aspekte, wie z.B. Konflikterkennung und Konfliktlösung müssen sorgfältig durchdacht werden.

Die Architektur für diese Variante wird auf dem folgenden Bild schematisch dargestellt:

flurfunkOffline1

 

Fazit

Offlinefähigkeit ist ein sehr interessantes Feature, das die Benutzererfahrung mit mobilen Anwendungen erheblich verbessern kann. Wenn der Anwender ständigen Verbindungsabbrüchen ausgesetzt ist, scheint diese Funktionalität ein unabdingbares Element der modernen Entwicklung von mobilen Apps zu sein.

Für die Art der Umsetzung muss man sich entsprechend Gedanken machen, ob die einfachste Variante reicht oder ob das Businessszenario die echte Offlinefähigkeit notwendig macht. Der Fokus muss hierbei auf der Erkennung der Bedürfnisse des Kunden liegen, um die passende Variante zu wählen.

Share |

Surface Pro 3 – Microsoft hat geliefert! 28.08.2014

Werner Franz
Werner Franz, Vorstand

Seit heute Vormittag halte ich mit dem Surface Pro 3 das professionelle Device in den Händen, das ich schon mit der ersten Surface-Ankündigung erwartet hatte:

      • Professionelles Windows 8-Arbeitsgerät, als Tablet Couch-fähig
      • Leistungsstark (Intel Core 17, 8 GB, 512 GB SSD)
      • Absolut wertiges, edles Design
      • Leicht und kaum höher als ein Schreibblock
      • Cooler Sitft, der auf Knopfdruck OneNote öffnet
      • Abnehmbare Tastatur im SDX corporate blau
      • Dazu eine Surface Arc Touch Mouse

Fazit: Perfekt. Endlich die gelungene Symbiose von Laptop und Tablet mit Coolness-Faktor.

Surface-IIISurface-III-cSurface-III-b

Ach ja: Infos zu Windows 8 App Development gibt’s hier

Share |

Top 10 Fehler–#11: Neglecting FxCop/Static Code Analysis 27.08.2014

Alexander Jung
Alexander Jung, Chief eXpert

Dies ist Teil 12 der kleinen Serie, die ich als Kommentar zum Blog-Beitrag Top 10 Mistakes that C# Programmers Make begonnen habe.

Und mittlerweile bin ich bei den Themen angekommen, die mir in der Liste noch fehlen. 11 von 10 ist also kein Fehler ;-)…

 

AJ’s Common Mistake #11: Neglecting FxCop/Static Code Analysis

Es zieht sich wie ein roter Faden durch viele von Patrick’s “Common Mistakes”, wurde aber von ihm nie explizit adressiert: FxCop bzw. die in Visual Studio eingebaute Variante Code Analysis würde helfen!

Der Compiler kann Warnungen liefern wenn es um typische Fallen auf Sprachebenen geht, leere Anweisungen weil ein Semikolon zu viel gesetzt wurde und ähnliches. Aber sobald es um die korrekte Verwendung von Methoden geht, um Code-Idiome und Patterns, um Prüfungen von Randbedingungen etc., schlägt die Stunde von Werkzeugen zur statischen Code-Analyse, speziell FxCop.

Merker: Es gibt keinen Grund, FxCop nicht einzusetzen! Aber viele Gründe das zu tun!

Man sollte das auch von Anfang an tun; FxCop einzuschalten wenn ein Projekt schon einige Zeit entwickelt wurde führt häufig zu einer Flut von Fehlermeldungen.

Worüber man durchaus zumindest kurz nachdenken sollte ist die Frage, wie FxCop eingesetzt werden soll, sprich mit welchen Regeln. Selbst Microsoft bietet von sich aus schon unterschiedliche Regel-Sets an. Und in der Voreinstellung ist Microsoft hier (leider) sehr zögerlich. Die voreingestellten Managed Recommended Rules kann man sich nach meiner Erfahrung auch sparen. Selbst klassische Fehler, wie Enums ohne 0-Wert werden damit nicht geprüft. Andererseits sind “Microsoft All Rules” dem einen oder anderen dann vielleicht doch etwas zu viel des Guten. Bleibt die Definition eines eigenen Regelsets. Genau das wird aber seit Visual Studio sehr gut unterstützt.

Und wem das noch nicht reicht, der kann das Thema auch noch weiter ausreizen:

Share |

Top 10 Fehler: Wrap-Up–erstmal… 22.08.2014

Alexander Jung
Alexander Jung, Chief eXpert

Dies ist Teil 11 der kleinen Serie, die ich als Kommentar zum Blog-Beitrag Top 10 Mistakes that C# Programmers Make begonnen habe.

Patricks Liste ist ja schon ziemlich umfangreich und in weiten Teilen (wenn auch nicht in allen Aspekten) kann ich mich damit anfreunden.

 

Hier nochmal die Übersicht und meine Wertung:

  1. Using a reference like a value or vice versa Enttäuschtes Smiley
    – Inhaltlich korrekt, aber nach meiner Erfahrung kein “common mistake”.
     
  2. Misunderstanding default values for uninitialized variables Smiley
    – Das dargestellte Problem ist eher akademisch als praxisrelevant, aber es gibt einen nahen Verwandten, den man erwähnen sollte…
     
  3. Using improper or unspecified string comparison methods Smiley mit geöffnetem Mund
    – Full Ack!
     
  4. Using iterative (instead of declarative) statements to manipulate collections Smiley mit geöffnetem Mund
    – Full Ack! Plus Ergänzungen!
     
  5. Failing to consider the underlying objects in a LINQ statement Smiley mit geöffnetem Mund
    – Full Ack! Und noch viel zu harmlos dargestellt.
     
  6. Getting confused or faked out by extension methods Enttäuschtes Smiley
    – Inhaltlich korrekt, aber nach meiner Erfahrung kein “common mistake”.
      
  7. Using the wrong type of collection for the task at hand Verärgertes Smiley
    – Problem erkannt, aber leider bei der Lösung danebengegriffen…
     
  8. Neglecting to free resources Smiley mit geöffnetem Mund
    – Full Ack! Der Klassiker…
     
  9. Shying away from exceptions Smiley mit geöffnetem Mund
    – Full Ack!
     
  10. Allowing compiler warnings to accumulate Smiley mit geöffnetem Mund
    – Full Ack!

Und auch mit seinem Wrap-Up kann ich mich anfreunden:

C# is a powerful and flexible language with many mechanisms and paradigms that can greatly improve productivity.  As with any software tool or language, though, having a limited understanding or appreciation of its capabilities can sometimes be more of an impediment than a benefit, leaving one in the proverbial state of “knowing enough to be dangerous”.

Wer mich kennt, weiß dass das ein gutes Ergebnis ist Zwinkerndes Smiley.

Auf der anderen Seite: Wenn ich mir die Liste insgesamt anschaue, dann fehlen mir da noch ein paar Dinge. Also wird das nicht der letzte Beitrag dieser Reihe sein…

Share |

Top 10 Fehler–#10: Allowing compiler warnings to accumulate 18.08.2014

Alexander Jung
Alexander Jung, Chief eXpert

Dies ist Teil 10 der kleinen Serie, die ich als Kommentar zum Blog-Beitrag Top 10 Mistakes that C# Programmers Make begonnen habe.

Heute zum Thema…

 

Common Mistake #10: Allowing compiler warnings to accumulate

Der (zugegeben etwas pathologische) Quellcode für folgende Ausgabe umfasst kaum ein dutzend Zeilen Code:

   1: 1>D:\Test\ConsoleApplication1\ConsoleApplication1\Program.cs(88,17,88,18): warning CS0642: Possible mistaken empty statement
   2: 1>D:\Test\ConsoleApplication1\ConsoleApplication1\Program.cs(84,17,84,19): warning CS0219: The variable 'i3' is assigned but its value is never used
   3: 1>D:\Test\ConsoleApplication1\ConsoleApplication1\Program.cs(91,13,91,27): warning CS0162: Unreachable code detected
   4: 1>d:\Test\ConsoleApplication1\ConsoleApplication1\Program.cs(84): warning CA1804: Microsoft.Performance : 'Program.Main(string[])' declares a variable, 'i3', of type 'int', which is never used or is only assigned to. Use this variable or remove it.
   5: 1>d:\Test\ConsoleApplication1\ConsoleApplication1\Program.cs(85): warning CA1804: Microsoft.Performance : 'Program.Main(string[])' declares a variable, 'i4', of type 'int', which is never used or is only assigned to. Use this variable or remove it.
   6: 1>d:\Test\ConsoleApplication1\ConsoleApplication1\Program.cs(96): warning CA1811: Microsoft.Performance : 'Program.TestDisposable()' appears to have no upstream public or protected callers.

Man kann sich ausmalen, wie sich das in einem größeren Projekt auswirkt. Und wie “hilfreich” dieser Ausgabe für Entwickler ist, die neu zum Team stoßen und die Anwendung das erste mal übersetzen.

Patrick hat damit zurecht ein Problem:

Remember, the C# compiler gives you a lot of useful information about the robustness of your code… if you’re listening. Don’t ignore warnings.

Warnungen dass eine Variable nicht verwendet wurde, dass Code nicht erreichbar ist und ähnliches, sind keine esoterischen Weisheiten, sondern ernstzunehmende Hinweise.

Patrick geht noch einen Schritt weiter…

They usually only take a few seconds to fix, and fixing new ones when they happen can save you hours. Train yourself to expect the Visual Studio “Error List” window to display “0 Errors, 0 Warnings”, so that any warnings at all make you uncomfortable enough to address them immediately.

… ohne das allerdings ausreichend zu begründen.

Was ist denn mit Warnings, die man im Grunde einfach zur Kenntnis nehmen könnte? “Ja, ich hab’s mir angeschaut, das ist OK so.” Sind die damit nicht erledigt?

Nein, sind sie nicht. Nicht solange sie vom Compiler gemeldet werden. 20 Warnungen dieser Kategorie zwingen mich regelmäßig dazu, wieder darüber nachzudenken. Sie zwingen neue Kollegen dazu, die gleichen Überlegungen anzustellen. Und sie sorgen dafür, dass die 21. Warning, die seit einiger Zeit auftaucht, im Rauschen untergeht und deshalb nicht behoben wird – obwohl sie womöglich unmittelbare Aufmerksamkeit verdient hätte.

Genau deshalb gilt in meinen Projekten eine strikte no-warnings-policy!

Und wenn man den Code nicht ändern will?

  • Für Compiler-Warnungen gibt es entsprechende Pragmas, mit der man die Warnung gezielt abschalten kann – womit man auch gleichzeitig dokumentiert, das man sich damit auseinandergesetzt hat und weiß, was man tut.
  • Für FxCop-Warnungen gibt es im Visual Studio einfache Möglichkeiten das gleiche zu tun.
  • StyleCop habe ich lange abgelehnt, weil mir genau diese Möglichkeit gefehlt hat. Aber auch hier wird die Situation besser.

 

Wovon ich übrigens kein großer Freund bin, ist die Option “Warnings as errors”. Während der Entwicklung – also während ich gerade Code produziere – sind Warnings völlig normal. Es macht keinen Sinn, formale Anforderungen für halbfertigen Code durchsetzen zu wollen, das behindert mich nur in meiner Arbeit.

Share |

SQL CLR: Integrationsmerkmale und Einsatzempfehlungen 13.08.2014

Frank Landgraf
Frank Landgraf, Principal BI eXpert

Es ist schon lange her, genauer gesagt im Jahr 2005, als Microsoft mit SQL CLR (dem Quasi-Nachfolger von Extended Stored Procedures) die Nutzung von .NET innerhalb des SQL Servers eingeführt hat. In diesem Artikel möchte ich hauptsächlich darauf eingehen, was Microsoft konkret hinsichtlich der CLR Integration in SQL Server getan hat und was man bei der Nutzung von Assemblies innerhalb des SQL Servers im Hinterkopf haben sollte.

Im persönlichen Projektalltag habe ich den Einsatz von eigenen Assemblies auf Produktionssystemen (mal abgesehen von Open-Source Projekten wie dem Analysis Services Stored Procedure Project) noch nicht so häufig gesehen. Das liegt sicher zum einen an restriktiven Vorschriften hinsichtlich des Server-Betriebsmanagements, aber möglicherweise auch an einer gewissen Vorsicht. Denn einen sporadischen Speicherfehler in einem Produktionssystem zu finden, kann eine heikle Angelegenheit sein. Schauen wir uns also kurz an, wie es um das Thema Security steht.

Microsoft hat sich doch einige Mühe gemacht, die Ausführung von .NET Code innerhalb einer SQL Server Instanz so sicher und zuverlässig wie möglich zu machen. Für die Codezugriffsberechtigungen SAFE und EXTERNAL_ACCESS gibt es eine Assemblyvalidierung beim Erstellen mit CREATE ASSEMBLY, bei der IL-Code geprüft wird und die garantieren soll, dass der Code typsicher ist und bestimmten Sicherheitsrichtlinien entspricht (Näheres dazu hier). Anderenfalls wird das CREATE ASSEMBLY-Statement mit einem Fehler abgebrochen. Im UNSAFE-Modus dagegen, darf auch Unmanaged Code enthalten sein und der Zugriff auf Systemressourcen ist nur durch die Berechtigungen des Aufrufers selbst beschränkt, aber nicht durch SQL Server.

Innerhalb des .NET-Frameworks wurden Methoden, die aus SQL Server Sicht hinsichtlich der Sicherheit problematisch sind, mit HostProtection-Attributen (HPA) versehen. Nutzt man innerhalb der eigenen Assembly eine solche Methode, wird je nach Codezugriffsberechtigung eine HostProtectionException geworfen. Im Folgenden Beispiel hatte ich ExternalAccess verwendet und die BeginExecuteNonQuery-Methode des SqlCommand aufgerufen.

SQLClr_Threading.

Hier noch der zugehörige Methodenausschnitt aus der System.Data.SqlClient.SqlCommand-Klasse:

image

Wollte man BeginExecuteNonQuery trotzdem einsetzen, kann man das natürlich tun. Allerdings muss dann die Assembly im UNSAFE-Modus bereitgestellt werden. Eine Liste der nicht erlaubten HPA gibt es hier.

Ein weiteres Sicherheitsfeature ist, dass SQL Server keine Assemblies aus dem Global Assembly Cache (z.B. mit Assembly.Load("....")) oder von einer sonstigen Stelle lädt, sondern nur diejenigen, die mit CREATE ASSEMBLY in SQL Server als Objekt in binärer Form deklariert wurden. Das Einbinden als BLOB hat zusätzlich den Vorteil, dass beim BACKUP und RESTORE die Assembly mitberücksichtigt wird, d.h. nach dem Wiederherstellen ist auch der Assemblycode wieder verfügbar.

Beim Erstellen einer Assembly in SQL Server wird diese als AppDomain (je Datenbank und Assembly-Eigentümer eine) isoliert. Hat man also 2 Assemblies in einer Datenbank mit 2 unterschiedlichen Assembly-Eigentümern, so werden diese auch in 2 getrennte AppDomain-Container überführt. Somit lässt sich auch der Extrem-Fehlerfall (Entladen der AppDomain) in gewisser Weise steuern. Generell empfiehlt es sich, Funktionen zusätzlich auch nach Codezugriffsberechtigung zu strukturieren. D.h. hat man eine Menge X von Funktionen, die im SAFE-Modus laufen können und eine Menge Y, die auf Ressourcen außerhalb von SQL Server zugreifen, bietet es sich an, diese auch in 2 Assemblies mit SAFE bzw. EXTERNAL_ACCESS-Berechtigungen auszulagern.

Um die Betriebssicherheit eines Servers zu wahren bzw. zumindest eine weitere Hürde nehmen zu müssen, dürfen nur Datenbankadministratoren mit sysadmin- bzw. serveradmin-Berechtigungen die Ausführung von Assemblies erlauben. Um sie erstellen zu dürfen, braucht man mindestens CREATE ASSEMBLY-Berechtigungen. Mindestens deshalb, weil das nur auf solche im Modus SAFE zutrifft. Für das Sicherheitslevel EXTERNAL_ACCESS sind es schon EXTERNAL_ACCESS-Berechtigungen und für UNSAFE führt kein Weg am Systemadministrator (sysadmin-Rolle) vorbei.

Zusätzlich gibt es noch eine Failure escalation policy, die beispielsweise beim Beenden einer SQL Session mit dem Kill-Befehl dafür sorgt, dass der jeweilige Thread sauber beendet wird. Sofern der Thread jedoch in einer kritischen Region besteht und dort ein Lock auf einem Objekt gehalten wird, entlädt SQL Server einfach die komplette AppDomain, sodass ein sauberer Zustand gewährleistet bleibt. Das Entladen der AppDomain kann insbesondere bei Situationen auftreten, in denen SQL Server der Speicher ausgeht (Memory Pressure).

Performance

SQL Server unterscheidet sich als Laufzeithost doch in einem zentralen Punkt besonders von anderen Konkurrenten wie Oracle oder IBM DB2. Während Oracle und DB2 CLR-Code in einem externen Prozess ausführen, geschieht dies bei SQL Server im Serverprozess selbst. Unter Sicherheitsaspekten könnte man natürlich argumentieren, dass die Isolation bei DB2 oder Oracle ein höheres Maß an Sicherheit (zumindest was den Datenbankhost angeht) gewährt, jedoch resultiert aus der Out-Of-Process Ausführung” auch der Nachteil, dass die Performance nicht so optimal ist, wie sie sein könnte und sich ferner auch einige andere Seiteneffekte (wie z.B. Sichtbarkeit von temporären Tabellen oder andere Transaktionsisolationlevel) ergeben.

Durch die Ausführung im SQL Server Hostprozess wird bei bestimmten Operationen eine sehr hohe Leistung erreicht. Insbesondere zeilenbasierte Operationen werden in SQL CLR wesentlich schneller ausgeführt als in Transact-SQL. Sobald jedoch mengenbasierte Aktionen ins Spiel kommen, ist T-SQL deutlich überlegen. Bei CLR Skalarfunktionen (ohne Datenzugriff via SqlConnection) entsteht ein wesentlich geringerer Call-Overhead als bei den Pendants in T-SQL. Sind Datenzugriffe im Spiel (z.B. mit der ContextConnection) kann man grob sagen, dass T-SQL 2-3x schneller ist, weil dort ggf. Daten im Bufferpool liegen und somit wesentlich schneller darauf zugegriffen werden kann.

Wann sollte man die Nutzung von .NET-Assemblies im SQL Server in Erwägung ziehen?

Die Antwort ist, wie so oft..."It depends!".

Bei rechenintensiven oder zeilenweisen Operationen kann der Performancegewinn mit CLR-Code enorm sein. Die charmante Erweiterung von Funktionalität, die nicht standardmäßig in T-SQL verfügbar ist, aber auch der Zugriff auf externe Ressourcen (z.B. Dateien) können den Einsatz von SQL CLR ebenfalls rechtfertigen.

Bei mengenbasierten Operationen sollte man dann aber doch zu (T-)SQL greifen.

Bei Prozessen, die eigentlich in die Middle-Tier-Schicht gehören, sollte man ebenfalls von einer Integration in die Datenzugriffsschicht Abstand nehmen.

Es gibt sicher Situationen, in denen die Entscheidung T-SQL vs. SQL CLR nicht eindeutig abzugrenzen ist. In solchen Fällen ist es empfehlenswert, die beiden Methoden einfach gegenüberzustellen und unter Berücksichtigung des Assembly-Management-Overheads die Art der Implementierung abzuwägen.

Fazit

SQL CLR dient bei genauer Betrachtung und unter Berücksichtigung der Vor- und Nachteile als sinnvolle Erweiterung der nativen Funktionalität von T-SQL. Wenn es mit Maß eingesetzt wird, können Business Anwendungen in bestimmten Szenarien gut davon profitieren.

PS: Unter http://www.sqlsharp.com findet man bereits eine Bibliothek mit allen möglichen Funktionen, die mit SQL CLR umgesetzt wurden und ggf. (nach einem Review!) eingesetzt werden können.

Quellen

1. http://msdn.microsoft.com/en-us/magazine/cc163716.aspx (Critical Regions)
2. http://msdn.microsoft.com/en-us/library/ms131089(v=sql.110).aspx (inkl. Unterseiten)
3. http://www.sqlskills.com/blogs/bobb/category/sqlclr/ (inkl. Unterseiten)

Share |

Top 10 Fehler–#9: Shying away from exceptions 08.08.2014

Alexander Jung
Alexander Jung, Chief eXpert

Dies ist Teil 9 der kleinen Serie, die ich als Kommentar zum Blog-Beitrag Top 10 Mistakes that C# Programmers Make begonnen habe.

Heute zum Thema… Exceptions!

 

Common Mistake #9: Shying away from exceptions

Ich kenne erfahrene Entwickler, die mit Exceptions lieber nichts zu tun haben wollen, weil sie nicht wissen, wie sie damit umgehen sollen. Damit meine ich nicht, dass die Grundlagen von try/catch/throw nicht bekannt wären; vielmehr machen sich viel zu wenig Entwickler Gedanken darüber, wann sie Exceptions werfen, wo sie sie fangen, warum sie das tun, und was dann damit passieren soll.

Ergo ist eine typische Verhaltensweise, Exceptions möglichst schnell plattzumachen oder Code von vorne herein so zu schreiben, das es erst gar nicht zu einer Exception kommt. Was genau das ist, was Patrick in seinem Mistake #9 ankreidet:

Some programmers are so “exception adverse” that they automatically assume the method that doesn’t throw an exception is superior.

Falls Patrick mit seiner Nummerierung eine Gewichtung der Fehler verbindet, dann wäre dieser Punkt bei mir definitiv auf einem der vorderen Plätze.

 

Tatsächlich gibt es sogar wohlgemeinte Ratschläge (der Weg zur Hölle… . Sorry.) in dieser Richtung: Lance Hunt gibt in seinem weit verbreiteten C# Coding Standards Document (alternativer Link) die folgende Empfehlung:

23. Avoid direct casts. Instead, use the “as” operator and check for null.
Example:
object dataObject = LoadData();
DataSet ds = dataObject as DataSet;
if(ds != null)
{…}

Patricks Empfehlung ist da die weitaus bessere:

However, it is incorrect to assume that TryParse is therefore necessarily the “better” method.  Sometimes that’s the case, sometimes it’s not. That’s why there are two ways of doing it. Use the correct one for the context you are in, remembering that exceptions can certainly be your friend as a developer.

Oder um es noch etwas deutlicher zu formulieren:

  • Man verwendet as, TryParse(), FirstOrDefault(), etc., wenn es im Sinne der Programmlogik absolut legal und erwartungskonform ist, dass der Aufruf auch fehlschlagen kann. Das ist immer dann der Fall wenn andere Typen zulässig sind oder wenn zu parsende Daten von außen kommen, etwa aus der Konfiguration, und ähnliches.
  • Man verwendet Casts, Parse(), First(), etc., wenn die Programmlogik ein Fehlschlagen eigentlich nicht zulässt, wenn das also ein Symptom für einen echten Programmierfehler ist. Solche Konstellationen sollten nicht durch irgendeine Fallback-Logik unter den Teppich gekehrt werden, da sie im schlimmsten Fall Folgefehler nach sich ziehen und zu korrupten Daten führen können.

Bezüglich Casts vs. as kommt noch ein weitere Aspekt hinzu: Der Unterschied zwischen den beiden ist tatsächlich größer, als null vs. Exception. Casts führen ggf. Type Conversions durch, während ein as damit nicht viel anfangen kann. Und bei Value Types ist as nicht mal zulässig.

Simples Beispiel:

   1: var i1= 3.5 as int; // Compilerfehler
   2: var i2= (int)3.5; // Typkonversion

Im Grunde gilt wie so oft: “One size fits all” funktioniert nicht. Man kommt nicht umhin den Einzelfall zu betrachten und Pauschalaussagen schaden mehr als sie nutzen.

Share |