Top 10 Fehler–#7: Collection-Klassen 25.07.2014

Alexander Jung
Alexander Jung, Chief eXpert

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

Heute zum Thema… (immer noch ein bisschen LINQ…)

 

Common Mistake #7: Using the wrong type of collection for the task at hand

Yup, das ist eine Sache, die ich auch immer wieder ärgerlich finde. Allerdings aus anderen Gründen, als Patrick:

C# provides a large variety of collection objects, with the following being only a partial list:
Array, ArrayList, BitArray, BitVector32, Dictionary<K,V>, HashTable, HybridDictionary, List<T>, NameValueCollection, OrderedDictionary, Queue, Queue<T>, SortedList, Stack, Stack<T>, StringCollection, StringDictionary.

Dabei hat er sogar Typen unterschlagen, die regelmäßig Probleme machen: CollectionBase und ICollection bzw. Collection<T> und ICollection<T>.

Und er scheint diese Inflation für eine gute Sache zu halten:

While there can be cases where too many choices is as bad as not enough choices, that isn’t the case with collection objects. The number of options available can definitely work to your advantage.  Take a little extra time upfront to research and choose the optimal collection type for your purpose.  It will likely result in better performance and less room for error.

Und kommt zu einer Empfehlung, mit der ich mich nicht anfreunden kann:

If there’s a collection type specifically targeted at the type of element you have (such as string or bit) lean toward using that one first. The implementation is generally more efficient when it’s targeted to a specific type of element.

Offen gestanden ist mir das etwas zu kurzsichtig.

 

Das Thema Collections krankt an zwei Phänomenen:

  • Historischer Wildwuchs
  • Vermischung von Konzepten

Das hat u.a. dazu geführt, dass das .NET Framework – sonst immer ein guter Indikator – und die Empfehlungen von Microsoft selbst zu dieser Frage nur Chaos als Antwort bieten.

Historischer Wildwuchs

.NET 1.0 hatte keine Unterstützung für Generics. Trotzdem brauche man natürlich Collections, und so entstanden Klassen, die mit Objects arbeiten, sowie spezialisierte Ergänzungen für bestimmte Typen und Anforderungen.

Mit .NET 2.0 kamen Generics, einschließlich der entsprechenden Typen für Collections. Und damit auch die Empfehlung, diesen den Vorzug zu geben:

If you’re using the .NET Framework 2.0 or later, using the old non-generic collections is strongly discouraged. (MSDN Magazine)

Natürlich waren zu diesem Zeitpunkt schon wesentliche Teile des .NET Frameworks in Stein gemeißelt. Ergo kommt man auch heute oft nicht an NameValueCollection oder anderen Klassen vorbei.

Vermischung von Konzepten

Von Anfang an gab es zwei konzeptionelle Zusammenhänge in denen Collections eine Rolle spielen:

  1. Als Implementierung von Datenstrukturen
  2. Als Schnittstelle zu den Datenstrukturen

Diese Trennung ist nun nicht gerade neu. Die Collection-Klassen in der Collection Class Library von IBM (Teil der IBM Open Class Library) haben Schnittstelle (Stack, Queue, ec.) und die eigentliche Implementierung (Array, Linked List, etc.) über Templates zusammengebaut – vor über einem viertel Jahrhundert. Die STL erreicht eine Trennung der Konzepte über Container-Klassen und Iteratoren. 

Der Versuch, gleiches im .NET Framework zu verankern ist nur teilweise gelungen. Einerseits gab es von Anfang an ICollection als Schnittstelle und CollectionBase als Standard-Implementierung, die auch oft verwendet wurden (CollectionBase Ableitungen, Roles.Providers, …). Andererseits findet man aber auch genügend Stellen, an denen das zugunsten andere Datenstrukturen, etwa NameValueCollection oder Arrays, nicht passiert. Und auch CollectionBase (die man als “Schnittstellenklasse” bezeichnen könnte) delegiert die eigentliche Datenhaltung zwar weiter, ist aber so eng an ArrayList gekoppelt, dass an einen Austausch kaum zu denken ist.

Trotzdem versucht Microsoft das Konzept der Trennung weiter zu forcieren, zum Beispiel auch mit FxCop-Regeln für gegen Arrays und List<T>. Für wiederverwendbare Klassen, die relativ eigenständig entwickelt werden und in unterschiedlichen Kontexten eingesetzt werden sollen macht das durchaus Sinn.

Andererseits gilt wie so oft: “One size fits all” funktioniert nicht!
Zum einen macht es keinen Sinn, die konzeptionelle Trennung von Schnittstelle und Implementierung gleichmacherisch auf alles zu übertragen. Zum anderen wurden die Karten mit LINQ neu gemischt.

Wenn es nicht um wiederverwendbaren Code geht, sondern um Schnittstellen in meiner Anwendungsarchitektur, dann interessiert nicht die Abstraktion der Datenstruktur, sondern Effizienz und Features.

Je nach Architekturansatz und Gusto kommen dann oft LINQ-Interfaces (IEnumerable<T> oder IQueryable<T>) zum Einsatz, oder die tatsächlichen Container – Dank der Unterstützung von Enumerable.ToArray() und Enumerable.ToList() üblicherweise Arrays oder List<T>. Sobald man sich der Oberfläche nähert mag auch mal eine ObservableCollection<T> im Spiel sein.

ICollection und Konsorten spielen hier definitiv keine Rolle. Dagegen ist auch prinzipiell nichts zu sagen, solange man konsistent bleibt. Auch wenn FxCop sich beschwert. FxCop-Regeln kann man abschalten!

Fazit

Alles zusammengenommen – gewürzt mit veralteten Empfehlungen – ist das eine manchmal etwas unverdauliche Suppe, die wir da auslöffeln müssen.

Meine Empfehlung? Sich bewusst sein, dass es nicht die Antwort gibt, und dass unterschiedliche Kontexte unterschiedliche Lösungen brauchen. Bei wiederverwendbare Komponenten würde ich “konzeptioneller Reinheit” den Vorzug geben, in der Anwendungsentwicklung dem pragmatischen Ansatz.

 

Weitere Informationen und Diskussionen zum Thema:

  • “Collections (C# and Visual Basic)” (msdn)
  • “Selecting a Collection Class” (msdn)
  • “Why we don’t recommend using List<T> in public APIs” (Krzysztof Cwalina)
  • “FAQ: Why does DoNotExposeGenericLists recommend that I expose Collection<T> instead of List<T>?” (Code Analysis Team Blog)
  • stackoverflow: 1, 2, 3
Share |

Service Delivery Manager gesucht 24.07.2014

Simone Franz
Simone Franz, Marketing Manager

SDX AG unterstützt als Technologie-Unternehmen Enterprise-Kunden
(u.a. Fresenius, SEB, Dekabank, Commerzbank, Helaba, Telekom, Airbus,
Lufthansa Systems, …) bei der Realisierung von attraktiven Windows-/Web-/Mobile- und BI-Lösungen.

Im Bereich Mobile Development entwickeln wir Business Apps nativ für Windows 8 / Phone 8 sowie mit Hybrid-Technologien für unterschiedlichste Devices.


Sie positionieren und managen

Ihre Kernkompetenz ist das Positionieren und das Managen unserer festangestellten, zertifizierten eXperts bei unseren Enterprise Kunden. Sie übernehmen Ressourcen- und Budget-Verantwortung und sichern die qualitativ hochwertige Leistungs- und Lieferfähigkeit der SDX Services.
Sie unterstützen Account Manager und eXperts bei der Erstellung von Consulting- und Projekt-Angeboten. Weiterhin verantworten Sie in Zusammenarbeit mit eXperts und Marketing die Erstellung von z.B. Projektsteckbriefen, eXperts Profilen und showcases/PoCs.

Sie beherrschen Komplexität

Als Wirtschaftsinformatiker (oder vergleichbares Studium) beherrschen Sie Komplexität durch technische Kompetenz und starke Soft-Skills. Sie verstehen die Business- und IT-Anforderungen unserer Kunden, kennen deren typische Strukturen bzw. Prozesse und verstehen es, mit den jeweiligen Stakeholdern erfolgreich langfristige Beziehungen aufzubauen und zu halten.

Sie sind professionell und engagiert

Folgende Skills zählen zu Ihren Stärken
· Vernetztes Denken und zielorientiertes Managen
· Serviceorientierung kombiniert mit Verhandlungsgeschick
· Starke Kommunikation in Wort und Schrift
· sehr gute Deutsch- und Englischkenntnisse
· Mind. 3 Jahre Berufserfahrung als Projektleiter/Projektmanager
· Technologie Know-How: Microsoft Application Platform; Mobile-Technologien
· optional: Kontakte in IT-Abteilungen

Herzlich Willkommen bei SDX!

SDX bietet Ihnen ein attraktives, erfolgsabhängiges Gehaltspaket.
Wir freuen uns auf Ihre Bewerbung inkl. Gehaltsvorstellung per E-Mail an Simone.Franz@sdx-ag.de
Bei Fragen können Sie sich auch gerne telefonisch an Frau Franz (069/24 75 18 20) wenden.

Mehr über die SDX und die eXperts finden Sie auf unserem Flurfunk oder auf www.Facebook.com/sdx.ag

Share |

So sehen Sieger aus! 22.07.2014

Svenja Henß
Svenja Henß, Senior Assistant

Gemeinsam mit 36.000 Läufern von 1.500 Münchner und internationalen Unternehmen hat das SDX Team München am 15.7.2014 am B2Run Firmenlauf im Olympiapark in München teilgenommen.

Es war sehr aufregend in diesem Menschenmeer langsam zum Start geschoben zu werden (vom Ausgangspunkt bis zum Start haben wir über eine Stunde benötigt) und sich dann ab der Startlinie in einem Gewusel anderer Läufer seine Laufstrecke zu erkämpfen. Die Strecke war sehr schön und laaaang (6,4 km). Zwinkerndes Smiley 
Trotzdem haben es alle aus unserem Team bis ins Ziel geschafft und wurden durch den Einlauf ins Münchner Olympiastadion durch das Marathontor und einer anschließenden Erfrischung belohnt. Nach der Veranstaltung haben wir noch ein wenig der Siegerehrung gelauscht und die After-Run-Party genossen, bevor wir uns, nach einer kleinen Stärkung, wieder auf dem Heimweg gemacht haben.

Auch wenn wir es nicht mehr unter die Ersten geschafft hat, so hat das Event bei uns allen einen bleibenden Eindruck und Muskelkater hinterlassen.

Wir freuen uns schon auf das nächste Jahr, dann auf jeden Fall mit dem kompletten Münchner eXperts Team! Smiley

 

001WP_20140715_012

Wir bedanken uns herzlich bei Radio Charivari für das schöne Foto.

Share |

Top 10 Fehler–#6: Extension Methods 18.07.2014

Alexander Jung
Alexander Jung, Chief eXpert

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

Heute zum Thema… – immer noch LINQ. Irgendwie.

 

Common Mistake #6: Getting confused or faked out by extension methods

Hm.

While there certainly are advantages to using extension methods, they can cause headaches and wasted time for those developers who aren’t aware of them or don’t properly understand them. […]

Even developers who are familiar with extension methods still get caught occasionally, when there is a method with the same name on the object, but its method signature differs in a subtle way from that of the extension method.

Extension Methods ein “Common Mistake”? Ist das so? Vielleicht für den einen oder anderen. Gelegentlich. Aber “Common”? Nicht nach meiner Erfahrung.

 

Extension Methods werden im Visual Studio über Intellisense kenntlich gemacht und F12 (“go to definition”) tut ein Übriges.

image 

Dazu kommt, dass Extension Methods nicht nur Teil des Fundamentes von LINQ sind, sondern mittlerweile auch ASP.NET MVC reichlich Gebrauch davon macht. Mit häufigem Nutzen kommt aber auch Übung.

Ergo: Natürlich muss man Extension Methods mal verstanden haben. Das ist aber nach meiner Erfahrung kein echtes Problem. (Für das Schreiben eigener Extension Methods mag anderes gelten.)

Mehr zum Thema findet sich hier

Share |

Top 10 Fehler–#5: Failing to consider the underlying objects in a LINQ statement 16.07.2014

Alexander Jung
Alexander Jung, Chief eXpert

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

Heute zum Thema… immer noch LINQ.

 

Common Mistake #5: Failing to consider the underlying objects in a LINQ statement

Eine große Stärke von LINQ ist, dass ich mit der gleichen Syntax und Mächtigkeit Abfragen im Speicher, gegen die Datenbank, XML Dokumente, OData- und andere Services – kurz: alles, wofür es einen LINQ Provider gibt – durchführen kann.

Eine große Falle von LINQ ist, dass ich mit der gleichen Syntax und Mächtigkeit Abfragen schreiben kann, ohne dass die Eigenheiten des Providers auffällig werden.

Eine noch größere Falle von LINQ ist, dass ich mit der gleichen Syntax und Mächtigkeit Abfragen schreiben kann, ohne dass die Fähigkeiten des Providers berücksichtigt werden.

 

Patrick zeigt dazu ein sehr einfaches Beispiel – ein String-Vergleich, der von LINQ-to-Objects natürlich anders durchgeführt wird, als bei der Übersetzung in SQL und Ausführung durch die Datenbank.

Das ist richtig, und doch kratzt es nur an der Oberfläche. Nicht alle Provider unterstützen alle Möglichkeiten, die mit LINQ vorgesehen sind, und LINQ bietet nicht jedes Feature aller Datenquellen an. Beliebtes Beispiel sind Abfragen gegen die Datenbank mit LIKE (hier, hier). Spätestens wenn man sich an Sonderimplementierungen bindet ist an einen Wechsel des Providers ohnehin nicht mehr zu denken.

Verheimlichen lässt sich der Provider spätestens dann nicht mehr, wenn die LINQ-Abfrage an einen Kontext gebunden ist, wie das bei LINQ2SQL und LINQ2EF der Fall ist:

   1: using (var context = new ProductContext()) 
   2: {     
   3:     // Perform data access using the context 
   4:     return context.Products.Where(p => p.ID == 4711);
   5: }

Das geht sowas von daneben…

Manchmal sind es unerwartete Ergebnisse. Manchmal Exceptions vom Provider weil er bestimmte Dinge nicht umsetzen kann. Oder miese Performance, weil der LINQ-Ausdruck so hingebogen wurde, dass er scheinbar tut – nur eben im Speicher, statt in der Datenbank.

Aber wie auch immer sich das Problem darstellt – es ist eine häufig zu beobachtende Fehlerquelle. (Grund schient mir zu sein, dass jedem Provider die Fähigkeiten und Eigenschaften der Programmiersprache unterstellt werden. Nachvollziehbar, aber trotzdem ein Trugschluss.)

Daher die klare Empfehlung:

  • Wie Patrick schon sagt: Man sollte sich bei LINQ immer über die Datenquelle im Klaren sein.
  • Das betrifft nicht nur die Abfragen selbst, sondern auch die Frage nach lazy/eager evaluation.
  • Darüber hinaus sollte man durchaus auch mal prüfen, welche Abfragen unter der Oberfläche entstehen, etwa indem man sich das abgesetzte SQL Statement anschaut.

Besondere Steigerung erfährt dieses Problem übrigens dann, wenn unterschiedliche Datenquellen in einem LINQ-Ausdruck gemischt werden…

Share |

Sonniger Office Day im Juli 10.07.2014

Svenja Henß
Svenja Henß, Senior Assistant

Letzte Woche Freitag, war es mal wieder Zeit für den monatlichen Office Day.

Besonderen Grund zur Freude hatte Simone, die von den eXperts zu Ihrem Geburtstag mit einem Geschenk überrascht wurde. Smiley 

Wir freuen uns einen neuen eXpert im Team begrüßen zu können. Lukasz verstärkt seit 01. Juli das Development Team in Frankfurt.

Neben dem monatlichen Business Update standen folgende Breakout-Sessions und Vorträge auf der Agenda:

- Lean Werkzeuge 1+2: Feedback u. Team Board Meeting
- SharePoint PerformancePoint- / Reporting Services Einrichtung u. Nutzung
- UI Design & Usability & UX

IMG_7579IMG_7585IMG_7596IMG_7613IMG_7617IMG_7625IMG_7626IMG_7631IMG_7634

Share |

Top 10 Fehler–#4: Vermeidung von LINQ 09.07.2014

Alexander Jung
Alexander Jung, Chief eXpert

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

Heute zum Thema LINQ.

 

Common Mistake #4: Using iterative (instead of declarative) statements to manipulate collections

Dieser Punkt ist ein Plädoyer für den Einsatz von LINQ, speziell auch LINQ to Objects.

Ich weiß nicht wirklich warum das so ist, aber mir kommen immer wieder Entwickler unter, die LINQ bislang links liegen lassen. Teilweise, weil sie den Mehrwert nicht sehen; manchmal – bei Produkten, die schon länger in der Wartung sind – kommt auch das Argument, dass mit LINQ eine neue Komplexität in den Code Einzug halten würde, der nicht jeder Entwickler gewachsen wäre. Sic!

Ich kann dabei Patricks Aussage nur zustimmen:

[…] there are cases where a single LINQ statement can easily replace dozens of statements in an iterative loop (or nested loops) in your code.  And less code general means less opportunities for bugs to be introduced.

 

Auf der anderen Seite fehlt in Patricks Liste das andere Extrem, das ich mittlerweile immer öfter sehe, und daher als Unterpunkt ergänzen würde:

Common Mistake #4a: LINQ Spaghetti Code.

Ich habe schon Monster-Ausdrücke in LINQ gesehen, bei denen Abfragen geschachtelt, Daten im Select gemappt wurden und Verarbeitungen in Lambda-Ausdrücken versenkt wurden. Lesbarkeit good-bye!

Und scheinbar bin ich da nicht der Einzige, sonst würde der Einsatz von LINQ in den Kommentaren nicht derart heftig und kontrovers diskutiert werden:

LINQ statements should only be used for simple processing. Complex LINQ statement is write-only code: hard to read and understand, very hard to debug, and thus expensive to maintain. (Soonts)

I love LINQ to interact with SQL in C#, but When it comes to arrays or lists, It's very rare it is more readable or more maintainable than a classic for loop. (Garrett)

Allerdings ist das kein Grund, LINQ in Bausch und Boden zu verdammen, wie es manche Kommentatoren tun:

In our engineering section use of LINQ is rejected in code reviews, its viewed as unnecessary and just complicates code. (brianm101)

My take from this list is wow, serious language design issues! […] LINQ is its own special little area of hell IMNSHO. […] That's just asking for problems. (Techy)

LINQ hat seine Fallen und wie jedes andere Sprachmittel auch, kann man es richtig und falsch einsetzen. Wer es nutzt, wie im von Garrett bereitgestellten Beispiel dargestellt (leider nicht mehr verfügbar, aber hier ist eine schöne Alternative), der macht sicher etwas falsch! Seine Schlussfolgerung

The above code is what LINQ always ends up looking like, at best.

ist allerdings Quatsch. Genauso könnte man argumentieren, dass Methoden mit Klammerbergen aus geschachtelten Schleifen, if-Abfragen etc. die Strukturierte Programmierung generell in Frage stellen.

 

Wer mehr Informationen zu LINQ sucht findet dazu auf MSDN diverse Beispiele. Zum Ausprobieren ist LINQPad sehr beliebt.

Share |

Top 10 Fehler–#3: String-Vergleichsmethoden 04.07.2014

Alexander Jung
Alexander Jung, Chief eXpert

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

Heute zum Thema…

Common Mistake #3: Using improper or unspecified string comparison methods

Dieser Punkt ist ein klares Plädoyer dafür, bei allen String-Vergleichen die Methoden zu verwenden die Sprache und Region einbeziehen. Dem kann ich nur aus vollem Herzen zustimmen!

Wenn an dieser Aussage überhaupt etwas auszusetzen ist, dann dass Patrick sie zu eng fasst, nur beschränkt auf den Vergleich mit string.Equals (mit Nennung von Compare als Nachsatz). Dieser Punkt gilt aber für alle Vergleichsmethoden, also auch IndexOf, StartsWith, etc. und darüber hinaus auch Formatierungsfunktionen, wie string.Format und “beliebiger Datentyp”.ToString! Weitere Hinweise finden sich auf MSDN.

Und: Dieser Punkt wäre definitiv auf meiner eigene Top 10-Liste weit oben.

Auf der einen Seite weil es notwendig ist…

… zumindest, wenn man nicht ausschließlich im US-amerikanischen Markt unterwegs ist (und damit nicht mit Format-Kollisionen rechnen muss).

Schon wenn man nur für eine Sprache – Deutsch liegt nahe – entwickelt, wird man immer wieder über Situationen stolpern, in denen Daten interpretiert werden müssen, die als Strings vorliegen statt streng typisiert. Typische Kandidaten hat man immer dann, wenn es um Schnittstellen nach außen geht:

  • CSV-Dateien
  • Datenbanken (je nach Konfiguration)
  • REST-Services (z.B. wenn das JSON als String aufbereitet wird)
  • Konfigurationsdateien

Wenn man hier einmal die Information verloren hat, in welcher lokalisierten Form ein Datum vorliegt, kommt man in Teufels Küche.

Auf mehrsprachige Anwendungen muss ich wohl nicht gesondert hinweisen.

… auf der anderen Seite, weil es nervig ist!

Es macht keinen Spaß, immer wieder CultureInfo.InvariantCulture oder StringComparison.CurrentCultureIgnoreCase hinschreiben zu müssen. Also tut man es nicht. Und in Probleme läuft man frei nach Murphy erst dann, wenn die Anwendung eine Zeitlang gelaufen ist. Dann, wenn nicht mehr nachvollziehbar ist, bei welchen der Datumswerte in dieser Tabelle Tag und Monat vertauscht wurden….

Was kann man dagegen tun?

  • Disziplin? Notwendig aber nicht hinreichend. Selbst ein “williger” Entwickler (ich bin das beste Bespiel) muss sich immer wieder daran erinnern.
  • Hilfen! Ich habe mir mittlerweile Code-Snippets geschrieben, die mir die obigen Klassen verfügbar machen, selbst wenn die passende using-Direktive fehlt.
  • Zwang! FxCop bzw. statische Codeanalyse (und eine no-warnings-policy!). Microsoft hat dem Thema eine Kategorie spendiert: Globalization Warnings.
Share |

Do more with LESS 01.07.2014

Nico Bolender
Nico Bolender, .Net Enterprise Developer

Eigentlich ist LESS ja ein alter Hut und im Web finden sich mehr Beiträge darüber welchen Präprozessor man verwenden soll, als ob überhaupt einer verwendet werden sollte. Das ist eine gute Sache, weil unabhängig für welchen Präprozessor man sich entscheidet, die Arbeit mit CSS erheblich erleichtert wird.

Man erkennt schon nach wenigen Minuten die Vorteile, die einem z.B. durch den Einsatz von LESS geschenkt werden. Man kann viel strukturierter und auf Komponentenbasis CSS-Dateien erstellen (lassen). Hierfür muss man sich nur an eine einfache und schnell zu erlernende Syntax gewöhnen. Aber auch ohne die Syntax zu beherrschen kann man schon valides LESS schreiben, denn CSS ist ebenfalls valider LESS-Code. CSS wird also nur durch viele hilfreiche Funktionen erweitert.

Ein paar Beispiele zum Anheizen

Variablen: Mit Variablen hat man die Möglichkeit Werte, die an verschiedenen Stellen benötigt werden, an einer Stelle zu definieren. Dadurch kann mit einer Änderung der Wert für alle verwendeten Bereiche verändert werden. Was aber viel cooler ist, ist das man mit Variablen rechnen kann. Man kann sich also Höhen und Breiten oder aber auch Farbwerte ausrechnen.

LESS:

   1: @padding10: 10px;
   2:  
   3: .page{
   4:     padding: @padding10;
   5: }
   6: .menu{
   7:     padding-left: @padding10 + 5px;
   8: }

CSS:

   1: .page{
   2:     padding: 10px;
   3: }
   4: .menu{
   5:     padding-left: 15px;
   6: }

Mixins: Mixins helfen dabei redundante CSS-Anweisungen zu eliminieren indem man den redundanten Part einfach als Mixin schreibt und an den gewünschten Stellen verwendet. Ein gutes Beispiel dafür sind die Browser Prefixe von manchen CSS-Properties. Wie das aussieht zeigt folgendes Codebeispiel:

LESS:

   1: .backface-hidden {
   2:   -moz-backface-visibility: hidden;
   3:   -webkit-backface-visibility: hidden;
   4:   backface-visibility: hidden;
   5: }
   6:  
   7: .menu{
   8:     .backface-hidden;
   9:     position: fixed;
  10:     top: 0px;
  11: }
  12:  
  13: .sidemenu{
  14:     .backface-hidden;
  15:     position: fixed;
  16:     right: 0px;
  17:     bottom: 0px;
  18: }

CSS:

   1: .backface-hidden {
   2:   -moz-backface-visibility: hidden;
   3:   -webkit-backface-visibility: hidden;
   4:   backface-visibility: hidden;
   5: }
   6:  
   7: .menu{
   8:     -moz-backface-visibility: hidden;
   9:     -webkit-backface-visibility: hidden;
  10:     backface-visibility: hidden;    position: fixed;
  11:     top: 0px;
  12: }
  13:  
  14: .sidemenu{
  15:     -moz-backface-visibility: hidden;
  16:     -webkit-backface-visibility: hidden;
  17:     backface-visibility: hidden;    position: fixed;
  18:     right: 0px;
  19:     bottom: 0px;
  20: }

Vererbung”: Mit der “Vererbung” kann man viel natürlicher eine CSS-Klassen Hierarchie abbilden. Dies geschieht indem man die zugehörigen CSS-Klassen einfach innerhalb der gewünschten CSS-Klasse definiert. Was ich damit meine zeigt das nachfolgende Beispiel. Dadurch ist die Zugehörigkeit klar ersichtlich und es ist wesentlich übersichtlicher durch die Einrückungen.

LESS:

   1: .menu {
   2:     list-style: none;
   3:     position: fixed;
   4:  
   5:     & > li {
   6:         display: inline;
   7:         color: #ffffff;
   8:     }
   9:  
  10:     .link{
  11:         color: orange !important;
  12:         font-weight: bolder;
  13:     }
  14: }

CSS:

   1: .menu {
   2:     list-style: none;
   3:     position: fixed;
   4: }
   5: .menu .link {
   6:     color: orange !important;
   7:     font-weight: bolder;
   8: }
   9: .menu > li {
  10:   display: inline;
  11:   color: #ffffff;
  12: }

Module: Des Weiteren kann man andere LESS-Dateien über die @import “path/to/lessfile.less”; importieren und so kleine übersichtliche Module von bestimmten Bereichen erstellen und diese in einer globalen Datei importieren. Damit lässt sich eine CSS-Datei erzeugen, die alle anderen Styles beinhaltet.

Das LESS eine gute Sache ist, erkennt man auch daran, dass man es in den Windows Web Essentials findet. Durch die Installation der Web Essentials, wird automatisch der Compiler für LESS (aber auch für SASS) mitinstalliert. Dadurch muss man sich nicht erst irgendwelche Kommandozeilen Tools oder sogar Ruby installieren, sondern kann in seiner gewohnten Umgebung arbeiten.

Durch die Integration in Visual Studio gibt es nun also keine Ausrede mehr, LESS oder SASS nicht zu verwenden! Zwinkerndes Smiley.

Fazit

Ich hoffe ich konnte diejenigen, die bisher nichts von LESS oder SASS gehalten haben, oder es noch nicht kannten, davon überzeugen auf jeden Fall mal einen tieferen Blick darauf zu werfen. Am Ende erleichtern diese Tools einem nämlich die Arbeit. Das ich in diesem Artikel mehr auf LESS eingegangen bin, hat nix damit zu, dass LESS besser als SASS ist. SASS kann das gleiche was LESS kann und sogar noch mehr und erzeugt in vielen Fällen intelligenteres CSS. Ich wollte nur aufzeigen, dass man selbst schon durch die Verwendung von LESS sein CSS wesentlich besser strukturieren kann.

Wer sich einarbeiten will findet nachfolgend diverse Links zu den Themen LESS und SASS sowie ein paar Vergleiche der beiden.

LESS – Homepage
SASS - Homepage
Vergleich zwischen LESS und SASS

Share |

Parallelisierte Ausführung von SELECT INTO in SQL Server 2014 27.06.2014

##Name des eXperts##
Andrej Kuklin, Principal BI eXpert

SQL Server 2014 brachte viele neue Funktionalitäten und Verbesserungen mit sich. Eine ziemlich coole Verbesserung, die keine Änderungen im bestehenden Code erfordert, ist die mögliche parallelisierte Ausführung von SELECT INTO-Abfragen.

In diesem Artikel werde ich mit realen Daten messen, wie groß der Parallelisierungseffekt sein kann. Da die parallelisierte Ausführung besonders in DWH-Szenarien vorteilhaft ist, werde ich zusätzlich die Kombination von SELECT INTO mit clustered columnstore Indexes - einem anderen neuen Feature in SQL Server 2014 - testen.

Selbstverständlich spielt bei solchen Lasttests die physikalische Infrastruktur eine ganz entscheidende Rolle. Mein System ist wie folgt ausgestattet:

  • Intel Core i7-2760QM (8 logische Kerne x 2,4 GHz)
  • 8 Gb RAM
  • Samsung SSD PM830

Ein geeignet großes Datengerüst habe ich auf der Basis von AdventureWorksDW mit dem Skript von Bob Duffy generiert (FactInternetSalesBig 3,5 Gb, 24 Mio Datensätze). Damit SELECT INTO Operationen nicht von Vergrößerungen der MDF-Datei ausgebremst werden, habe ich den Platz vorreserviert.

ALTER DATABASE [AdventureWorksDW2012] MODIFY FILE ( NAME = N'AdventureWorksDW2012_Data', SIZE = 53248000KB )

Als Erstes werde ich eine Baseline erstellen. Dafür versetze ich die Datenbank ins Kompatibilitätsmodus SQL Server 2012 und messe die Geschwindigkeit vom nicht parallelisierten SELECT INTO.

   1: ALTER DATABASE [AdventureWorksDW2012] SET COMPATIBILITY_LEVEL = 110;
   2: SELECT  * INTO dbo.FactInternetSales_rowstore_serial FROM dbo.FactInternetSalesBig;

rowstore_serial

Wie erwartet, ist der Ausführungsplan seriell, die Ausführungszeit beträgt 40,7 Sekunden. Jetzt ändere ich das Kompatibilitätsmodus in SQL Server 2014 und wiederhole den SELECT INTO-Befehl.

   1: ALTER DATABASE [AdventureWorksDW2012] SET COMPATIBILITY_LEVEL = 120;
   2: SELECT  * INTO dbo.FactInternetSales_rowstore_parallel FROM dbo.FactInternetSalesBig;

rowstore_parallel

Diesmal ist sowohl das Lesen der Quelltabelle als auch das Schreiben in die Zieltabelle parallelisiert. Die Ausführungszeit beträgt 19,9 Sekunden.

Als Nächstes erstelle ich einen clustered columnstore Index auf der Quelltabelle.

CREATE CLUSTERED COLUMNSTORE INDEX [CCSI_FactInternetSalesBig] ON [dbo].[FactInternetSalesBig];

Die Operation ist nicht gerade schnell und benötigt gute 20 Minuten. Dafür ist die Tabelle am Ende nur noch 234 Mb groß (ursprünglich 3,5 Gb).

NB: Ich konnte mit SQL Server 2014 RTM keinen clustered columnstore Index erstellen, da der SQL Server der Abfrage einen zu geringen Memory Grant zugeteilt hat und die Ausführung mit der bekannten Fehlermeldung „Msg 701, Level 17, State 128, Line 1. There is insufficient system memory in resource pool 'default' to run this query” abgebrochen wurde. Erst nach der Installation vom Cumulative Update 1 hat das Erstellen geklappt. Es gibt noch weitere Möglichkeiten, wie man den Abbruch beim Erstellen des Indexes verhindern kann (unter Anderem durch Hochsetzen von REQUEST_MAX_MEMORY_GRANT_PERCENT und Einschränken der Parallelität mit MAXDOP), die im SQL Server Columnstore Index FAQ beschrieben sind.

Der serielle Ausführungsplan, der entweder mit ALTER DATABASE [AdventureWorksDW2012] SET COMPATIBILITY_LEVEL = 110 oder mit dem MAXDOP-Hint erzwungen werden kann, sieht wie folgt aus:

columnstore_serialD

Das Ergebnis ist etwas enttäuschend - 46 Sekunden, sogar geringfügig schlechter, als unsere Baseline.

Die parallelisierte Ausführung auf dem columnstore Index, wie unten abgebildet, ist aber wesentlich besser – 15,9 Sekunden.

columnstore_parallel

Folgende Tabelle zeigt den Vergleich aller vier Methoden.

Rowstore Columnstore
Seriell 100% 113%
Parallel 48,9% 39,1%

Fazit

Die parallelisierten SELECT INTO Abfragen in SQL Server 2014 sind bei vorhandenen IO-Ressourcen spürbar schneller als die seriellen. Beim Einsatz von clustered columnstore Indexes ist die Geschwindigkeit von diesen typischen DWH-Operationen noch etwas höher.

Leider sind die normalen INSERT-Befehle weiterhin seriell. Beim Einfügen in eine existierende Tabelle kann die Parallelität und Skalierbarkeit nur mit dem Einsatz von zusätzlichen Tools (z.B. SSIS oder BCP) erreicht werden.

Share |