Mai 2016

Band 31, Nummer 5

Datenpunkte – Dapper, Entity Framework und Hybrid-Apps

Von Julie Lerman

Julie LermanSie haben wahrscheinlich bemerkt, dass ich viel über Entity Framework schreibe, den Microsoft Object Relational Mapper (ORM), der seit 2008 als wichtigste .NET-Datenzugriffs-API dient. Es gibt auf dem Markt auch noch andere .NET-ORMs, von denen eine bestimmte Kategorie, Mikro-ORMs, jedoch aufgrund ihrer hohen Leistung viel Beachtung erhält. Der Mikro-ORM, der am meisten erwähnt wird, heißt Dapper. Was letztlich mein Interesse so erregte, dass ich mir Zeit nahm, um mich eingehend damit zu beschäftigen, war die Meldung, dass verschiedene Entwickler Hybridlösungen mithilfe von EF und Dapper erstellt hatten, und dabei jedem ORM ermöglichten, in einer einzelnen Anwendung seine jeweiligen Stärken auszuspielen.

Nachdem ich zahlreiche Artikel und Blogbeiträge gelesen, mich mit Entwicklern unterhalten und ein bisschen mit Dapper herumgespielt hatte, wollte ich meine Erkenntnisse mit Ihnen teilen. Dies gilt insbesondere für diejenigen von Ihnen, die wie ich vielleicht schon von Dapper gehört haben, aber nicht wirklich wissen, was das ist, wie es funktioniert und warum die Leute es so mögen. Anzumerken ist, dass ich auf keinen Fall eine Expertin bin. Ich weiß eher genau so viel, dass ich fürs Erste meine Neugier befriedigen und hoffentlich Ihr Interesse erregen kann, damit Sie noch weiter eintauchen.

Was spricht für Dapper?

Dapper hat eine interessante Geschichte und seinen Ursprung in einer Ressource, mit der Sie vielleicht schon sehr vertraut sind: Marc Gravell und Sam Saffron haben Dapper, als sie an Stack Overflow arbeiteten, mit dem Ziel entwickelt, Leistungsprobleme mit der Plattform zu beheben. Stack Overflow ist eine Website mit sehr hohem Datenverkehrsaufkommen, bei der Leistungsprobleme nicht ausbleiben. Laut der Stack Overflow-Seite „About us“ wurden 2015 auf Stack Overflow 5,7 Mrd. Seiten aufgerufen. 2011 verfasste Saffron einen Blogbeitrag zu seiner und Gravells Arbeit mit dem Titel „How I Learned to Stop Worrying and Write My Own ORM“ (bit.ly), in dem er die Leistungsprobleme von Stack Overflow zu dieser Zeit erläutert, die von der Nutzung von LINQ to SQL herrührten. Er führt weiter aus, warum das Schreiben eines benutzerdefinierten ORM, Dapper, die Lösung für die Optimierung des Datenzugriffs auf Stack Overflow war. Fünf Jahre später ist Dapper nun weit verbreitet und ein Open-Source-Angebot. Gravell und Stack Overflow-Teammitglied Nick Craver kümmern sich unter github.com/StackExchange/dapper-dot-net weiter um die Verwaltung dieses Projekts.

Kurzübersicht über Dapper

Dapper erlaubt Ihnen das Nutzen Ihrer SQL-Fähigkeiten zum Erstellen von Abfragen und Befehlen, so wie Sie es sich vorstellen. Es ist näher an der Hardware als ein Standard-ORM, sodass der Aufwand für das Interpretieren von Abfragen wie LINQ to EF in SQL verringert wird. Dapper bietet einige überzeugende Transformationsfunktionen, wie z. B. die Fähigkeit, eine an eine WHERE IN-Klausel übergebene Liste aufzulösen. Doch zumeist ist der an Dapper gesendete SQL-Code direkt einsatzbereit, und Abfragen erreichen die Datenbank wesentlich schneller. Wenn Sie SQL gut beherrschen, können Sie sicher sein, dass Sie so performante Befehle wie möglich schreiben. Sie müssen eine Art von „IDbConnection“ erstellen, z. B. eine „SqlConnection“ mit einer bekannten Verbindungszeichenfolge, um die Abfragen auszuführen. Anschließend kann Dapper mithilfe seiner API die Abfragen für Sie ausführen und, sofern das Schema der Abfrageergebnisse mit den Eigenschaften des Zieltyps übereinstimmt, Objekte automatisch instanziieren und mit den Abfrageergebnissen auffüllen. In diesem Zusammenhang gibt es einen weiteren spürbaren Leistungsvorteil: Dapper speichert die erlernte Zuordnung effektiv im Cache, was zu einer sehr schnellen Deserialisierung nachfolgender Abfragen führt. Die von mir aufgefüllte Klasse, „DapperDesigner“ (siehe Abbildung 1), ist für das Verwalten von Designern definiert, die sehr modische (engl. dapper) Kleidung entwerfen.

Abbildung 1: „DapperDesigner“-Klasse

public class DapperDesigner
{
  public DapperDesigner() {
    Products = new List<Product>();
    Clients = new List<Client>();
  }
  public int Id { get; set; }
  public string LabelName { get; set; }
  public string Founder { get; set; }
  public Dapperness Dapperness { get; set; }
  public List<Client> Clients { get; set; }
  public List<Product> Products { get; set; }
  public ContactInfo ContactInfo { get; set; }
}

Das Projekt, in dem ich Abfragen ausführe, hat einen Verweis auf Dapper, den ich über NuGet abrufe (install-package dapper). Es folgt ein Beispielaufruf aus Dapper zum Ausführen einer Abfrage aller Zeilen in der Tabelle „DapperDesigners“:

var designers = sqlConn.Query<DapperDesigner>("select * from DapperDesigners");

Beachten Sie, dass ich für Codelisten in diesem Artikel „select *“ verwende, anstatt Spalten für Abfragen explizit zu projizieren, wenn ich alle Spalten einer Tabelle verwenden möchte. „sqlConn“ ist ein vorhandenes „SqlConnection“-Objekt, das ich zusammen mit seiner Verbindungszeichenfolge bereits instanziiert, aber noch nicht geöffnet habe.

Die „Query“-Methode ist eine von Dapper bereitgestellte Erweiterungsmethode. Wenn diese Zeile ausgeführt wird, öffnet Dapper die Verbindung, erstellt einen „DbCommand“-Befehl, führt die Abfrage genau so aus, wie ich sie geschrieben habe, instanziiert ein „DapperDesigner“-Objekt für jede Zeile in den Ergebnissen und überträgt die Werte aus den Abfrageergebnissen in die Eigenschaften der Objekte. Dapper kann mithilfe einiger Muster die Ergebniswerte mit Eigenschaften in Übereinstimmung bringen, auch wenn die Eigenschaftennamen nicht den Spaltennamen entsprechen, und sogar, wenn die Eigenschaften sich nicht in derselben Reihenfolge wie die übereinstimmenden Spalten befinden. Es kann allerdings keine Gedanken lesen. Erwarten Sie deshalb nicht, dass Dapper Zuordnungen bestimmen kann, die beispielsweise verschiedene Zeichenfolgenwerte aufweisen, bei denen die Reihenfolgen oder Namen der Spalten und Eigenschaften nicht synchron sind. Ich habe einige schräge Experimente mit Dapper ausgeführt, um herauszufinden, wie es reagiert. Außerdem gibt es globale Einstellungen, die steuern, wie Dapper Zuordnungen folgern kann.

Dapper und relationale Abfragen

Mein „DapperDesigner“-Typ weist mehrere Beziehungen auf: 1:n (Products), 1:1 (ContactInfo) und n:m (Clients). Ich habe mit der Ausführung von Abfragen dieser Beziehungen experimentiert, was ergab, dass Dapper diese Beziehungen verarbeiten kann. Es ist allerdings nicht so einfach wie das Ausdrücken einer LINQ to EF-Abfrage mit einer „Include“-Methode oder gar einer Projektion. Meinen T-SQL-Kenntnissen wurde jedoch alles abverlangt, da mir EF in den letzten Jahren erlaubt hat, ziemlich faul zu werden.

Hier ein Beispiel der Abfrage der 1:n-Beziehung mithilfe der SQL, die ich direkt in der Datenbank verwenden würde:

var sql = @"select * from DapperDesigners D
           JOIN Products P
           ON P.DapperDesignerId = D.Id";
var designers= conn.Query<DapperDesigner, Product,DapperDesigner>
(sql,(designer, product) => { designer.Products.Add(product);
                              return designer; });

Beachten Sie, dass die „Query“-Methode erfordert, dass ich beide Typen, die erstellt werden müssen, und den zurückzugebenden Typ angebe, der vom letzten „type“-Parameter (DapperDesigner) ausgedrückt wird. Ich verwende ein mehrzeiliges Lambda, um zuerst die Graphen zu erstellen. Dann füge ich die relevanten Produkte ihren übergeordneten „Designer“-Objekt und danach dem „IEnumerable“-Element hinzu, das die „Query“-Methode zurückgibt.

Die Kehrseite dieser Vorgehensweise unter Aufbringung aller meiner SQL-Kenntnisse ist, dass die Ergebnisse vereinfacht werden, wie dies auch bei der EF-Methode „Include“ der Fall wäre. Ich erhalte eine Zeile pro Produkt mit duplizierten Designern. Dapper bietet eine „MultiQuery“-Methode, die mehrere Resultsets zurückgeben kann. Kombiniert mit dem GridReader von Dapper übertrifft die Leistung dieser Abfragen EF-Methoden des Typs „Include“ bei weitem.

Schwierigere Programmierung, schnellere Ausführung

Das Ausdrücken von SQL und Auffüllen zugehöriger Objekte sind Aufgaben, die ich EF im Hintergrund ausführen lasse, weshalb der Programmieraufwand definitiv höher ist. Doch wenn Sie es mit großen Datenmengen zu tun haben und die Laufzeitleistung wichtig ist, kann sich dieser Aufwand sicherlich lohnen. Meine Beispieldatenbank enthält ca. 30.000 Designer, von denen nur einige Produkte aufweisen. Ich habe einige einfache Benchmarktests durchgeführt, wobei ich sichergestellt habe, dass ich Äpfel mit Äpfeln verglichen habe. Bevor die Testergebnisse untersucht werden, möchte ich erläutern, wie ich diese Messungen durchgeführt habe.

Wie Sie wissen, ist EF standardmäßig darauf ausgelegt, Objekte nachzuverfolgen, die das Ergebnis von Abfragen sind. EF erstellt deshalb zusätzliche Nachverfolgungsobjekte (was etwas Aufwand mit sich bringt) und muss auch mit diesen Nachverfolgungsobjekten interagieren. Dapper hingegen lädt Ergebnisse einfach im Arbeitsspeicher ab. Aus diesem Grund darf die EF-Nachverfolgung von Änderungen nicht berücksichtigt werden, sobald Leistungsvergleiche erfolgen. Hierfür definiere ich alle meine EF-Abfragen mit der „AsNoTracking“-Methode. Beim Vergleichstest der Leistung müssen Sie außerdem verschiedene standardmäßige Benchmarkmuster befolgen. Sie müssen beispielsweise die Daten betriebsbereit machen, die Abfrage viele Male wiederholen und die schnellsten und langsamsten Zeiten ausschließen. Im Beispieldownload können Sie die Details dazu finden, wie ich meine Benchmarktests erstellt habe. Dennoch möchte ich weiter von eher „schlanken“ Benchmarktests sprechen, um Ihnen eine Vorstellung der Unterschiede zu geben. Für ernsthafte Benchmarktests sind wesentlich mehr Iterationen als meine 25 erforderlich (fangen Sie bei 500 an). Außerdem müssen Sie die Leistung des Systems berücksichtigen, auf dem Sie arbeiten. Ich führe diese Tests auf einem Laptop unter Verwendung einer SQL Server LocalDB-Instance aus, weshalb meine Ergebnisse nur für Vergleichszwecke nützlich sind.

Diese Zeiten, die ich in meinen Tests messe, beziehen sich auf das Ausführen der Abfrage und Erstellen der Ergebnisse. Die Instanziierung von Verbindungen oder DbContexts wird nicht gezählt. Der DbContext wird wiederverwendet, sodass die von EF benötigte Zeit zum Erstellen des Modells im Arbeitsspeicher nicht berücksichtigt wird, da dies nur einmal pro Anwendungsinstanz und nicht für jede Abfrage erfolgt.

Abbildung 2 zeigt die „select *“-Tests für Dapper und die EF LINQ-Abfrage, damit Sie das Grundkonstrukt meines Testmusters erkennen können. Beachten Sie, dass ich neben der Erfassung der tatsächlichen Dauer die Dauer jeder Iteration zur weiteren Analyse in einer Liste (namens „times“) erfasse.

Abbildung 2: Tests zum Vergleichen von EF und Dapper beim Abfragen aller DapperDesigners

[TestMethod,TestCategory("EF"),TestCategory("EF,NoTrack")]
public void GetAllDesignersAsNoTracking() {
  List<long> times = new List<long>();
  for (int i = 0; i < 25; i++) {
    using (var context = new DapperDesignerContext()) {
      _sw.Reset();
      _sw.Start();
      var designers = context.Designers.AsNoTracking().ToList();
      _sw.Stop();
      times.Add(_sw.ElapsedMilliseconds);
      _trackedObjects = context.ChangeTracker.Entries().Count();
    }
  }
  var analyzer = new TimeAnalyzer(times);
  Assert.IsTrue(true);
}
[TestMethod,TestCategory("Dapper")
public void GetAllDesigners() {
  List<long> times = new List<long>();
  for (int i = 0; i < 25; i++) {
    using (var conn = Utils.CreateOpenConnection()) {
      _sw.Reset();
      _sw.Start();
      var designers = conn.Query<DapperDesigner>("select * from DapperDesigners");
      _sw.Stop();
      times.Add(_sw.ElapsedMilliseconds);
      _retrievedObjects = designers.Count();
    }
  }
  var analyzer = new TimeAnalyzer(times);
  Assert.IsTrue(true);
}

Zum Vergleich von Äpfeln mit Äpfeln ist noch eine Anmerkung zu machen. Dapper verwendet rohe SQL. EF-Abfragen werden standardmäßig mithilfe von LINQ to EF ausgedrückt und unterliegen demselben Aufwand zum Erstellen der SQL für Sie. Nachdem diese SQL erstellt wurde (die auch auf Parametern basieren kann), wird diese im Arbeitsspeicher der Anwendung zwischengespeichert, sodass dieser Aufwand bei einer Wiederholung verringert wird. Darüber hinaus hat EF die Fähigkeit zum Ausführen von Abfragen mithilfe roher SQL, weshalb ich beide Ansätze berücksichtigt habe. Abbildung 3 zeigt die Vergleichsergebnisse von vier Testgruppen. Der Download enthält noch mehr Tests.

Abbildung 3: Durchschnittliche Zeit in Millisekunden zum Ausführen einer Abfrage und Auffüllen eines Objekts basierend auf 25 Iterationen unter Ausschluss des schnellsten und langsamsten Ergebnisses

*„AsNoTracking“-Abfragen Beziehung LINQ to EF* EF – rohe SQL* Dapper – rohe SQL
Alle Designer (30.000 Zeilen) 96 98 77
Alle Designer mit Produkten (30.000 Zeilen) 1 : * 251 107 91
Alle Designer mit Kunden (30.000 Zeilen) * : * 255 106 63
Alle Designer mit Kontakten (30.000 Zeilen) 1 : 1 322 122 116

 

In den in Abbildung 3 gezeigten Szenarien ist es einfach, für die Nutzung von Dapper im Vergleich zu LINQ to-Entitäten zu plädieren. Doch die geringfügigen Unterschiede zwischen rohen SQL-Abfragen rechtfertigen u. U. nicht immer den Wechsel zu Dapper für bestimmte Aufgaben in einem System, in dem Sie ansonsten EF nutzen. Naturgemäß sind Ihre eigenen Anforderungen unterschiedlich und können den Grad der Abweichung zwischen EF-Abfragen und Dapper beeinflussen. Doch bei einem System wie Stack Overflow mit einem solch hohen Datenverkehr kann selbst eine Handvoll Millisekunden, die pro Abfrage eingespart werden, entscheidend sein.

Dapper und EF zum Erfüllen anderer Persistenzanforderungen

Bislang habe ich einfache Abfragen gemessen, bei denen ich lediglich alle Spalten einer Tabelle abgerufen habe, die exakt mit den Eigenschaften der zurückgegebenen Typen übereinstimmten. Was passiert, wenn Sie Abfragen in Typen projizieren? Solange das Schema der Ergebnisse dem Typ entspricht, erkennt Dapper beim Erstellen der Objekte keine Unterschiede. EF muss sich hingegen mehr anstrengen, wenn die Ergebnisse der Projektion nicht mit einem Typ übereinstimmen, der Teil des Modells ist.

„DapperDesignerContext“ hat ein „DbSet“ für den „DapperDesigner“-Typ. In meinem System habe ich einen anderen Typ namens „MiniDesigner“, der über eine Teilmenge der „DapperDesigner“-Eigenschaften verfügt:

public class MiniDesigner {
    public int Id { get; set; }
    public string Name { get; set; }
    public string FoundedBy { get; set; }
  }

„MiniDesigner“ ist nicht Teil meines EF-Datenmodells, weshalb „DapperDesigner­Context“ diesen Typ nicht kennt. Ich habe festgestellt, dass das Abfragen aller 30.000 Zeilen und deren Projektion in 30.000 „MiniDesigner“-Objekte mit Dapper 25 % schneller als mit EF unter Verwendung roher SQL war. Ich empfehle wiederum das Erstellen eigener Leistungsprofile, um Entscheidungen für Ihr eigenes System zu treffen.

Dapper kann auch verwendet werden, um Daten per Push in die Datenbank mittels Methoden zu übertragen, die es Ihnen ermöglichen zu bestimmen, welche Eigenschaften für die vom Befehl angegebenen Parameter gewählt werden müssen. Dies gilt unabhängig davon, ob Sie einen rohen INSERT- oder UPDATE-Befehl verwenden oder eine Funktion oder gespeicherte Prozedur auf die Datenbank anwenden. Für diese Aufgaben habe ich keine Leistungsvergleiche angestellt.

Praxisbeispiele für hybrides Dapper plus EF

Es gibt unzählige Systeme, die Dapper zu 100 % zur Erfüllung ihrer Anforderungen an Datenpersistenz nutzen. Doch wie Sie sich erinnern, wurde mein Interesse erregt, da Entwickler sich über Hybridlösungen unterhielten. Mitunter gibt es Systeme mit vorhandenem EF, die versuchen, bestimmte Problembereiche in den Griff zu bekommen. In anderen Fällen haben sich Teams entschieden, Dapper für alle Abfragen und EF für alle Speichervorgänge zu nutzen.

Als Antwort auf meine diesbezügliche Frage auf Twitter habe ich verschiedene Rückmeldungen erhalten.

@garypochron erzählte mir, dass sein Team weitere daran arbeite, Dapper in Bereichen mit hohem Aufrufaufkommen zu nutzen, und Ressourcendateien zum Verwalten der Organisation von SQL einsetze. Ich war überrascht zu erfahren, dass Simon Hughes (@s1monhughes), Entwickler des beliebten EF Reverse POCO Generator, genau den entgegengesetzten Weg wählt und Dapper standardmäßig und EF für knifflige Probleme nutzt. Er sagte mir, dass er nach Möglichkeit auf Dapper und bei komplexen Updates auf EF setze.

Ich habe auch schon verschiedene Diskussionen erlebt, bei denen der Hybridansatz eher dem Wunsch nach einer Trennung der Zuständigkeiten als einer Steigerung der Leistung folgt. Die gängigsten dieser Ansätze nutzen die Standardabhängigkeit der ASP.NET-Identität von EF und anschließend Dapper für die restlichen Persistenzanforderungen in der Lösung.

Ein direkteres Arbeiten mit der Datenbank hat neben Leistungs- noch andere Vorteile. Die beiden SQL Server-Experten Rob Sullivan (@datachomp) und Mike Campbell (@angrypets) mögen Dapper sehr. Sullivan stellt heraus, dass man Datenbankfeatures nutzen könne, auf die EF keinen Zugriff bietet, wie z. B. die Volltextsuche. Langfristig geht es bei diesem besonderen Feature tatsächlich um Leistung.

Andererseits lassen sich mit EF neben der Nachverfolgung von Änderungen Dinge verwirklichen, die mit Dapper nicht möglich sind. Ein gutes Beispiel ist dasjenige, das ich beim Erstellen der Lösung für diesen Artikel genutzt habe: die Fähigkeit zum Migrieren Ihrer Datenbank, sobald sich das Modell ändert, mithilfe von EF Code First-Migrationen.

Dapper eignet sich allerdings nicht für jeden. @damiangray erzählte mir, dass Dapper keine Option für seine Lösung ist, da er in der Lage sein muss, „IQueryable“-Elemente und nicht tatsächliche Daten aus einem Teil seines Systems in einen anderen zurückzugeben. Dieses Thema (verzögerte Abfrageausführung) wurde im GitHub-Repository von Dapper unter bit.ly/22CJzJl angesprochen, falls Sie mehr darüber wissen möchten. Beim Entwerfen eines Hybridsystems empfiehlt sich das Befolgen des CQS-Prinzips (Command Query Separation, Trennung von Befehl und Abfrage), gemäß dem Sie getrennte Modelle für bestimmte Arten von Transaktionen entwerfen (wovon ich ein Fan bin). Auf diese Weise versuchen Sie, Datenzugriffscode zu erstellen, der durchschnittlich genug ist, um sowohl mit EF und Dapper zu funktionieren, was häufig zum Verzichten auf Vorteile des jeweiligen ORM führt. Während meiner Arbeit an diesem Artikel hat Kurt Dowswell den Beitrag „Dapper, EF and CQS“ (bit.ly/1LEjYvA) veröffentlicht. Praktisch für mich und praktisch für Sie.

Denjenigen, die CoreCLR und ASP.NET Core entgegensehen, ist zu sagen, dass Dapper diese jetzt auch unterstützt. Weitere Informationen finden Sie in einem Thread im GitHub-Repository von Dapper unter bit.ly/1T5m5Ko.

Ich habe also endlich einen Blick auf Dapper geworfen. Sie möchten wissen,

was ich davon halte? Ich bereue, mir nicht früher Zeit genommen zu haben, um mich mit Dapper zu beschäftigen, und bin froh, es getan zu haben. Ich habe stets empfohlen, „AsNoTracking“ bzw. Sichten oder Prozeduren in der Datenbank zu verwenden, um Leistungsprobleme in den Griff zu bekommen. Das war für mich oder meine Kunden nie ein Nachteil. Doch nun habe ich ein weiteres Ass im Ärmel, das ich Entwicklern empfehlen kann, die aus ihrem mit EF arbeitenden Systemen mehr Leistung herausholen möchten. Es handelt allerdings nicht um einen todsicheren Tipp. Meine Empfehlung ist, Dapper in Augenschein zu nehmen, die Leistungsunterschiede (je nach Skalierung) zu messen und eine Balance zwischen Leistung und Einfachheit der Programmierung zu finden. Sehen Sie sich den offenkundigen Zweck von Stack Overflow an: Fragen, Kommentare und Antworten abfragen und anschließend Graphen von einer Frage mit dazugehörigen Kommentaren und Antworten zusammen mit verschiedenen Metadaten (Bearbeitungen) und Benutzerinformationen zurückgeben. Auf Stack Overflow erfolgen dieselben Arten von Fragen und das Zuordnen derselben Form von Ergebnissen immer und immer wieder. Dapper ist darauf ausgelegt, bei dieser Art sich wiederholender Abfragen zu glänzen und jedes Mal intelligenter und schneller zu werden. Auch wenn Sie kein System mit einer solch irrwitzigen Anzahl von Transaktionen haben, auf die Dapper ausgelegt wurde, werden Sie vermutlich feststellen, dass eine Hybridlösung genau das ist, was Sie brauchen.


Julie Lermanist Microsoft MVP, .NET-Mentorin und Unternehmensberaterin. Sie lebt in den Bergen von Vermont. Sie hält bei User Groups und Konferenzen in der ganzen Welt Vorträge zum Thema Datenzugriff und anderen .NET-Themen. Julie Lerman führt unter thedatafarm.com/blog einen Blog. Sie ist die Autorin von „Programming Entity Framework“ sowie der Ausgaben „Code First“ und „DbContext“ (alle bei O’Reilly Media erschienen). Folgen Sie ihr auf Twitter: @julielerman, und sehen Sie sich ihre Pluralsight-Kurse unter juliel.me/PS-Videos an.

Unser Dank gilt den folgenden technischen Experten von Stack Overflow für die Durchsicht dieses Artikels: Nick Craver und Marc Gravell
Nick Craver (@Nick_Craver) fungiert als Entwickler, Site Reliability Engineer und manchmal auch DBA für Stack Overflow. Seine Spezialgebiete sind die Leistungsoptimierung auf allen Ebenen, allgemeine Systemarchitektur, Hardware im Rechenzentrum und die Pflege zahlreicher Open-Source-Projekte wie beispielsweise Opserver. Seine Website finden Sie unter.

Marc Gravell ist Entwickler bei Stack Overflow mit besonderem Schwerpunkt auf Hochleistungsbibliotheken und Tools für .NET, insbesondere für die Bereiche Datenzugriff, Serialisierung und Netzwerk-APIs. Er wirkt in diesen Bereichen auch an verschiedenen Open-Source-Projekten mit.