Manfred Steyer: Stored Functions mit Entity Framework Code First nutzen

Während Entity Framework prinzipiell mit Stored Functions bzw. Table Valued Functions, die innerhalb von Abfragen eingesetzt werden können, umgehen kann, hat diese Möglichkeit noch nicht den Weg in die Welt von Code First geschafft. Allerdings können Entwickler seit Entity Framework 6.1 auf die Mapping API zugreifen. Dabei handelt es sich um ein Objektmodell, welches in vergangenen Versionen lediglich interne zur Verfügung stand und sowohl lesenden als auch schreibenden Zugriff auf das Entity Data Model gewährt. Dies kann ma n sich zunutze machen, um eine Unterstützung für Stored Functions bzw. Table Valued Functions zu implementieren. Da das Entity Data Model und seine Repräsentation durch die Mapping API recht komplex sind, handelt es sich bei dieser Aufgabe nicht um etwas, das man nebenbei erledigen kann.

Glücklicherweise hat sich darum bereits jemand angenommen und das Ergebnis dieses Unterfangens als freies Projekt bereitgestellt. Dieses findet man unter [1]. Es bietet für die Konfiguration von Sotred Functions bzw. Table Valued Functions in Code-First-Szenarien eine einfache API, welche die Komplexität der Mapping-API verbirgt. Jenen, die wissen möchten, wie dieses Projekt die Mapping-API verwendet oder nicht von einem externen Projekt abhängig sein wollen, bietet dieser Beitrag eine nähere Beschreibung des von diesem Projekt verfolgten Ansatzes.

Für jede gewünschte Stored Function ist eine Methode in der verwendeten DbContext-Implementierung einzurichten. Diese kann beliebige Parameter, die sich auf primitive Typen abstützen, entgegennehmen und liefert ein IQueryable retour. Obwohl es sich bei T um einen primitiven Typen oder auch um einen komplexen Typen handeln könnte, gehen die nachfolgenden Beispiele davon aus, dass T den Typ einer Entität wiederspiegelt. Die einzige Aufgabe dieser Methoden besteht darin, an die Methode CreateQuery zu delegieren, damit diese die gewünschte Stored Function aufruft. CreateQuery ist Teil des vom DbContext verwendeten ObjectContext-Objektes. Im betrachteten Beispiel wird auf diese Weise die Stored Function GetHotels angestoßen.

public virtual IQueryable<Hotel> GetHotels(int? regionId)
{
    var regionIdGenerator = regionId.HasValue ?
        new ObjectParameter("regionId", regionId.Value) :
        new ObjectParameter("regionId", typeof(string));

    return ((IObjectContextAdapter)this).ObjectContext
        .CreateQuery(
            string.Format("[{0}].{1}", GetType().Name, "[GetHotels](@regionId)"), regionIdGenerator);
}

Damit Entity Framework die aufgerufene Stored Function kennt, muss der Entwickler dafür sorgen, dass sie im Entity Data Model definiert wird. Dazu kommt eine StoreModelConvention, welche dem Entwickler Zugriff auf die Mapping-API gibt, zum Einsatz. Da diese Implementierung auch den verwendeten DbContext benötigt, erhält sie einen Typparameter T, über den der Konsument ihren Typ angeben kann.

public class CustomFunctionsConvention<T> : IStoreModelConvention<EntityContainer>
    where T : DbContext
{
    […]
}

Das Interface IStoreModelConvention gibt eine Methode Apply vor, welche zu implementieren ist. Das nachfolgende Beispiel zeigt eine Implementierung, welche die zuvor erwähnte Stored Function GetHotels ins sowohl zum Store Model als auch zum Conceptual Model des Entity Data Models hinzufügt und eine Verbindung über das Mapping herstellt. Dieses Beispiel stützt sich auf zwei nachfolgend beschriebene Hilfsmethoden: CreateConceptualFunctionDefinition erzeugt die Definition für das Conceptual Model und CreateStoreFunctionDefinition erstellt das Gegenstück für das Store Model.

public void Apply(EntityContainer item, DbModel model)
{
    var functionImportDefinition 
           = CreateConceptualFunctionDefinition (model);
    var storeFunctionDefinition 
           = CreateStoreFunctionDefinition(model);


    model.ConceptualModel.Container.AddFunctionImport(functionImportDefinition);
    model.StoreModel.AddItem(storeFunctionDefinition);


    var mapping = new FunctionImportMappingComposable(
                        functionImportDefinition,
                        storeFunctionDefinition,
                        new FunctionImportResultMapping(),
                        model.ConceptualToStoreMapping);


    model.ConceptualToStoreMapping.AddFunctionImportMapping(mapping);
}

Die Methode CreateConceptualFunctionDefinition findet sich im nachfolgenden Beispiel. Sie erzeugt Beschreibungen der Übergabeparameter und des Rückgabewertes. Um zu den Beschreibungen der hiervon betroffenen Datentypen zu gelangen, stützt sie sich auf die Hilfsmethoden GetPrimitiveType und GetEntityType, welche weiter unten abgebildet sind. Die Variable functionPayload repräsentiert die gesamte Stored Function inkl. Parameter und Rückgabewerte. Interessant sind hier die Eigenschaften IsComposable und IsFunctionImport, welche auf true gesetzt werden. Erstere gibt an, dass es sich hierbei um eine Funktion handelt, die von SQL-Anweisungen (z. B. innerhalb eines SELECTs) aufgerufen werden kann. Letztere gibt an, dass es sich hiermit eine Stored Function auf konzeptioneller Ebene handelt. Die Funktion EdmFunction.Create erzeugt mit der functionPayload ein Objekt vom Typ EdmFunction und liefert dieses retour. Im Zuge dessen wird auch der Name der Funktion angegeben sowie die Tatsache, dass es sich hiermit um ein Element für das Conceptual Model handelt, durch Angabe von DataSpace.CSpace unterstrichen.

private EdmFunction CreateConceptualFunctionDefinition(DbModel model)
{
    // Describe In-Parameter
    var parameterType = GetPrimitiveType(typeof(int));

    var parameter = FunctionParameter.Create("regionId", 
                                parameterType, ParameterMode.In);


    // Describe returnValue

    var returnValueType = GetEntityType(model, "Hotel");

    var returnValue = FunctionParameter.Create(
                                    "ReturnParam",
                                    returnValueType.GetCollectionType(),
                                    ParameterMode.ReturnValue);

    // Betroffene EntitySets

    var entitySets = model
                        .ConceptualModel
                        .Container
                        .EntitySets
                        .Where(s => s.ElementType == returnValueType)
                        .ToList();

    var functionPayload = new EdmFunctionPayload {
            Parameters = new [] { parameter },
            ReturnParameters = new[] { returnValue },
            IsComposable = true,
            IsFunctionImport = true,
            EntitySets = entitySets
    };

    return EdmFunction.Create(
        "GetHotels",
        model.ConceptualModel.Container.Name,
        DataSpace.CSpace,
        functionPayload,
        null);
}

GetPrimitiveType findet in einer von Entity Framework vordefinierten Auflistung, welche sämtliche primitive Datentypen beinhaltet, eine Beschreibung für den übergebenen CLR-Typen:

private static PrimitiveType GetPrimitiveType(Type type)
{
    var parameterType = PrimitiveType
                                    .GetEdmPrimitiveTypes()
                                    .Where(pt => pt.ClrEquivalentType == type)
                                    .First();
    return parameterType;
}

GetEntityType muss hingegen im Entity Data Model nach der Beschreibung des Typs, welcher die gewünschte Entität wiederspiegelt, suchen. Dazu wird die Auflistung EntityTypes verwendet. Für komplexe Typen und Enum-Typen stehen analog dazu weitere Auflistungen zur Verfügung:

private static EntityType GetEntityType(DbModel model, string name)
{
    var returnValueType = (EntityType) model.ConceptualModel.EntityTypes.Where(t => t.Name == name).First();
    return returnValueType;
}

Die Methode CreateStoreFunctionDefinition erzeugt eine Beschreibung der Stored Function für das Store Model. Sie ist analog zur oben betrachteten Funktion CreateConceptualFunctionDefinition aufgebaut. Allerdings verwendet sie anstatt der Beschreibungen der Typen aus dem Conceptual Model Beschreibungen der Typen der jeweiligen Datenbank, welche über die Methode GetStoreType in Erfahrung gebracht werden. Der Rückgabewert, bei dem es sich im Conceptual Model um eine Entität handelt, wird hier durch einen RowType, der aus verschiedenen Spalten besteht, beschrieben.

private EdmFunction CreateStoreFunctionDefinition(DbModel model)
{
    // Describe In-Parameter
    var conceptualParameterType = GetPrimitiveType(typeof(int));
    var parameterUsage = TypeUsage.CreateDefaultTypeUsage(conceptualParameterType);
    var parameterType = model.ProviderManifest.GetStoreType(parameterUsage).EdmType;

    var parameter = FunctionParameter.Create("regionId",
                                             parameterType, 
                                             ParameterMode.In);


    // Describe returnValue
    var conceptualReturnType = GetEntityType(model, "Hotel");
    var returnProperties = new List();

    var propertyToStoreTypeUsage = FindStoreTypeUsages(conceptualReturnType, model);


    foreach (var p in conceptualReturnType.Properties)
    {

        var usage = propertyToStoreTypeUsage[p];
        returnProperties.Add(EdmProperty.Create(p.Name, usage));
    }

    var returnValueType = RowType.Create(returnProperties, null);     

    var returnValue = FunctionParameter.Create(
                                    "ReturnParam",
                                    returnValueType.GetCollectionType(),
                                    ParameterMode.ReturnValue);

    var functionPayload = new EdmFunctionPayload
    {
        Parameters = new[] { parameter },
        ReturnParameters = new[] { returnValue },
        IsComposable = true,
        Schema = "dbo"
    };

    return EdmFunction.Create(
        "GetHotels",
        "CodeFirstDatabaseSchema",
        DataSpace.SSpace,
        functionPayload,
        null);

}

Um die richtigen Typen hierfür zu finden, stützt sich das betrachtete Beispiel auf die Hilfsmethode FindStoreTypeUsages, welche aus [1] übernommen wurde.

private Dictionary FindStoreTypeUsages(EntityType entityType, DbModel model)
{

    var propertyToStoreTypeUsage = new Dictionary();

    var entityTypeMapping =
        model.ConceptualToStoreMapping.EntitySetMappings.SelectMany(s => s.EntityTypeMappings)
            .Single(t => t.EntityType == entityType);

    foreach (var property in entityType.Properties)
    {
        var propertyMapping = (ScalarPropertyMapping)entityTypeMapping.Fragments.SelectMany(f => f.PropertyMappings).Single(p => p.Property == property);

        propertyToStoreTypeUsage[property] = TypeUsage.Create(
            propertyMapping.Column.TypeUsage.EdmType,
            propertyMapping.Column.TypeUsage.Facets.Where(
                f => f.Name != "StoreGeneratedPattern" && f.Name != "ConcurrencyMode"));
    }

    return propertyToStoreTypeUsage;
}

Diese unter Schweiß geschaffene Konvention ist am Ende noch in der Methode OnModelCreating des DbContextes zu registrieren:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    […]
    modelBuilder.Conventions.Add(new CustomFunctionsConvention());
}

Das hier gezeigte Beispiel kann unter [2] heruntergeladen werden. Die verwendete Datenbank kann mittels Migrations erzeugt werden. Anschließend sind noch die Stored Function, deren Quellcode sich in der Datei migrations\functions.sql befindet, einzuspielen und ein paar Testdatensätze (Regionen und Hotels) anzulegen. Die Id der abzufragenden Region wird in der Konstante Program.REGION_ID festgelegt.

Links

Sven Hubert: Ideen für die Verwendung des MSDN Azure Freikontingents: Schnelle Bereitstellung von Virtuellen Maschinen für Installationstests

Lässt sich Ihre entwickelte Anwendung unter Windows 7, Windows 8 oder auch noch unter Windows Server 2012 installieren und ausführen? Klappt das auch unter 32bit, 64bit, deutsch oder englisch? Auch wenn man sich an die Designrichtlinien von Microsoft hält, kann nur ein Test in einer entsprechenden Umgebung Gewissheit verschaffen. Das Vorhalten von entsprechenden Maschinen für eine entsprechende Anzahl an Entwicklern und Testern wird schnell zu einem Ressourcen- und Verwaltungsproblem. Als allumfassende Unternehmenslösung bietet sich das Lab Management des TFS an. Wer nur sporadisch eine virtuelle Maschine benötigt oder die Komplexität und Kosten der Unternehmenslösung scheut, kann alternativ auf Windows Azure zurückgreifen. Viele Betriebssysteme stehen im Image Katalog bereits zur Verfügung. Weitere lassen sich bei Bedarf ergänzen.

Eine virtuelle Maschine ist in weniger als 5 Minuten aufgesetzt. Dank dem MSDN Azure Freikontingent ist der Betrieb in den meisten Szenarien kostenlos. Die Einrichtung gestaltet sich wie gewohnt sehr einfach.

Aus der Image Library wird die benötigte Betriebssystemversion ausgewählt.

Einrichtung eines Virtuellen Computers

Einrichtung eines Virtuellen Computers

Es stehen Windows 7, Windows 8.1, Windows 2008 R2 und Windows Server 2012 R2 jeweils in englisch zur Verfügung.

Auswahlt des Images

Durch die Wahl einer geeigneten Größe einer Maschine können auch unterschiedliche Performance Szenarien überprüft werden.

Auswahl der Größe der Maschine

Nach nur wenigen Minuten ist eine Virtuelle Maschine vollständig eingerichtet.

Herstellung der Remote Desktop Verbindung

Eine Verbindung kann über den Remote Desktop aufgebaut werden.

Die Kosten für den Betrieb der virtuellen Maschine setzen sich aus dem verbrauchten Speicher und der Größe (Hauptspeicher, Anzahl Kerne) zusammen. Im konkreten Beispiel (3,5 GB, 2 Kerne) belaufen sich diese für die VHD auf rund 1,79 € pro Monat und für die Rechenleistung auf rund 11 Cent pro Stunde. Stoppt bzw. löscht man Maschinen, die nicht benötigt werden, können eine ganze Reihe von virtuellen Maschinen im Rahmen des MSDN Azure Freikontingents betrieben werden.

Es ist möglich, weitere für Tests benötigte Betriebssysteme zu ergänzen. Diese setzt man zunächst lokal unter Hyper-V auf. Nach der Installation der Integration Services und dem Durchführen von Sysprep kann ein entsprechend vorbereitetes Image in die Windows Azure Template Gallery eingefügt werden. Eine detaillierte Anleitung ist unter http://azure.microsoft.com/de-de/documentation/articles/virtual-machines-create-upload-vhd-windows-server/ zu finden.

Windows Azure bietet sich damit geradezu für schnelle Installationstests an, insbesondere wenn es an lokalen Ressourcen wie Plattenspeicher mangelt. Dank dem Freikontingent ist diese zudem in den meisten Fällen kostenfrei.

Ideen für die Verwendung des MSDN Azure Freikontingents: Schnelle Bereitstellung von Virtuellen Maschinen für Installationstests is a post from: AIT Blog

Lars Keller: Artikel: Business Apps – Patentrezept für Prozessbeschleunigung?

Zusammen mit Peter Nowak habe ich einen Artikel über Business Apps in der Computerwoche veröffentlicht.

IT-Verantwortliche sehen Business Apps inzwischen häufig als ideale Werkzeuge für schnelle und flexible Geschäftsprozesse. Diesen Anspruch erfüllen standardisiert programmierte Anwendungen allerdings oft nicht. Für den erhofften Erfolg muss eine App spezifisch auf die Unternehmens-IT zugeschnitten sein – genauso wie auf ihre User. Wir zeigen, worauf es im Entwicklungsprozess ankommt.

Den ganzen Artikel findet Ihr hier: http://www.computerwoche.de/a/business-apps-patentrezept-fuer-prozessbeschleunigung,3064460

Viel Spaß beim Lesen!

Lars Keller: cim–community in motion Konferenz

Letztes Jahr war es noch die ice-lingen, dieses Jahr im neuen Gewand erwacht die cim – community in motion. Die Anmeldung ist ab heute eröffnet!

Frank Solinske und ich sind wieder dabei und werden etwas zum Thema Mobility erzählen!

Wir sehen uns dort! :-)

ppedv Team Blog: Zugriffsreporting für Blogengine.net mit Logparser

Ich vertrete die These, das Suchmaschinen generierter Traffic im abnehmen ist. Also google muss sein Geschäftsmodell auf den Prüfstand stellen. Das tun sie auch, indem sie z.B. Werkzeuge wie Google Analyze zur Verfügung stellen. Damit übermittelt der Benutzer nicht nur Suchdaten, sondern auch Telemetrie Infos zur Nutzung von Websites. Auch andere Dienste wie Url Shortener sind Datenkraken, wie auch Scott Hanselman in seinem Blog anmerkt. Die Alternative einen eigenen Dienst mit Piwik aufzusetzen, hilft mehr Anonymität ins Web zu bringen. Falls der Benutzer im Browser dies unterbindet, bleibt nur noch der Blick in die Logfiles des Webservers. In unserem Fall des Internet Information Servers (kurz IIS), der von  Microsoft gratis mitgeliefert wird.

Für die Logfile Analyse hat Microsoft vor vielen Jahren den Logparser entwickelt. Ein sehr schneller kostenloser Parser. Im Stil von SQL Abfragen kann man durchaus forensische Analysen vornehmen.

Die Frage lautet konkret, wie viele User kommen durch eine Suchmaschine auf einen Blogeintrag, pro Monat.

In diesem Blog erkennt man einen Blogeintrag an der Sequenz /blog/ in der URl. Damit fallen alle direkten Aufrufe von blog.ppedv.de aus der Analyse heraus. In einem Google Ergebnisbild, wird ein suchender (ich liebe das Wort) auch dank der Darstellung vor allem auf den direkten Link clicken.

Stellt sich die  Frage, wie filtert man den Traffic der Spider heraus, Bisher hatten wir im Logparser die Namen der Bots in die Where Bedingung eingeschlossen.  Da man niemals weis, was man nicht weis, können auch unbekannte Spider agieren. Woran erkennt man also den Crawler einer Suchmaschine. Der sollte die Datei Robots.txt abrufen. Ein starkes Indiz für die Beweiskette,. Da die IP Adressen der Crawler endlich und in einem gewissen Maße auch fix sind kann man sich so eine Liste der Adressen anlegen.

   1:  logparser  "SELECT c-ip, cs(User-Agent) AS [UserAgent], COUNT(*) AS [summe]     
   2:  , MIN(TO_DATE(TO_LOCALTIME(TO_TIMESTAMP(date, time)))) AS FirstDate     
   3:  , MAX(TO_DATE(TO_LOCALTIME(TO_TIMESTAMP(date, time)))) AS LastDate 
   4:  INTO C:\LOGPARSER\LOG_RESULT\Robots.log 
   5:  FROM C:\inetpub\logs\LogFiles\W3SVC1\*.log 
   6:  WHERE cs-uri-stem = '/robots.txt' GROUP BY c-ip, UserAgent 
   7:  ORDER BY c-ip, UserAgent " -i:iisw3c -o:CSV

 

Im nächsten Schritt werden die Datensätze aus den Logfile kumuliert per Monat unter Ausschluss der IP Adressen der Suchmaschinen.

   1:  logparser  "select  TO_STRING(date, 'MMMM, yyyy') AS Month,count(c-ip) as blog
   2:   INTO C:\LOGPARSER\LOG_RESULT\zugriffe3d_blog.gif 
   3:  from c:\inetpub\logs\LogFiles\W3SVC1\*.log  
   4:  WHERE cs-uri-stem LIKE '%%/post/%%' 
   5:  and  c-ip not in (select TO_STRING(c-ip) 
   6:  from  C:\LOGPARSER\LOG_RESULT\Robots.log) 
   7:  group by Month " 
   8:  -o:chart -groupSize:1024x768 -i:iisw3c -chartType:ColumnStacked -view:off

 

Für alle interessierten die Zugriffszahlen als 2D Balkendiagramm.

image

Holger Schwichtenberg: Anmeldung zur cim-Konferenz in Lingen seit heute Nacht möglich

Seit heute Nacht 0:00 ist die Anmeldung für die Community-Konferenz "community in motion" (cim) am 13.9.2014 in Lingen möglich.

ppedv Team Blog: Sortieren und Filtern in einer Liste mit Angular.js

Anhand von Praxis bezogenen Anwendungsfällen werden in meinen Angular Blogposts konkrete Funktionen aus der JavaScript Bibliothek demonstriert. In diesem Code Beispiel wird eine Liste, deren Inhalte aus einem ASP.NET Web Api Service stammen, sortiert und gefiltert. Die Daten kommen aus der Northwind SQL Server Datenbank. Am Ende wird eigentlich nur JSON übermittelt. Es geht auch ohne Service.

Ziel ist das ein Suchfeld für Filtern der Daten und die Sortierung durch Click auf den Header zu ändern.

image

Das Angular Module wird mit einem Controller versehen. Dieser enthält den Scope, der als Viewmodel dient und die Daten enthält. Hier die Liste der Kunden, aus der Rückgabe des REST Ajax calls.

   1:  angular.module('kunden2App', [])
   2:  .controller('CRUDController', function ($scope, $http) {
   3:      $scope.customers = [];
   4:      $http.get('api/Customers/').success(function (data) {
   5:          $scope.customers = data;
   6:      })
   7:      .error(function (data) {
   8:          $scope.error = "Fehler " + data.ExceptionMessage;
   9:      });
  10:  });

Am einfachsten lässt sich eine Suche implementieren.  Dem Viewmodel wird einfach per ng-model Direktive einen neue und gebundene Eigenschaft zugewiesen. Hier in Zeile 5 mit search bezeichnet. In ng-repeat wird über das Pipe Symbol getrennt nach der iterationsanweisung (Zeile 11) der Filter Ausdruck angefügt. In diesem Fall wird der von Angular vordefinierte Filter verwendet. Es lassen sich auch eigene Filter mit der Notation .filter in JavaScript erstellen. Die Dokumentation hat dazu ein nettes Beispiel, das Check Icons darstellt.

Andere vordefinierte Filter gib es z.B. für Formatierung von Datumswerten. Dieser wird dann direkt im Binding angegeben.

{{1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'}}

Auch die voreingestellte Sortierung wird über den Order By Filter definiert. Aber auch der Benutzer kann die Reihenfolge der Firmennamen ändern indem er auf den Header clickt. Hier werden die Direktiven ng-click und ng-source eingesetzt. Aus erstem wird ein Click Event, das den Wert von reverse toggelt. Dieser Wert wird zur Steuerung des Orderby Statements verwendet. Um ein Icon anzuzeigen wird die Image Source an ein Bild gebunden, das ich downfalse.gif und downtrue.gif genannt habe. Ein kleiner Trick mit geringem Code und großer Wirkung.

   1:   
   2:  <body ng-app="kunden2App">
   3:     <div ng-controller="CRUDController">
   4:         <h2>Customers</h2>
   5:         Name Suchen <input type="text" ng-model="search" />
   6:         <table >
   7:           <tr>
   8:             <th>#</th>
   9:             <th> <a href="" 
ng-click="reverse=!reverse;order('CompanyName', reverse)">Name
<img ng-src="img/down{{reverse}}.gif"></a></th>
  10:           </tr>
  11:           <tr 
ng-repeat="customer in customers | filter:search | orderBy:'CustomerID':reverse" >
  12:               <td>{{ customer.CustomerID }}</td>
  13:               <td>{{ customer.CompanyName }}</td>
  14:            </tr>
  15:         </table>
  16:     </div>
  17:  <script src="Scripts/angular.js"></script>
  18:  <script src="CRUDController.js"></script>
  19:  </body>
  20:   

Am Ende sehr überschaubarer Aufwand.

Fabian Deitelhoff: Zwei ständige, digitale Begleiter

Mit den Jahren kristallisieren sich einige Anwendungen heraus, die ich immer wieder und recht zügig auf neue Systeme installiere. Bei mir sind das – Stand heute – zwei ständige Begleiter, die ich auf Windows-Systeme installiere. Eine der Anwendungen nur auf Host-Systeme. Die andere sowohl auf Host- als auch auf Gast-Systeme.

Diese beiden Anwendungen möchte ich hier kurz vorstellen und die Vorzüge aufzeigen. Weitere wichtige Werkzeuge, sowohl für den normalen Windows-Alltag als auch für (.NET) Entwickler, habe ich in einem Artikel für die windows.developer zusammengefasst.

Clover

Die erste Anwendung nennt sich Clover. Dahinter verbirgt sich eine Erweiterung für den Windows Explorer, die Tabs zu eben diesem Windows Explorer hinzufügt. Diese Tabs ähneln dem Browser Chrome.

Um die Funktionalität nutzen zu können, ist nach dem Download des zip-Archivs nur eine Installation notwendig. Das erledigt die ausführbare Datei im zip-Archiv anstandslos. Danach ersetzt Clover sozusagen die Funktionen vom Windows Explorer. Also immer wenn ein Explorer Fenster geöffnet wird, erscheint Clover und fügt beispielsweise den Ordner als Tab ein. Allerdings nicht nur für den Explorer an sich. Sondern auch für die Systemsteuerung, um nur ein Beispiel zu nennen. Alle Anwendungen, die etwas mit dem Windows Explorer zu tun haben, werden ab sofort in Clover als Tab dargestellt. Nur als Hinweis am Rande, weil das vielleicht nicht gewünscht ist. Ich persönliche finde das hervorragend, da ich jetzt nicht mehr verschiedene Stellen nach Informationen durchsuchen muss, sondern sofort weiß, wo ich zu suchen habe. Abbildung 1 zeigt Clover im Einsatz.

Abb. 1: Clover im Einsatz mit zwei Explorer Fenstern.

Abb. 1: Clover im Einsatz mit zwei Explorer Fenstern.

Im oberen Bereich gibt es noch eine Leiste für Lesezeichen. Diese und die Tabs erinnern deutlich mehr an einen Browser als an den alten Windows Explorer :). Daneben gibt es noch weitere Erleichterungen. Ein Tab kann beispielsweise mit der mittleren Maustaste geschlossen werden. Die mittlere Maustaste ist ebenfalls für das Öffnen eines beliebigen Objekts in einem neuen Tab zuständig. In den Einstellungen verbirgt sich zudem die Option, die in der vorherigen Session geöffneten Tabs beim erneuten Öffnen wieder anzuzeigen. Auch das war schon des Öfteren sehr hilfreich.

Alles in allem eine hervorragende Erweiterung die ich nur wärmstens empfehlen kann. Die aktuelle Version 3.0.406 unterstützt Windows XP, Windows 7 und Windows 8. Clover macht sich im Übrigen auch in virtuellen Maschinen sehr gut. Es ist also die Anwendung, die ich nicht nur auf Host-Systeme installiere.

f.lux

Als zweites installiere ich regelmäßig die Anwendung f.lux. Sie sorgt dafür, dass sich, ab einem bestimmten Zeitpunkt, die Farbtemperatur der angeschlossenen Bildschirme verändert. Auf der Webseite wird das Programm mit Phänomenen beworben, die jeder kennen wird, der viele Stunden des Tages am Rechner verbringt. Insbesondere, wenn es sehr früh oder sehr spät ist.

Ich denke jeder kennt den gespenstischen blauen Schein, der Leute umgibt, die vor dem Bildschirm sitzen. Oder wenn der Rechner eingeschaltet wird und es noch recht dunkel ist. Teilweise fühle ich mich dann immer wie geblendet. Hintergrund ist, dass Bildschirme so konzipiert sind, Licht ähnlich der Sonne zu emittieren. Dieses Licht hat einen hohen Blauanteil, was zu dem oben geschilderten blauen Schein führt. Abbildung 2 zeigt die sehr minimalistische Oberfläche von f.lux.

Abb. 2: Minimalistische Oberfläche von f.lux.

Abb. 2: Minimalistische Oberfläche von f.lux.

Wie die Abbildung zeigt, ist der Screenshot kurz vor dem Tag-Nacht-Wechsel entstanden. Im Moment ist das circa um 21:30 Uhr mitteleuropäischer Sommerzeit. Dann ändert sich die Farbtemperatur von 6.500 Kelvin (Tag) auf 3.400 Kelvin (Nacht). Das ist ein sehr deutlicher Unterschied, der am Anfang recht komisch wirkt.

Lange musste ich mich allerdings nicht daran gewöhnen. Die Vorteile fallen sehr schnell auf. Ich werde bei der Bildschirmarbeit deutlich weniger geblendet, habe weniger Kopfschmerzen und kann auch früh Morgens auf den Bildschirm gucken, ohne gleich ein neues Paar Augen zu brauchen. Da Screenshots von dem Farbwechsel nichts bringen und meine Fotos deutlich zu schlecht sind, kann ich nur empfehlen, es selbst einmal auszuprobieren.

Der Download von f.lux steht für Mac, Linux, Windows und iPhone/iPad zur Verfügung. Unter Windows erledigt ein Setup die Installation ohne Probleme. Beim ersten Start sollte direkt der eigene Standard eingestellt werden. Der bestimmt nämlich, zur welcher Uhrzeit der Tag-Nacht-Wechsel genau stattfindet und passt sich dem Jahresverlauf an. Da f.lux die Farbtemperatur der Bildschirme global regelt und es hier überhaupt keine lokale Einstellung gibt, installiere ich die Anwendung nur auf Host-Systemen.

Auch hier eine sehr klare Empfehlung meinerseits. Einfach mal ausprobieren und dem Wechsel der Farbtemperatur eine Chance geben :). Wer übrigens wieder den Nacht-Tag-Wechsel mitbekommt, ist entweder sehr früh aufgestanden oder saß eindeutig zu lange vor dem Bildschirm ;).

Ich freue mich immer über weitere, nützliche Tools. Einfach mailen oder direkt in die Kommentare schreiben.

ppedv Team Blog: Paging mit Angular.js

Man hat eine Idee. Wenn grad niemand für eine Diskussion bereit ist, stürzt man sich auf Google (sorry bing) und recherchiert was andere so schreiben. Danach fühlt man sich mit jeder Minute dümmer. Breeze Framework hier, vierhundert Zeilen State of the Art Injection, Factory per Direktive JavaScript Source dort. Unzählige Github Projekte die nach Download nie laufen und dann noch ein paar Nuget Pakete an deren Aktualität man leichte Zweifel hat.

Meine Meinung zu Paging hat sich im Laufe der Zeit mit wachsenden Datenmengen stark gewandelt. Nummerische Paging hinterlässt durchaus einige Sinnfragen. Niemand weis, was sich auf Seite 3 befindet und überspringt Seite 2, niemand blättert bis ans Ende des Google Index. Ich halte ein Forward Only Paging mit wachsender Menge an gleichzeitig dargestellten Daten für die beste Lösung. Oder eine sehr gute Suche.

image

In diesem Projekt wird ein ASP.NET Web Api Service mit Odata Query Syntax seitenweise abgefragt. Schon vorweg: die Schönheit von LINQ sucht man vergeblich. Breeze soll da Abhilfe schaffen. Das ändert nichts daran das weite Teile von Angular mit Zeichenketten operieren, wie man das aus den Anfängen der SQL Sprache kennt. Nachteile unter anderem: keine Intellisense Unterstützung, keine Compilerfehler.

Basis ist ein ASP.NET Web API Odata Service, den ich in einem weiteren Blog Artikel beschrieben habe. Um Seitenweise Ergebnisse zu erhalten kommt folgende ODATA URI Query zum Einsatz

$top=10&$skip=0&$orderby=CustomerName&$inlinecount=allpages

Der Parameter $top liefert immer 10 Datensätze. Mit Skip werden die bereits geladenen übersprungen. In der Regel solle in Paging Szenarien sortiert werden, hier nach CustomerName. Besonderes Highlight ist der Parameter InlineCount der die Anzahl der gesamten Records zurück liefert.

Eine auf Angular basierende SPA Anwendung besteht aus einer HTML5 Datei und einem JavaScript Controller. Die HTML Datei enthält eine Referenz auf die Controller JS und das Angular Framework.

Mit dem Attribut ng-app wird Angular instanziiert. Der Controller wird per ng-controller Direktive zugewiesen.

   1:  <body ng-app="kundenApp">
   2:      <h1>SPA 1</h1>
   3:      <div ng-controller="pagingController">
   4:  ....
   5:   
   6:      <script src="Scripts/angular.js"></script>
   7:      <script src="pagingController.js"></script>
   8:  </body>

 

Ein Controller fungiert ähnlich einem ViewModel und hält den Datencontext ($scope) und die Commands (Events). Ein oder mehrere Controller werden in einem Modul zusammengefasst. Da keine anderen Module eingebunden werden, wird ein leeres Array als Parameter übergeben.

Die Daten werden im Kunden Objekt als Array gehalten. Die Methode LadeRecords wird die ersten 10 Datensätze laden.

   1:  angular.module('kundenApp', [])
   2:   .controller('pagingController', function ($scope, $http) {
   3:      $scope.kunden = [];
   4:      $scope.ladeRecords = function ()
   5:  ....

 

$http ist ein sogenannter Service, der in die Funktion per Parameter eingebunden wird. Per GET wird der REST Service aufgerufen. Die Parameter werden per Querystring übermittelt und als Array der Methode übergeben. Als Page Länge wird hier 10 Datensätze angenommen. Folglich braucht die Paging Logik eine Eigenschaft mit dem Namen Seite.

   1:       $http(
   2:       {
   3:           method: 'GET',
   4:           url: 'http://localhost:1929/odata/CustomersO',
   5:           params: {
   6:               $top: 10,
   7:               $skip: $scope.seite*10,
   8:               $orderby: 'CustomerID',
   9:               $inlinecount: 'allpages'
  10:           }

Das Ergebnis des Ajax Callbacks auf den Service enthält JSON mit einigen zusätzlichen Daten,  wie die Anzahl der Records.

{
  "odata.metadata":"http://localhost:1929/odata/$metadata#CustomersO","odata.count":"92","value":[
    {
      "CustomerID":"ALFKI","CompanyName":"Alfred's Futterkiste Pferd","ContactName":"Alfred","ContactTitle":"Sales Representative","Address":"Martin-Schmeisser-Weg 18","City":"Osnabr\u00fcck","Region":null,"PostalCode":"84489","Country":"Germany","Phone":"030-0074321","Fax":"030-0076545","LastChange":"AAAAAAAAv84="
    },{

Die Rückgabe der Daten werden dann ausgewertet und dem ViewModel, also Scope zugewiesen. Um die neuen Kunden an das bestehende Array anzuhängen wird in JavaScript die Push Methode verwendet., mit ein wenig eigenartiger Syntax (Zeile 2). Die Anzahl wird per odata.Count ausgelesen und letztendlich der Seitenzähler inkrementiert.

   1:   .success(function (data, status) {
   2:          $scope.kunden.push.apply($scope.kunden,data.value);
   3:          $scope.records = data['odata.count'];
   4:          $scope.seite++;

 

Zusammengeführt wird dies in der finalen HTML Seite. Solange keine Daten vorhanden sind, wird eine Wartemeldung angezeigt. Per ng-show Direktive und JavaScript wird diese Meldung automatisch ein, bzw. nachher wieder ausgeblendet.

Mit dem Attribut ng-repeat werden aus dem Viewmodell die Kunden in einer Art for-each durchlaufen. Die Bindungsyntax mit den Doppelgeschweiften Klammern erlaubt den Zugriff auf die Eigenschaften des Kunden Objektes. Fast so wie in WPF oder Silverlight.

Sehr nett ist die durchgeführte Berechnung der Restdatensätze aus den gebundenen Wert Records und der Länge der aktuellen Kundenliste im Browser.

Letztendlich wird noch ein HTML Button mit der Angular Direktive ng-click an eine Methode aus dem Viewmodell gebunden.

   1:  <body ng-app="kundenApp">
   2:      <h1>SPA 1</h1>
   3:      <div ng-controller="pagingController">
   4:          <div ng-show="kunden.length==0">
   5:              Daten werden geladen...
   6:          </div>
   7:          <div ng-repeat="kunde in kunden" >
   8:              {{kunde.CompanyName}}
   9:          </div>
  10:          <div>Noch {{records-kunden.length}}</div>
  11:          <button id="pageButton" ng-click="ladeRecords()"  >mehr..</button>
  12:      </div>
  13:      <script src="Scripts/angular.js"></script>
  14:      <script src="pagingController.js"></script>

 

Auch der JavaScript Code wird nun im pagingController zusammengefasst. Der Eigenschaft ladeRecords wird eine anonymen Methode zugewiesen. Der $http Ajax Service Call erhält eine Success und eine Error Behandlung.

   1:  angular.module('kundenApp', [])
   2:  .controller('pagingController', function ($scope, $http) {
   3:      $scope.kunden = [];
   4:      $scope.ladeRecords = function ()
   5:      {
   6:          $http( {
   7:           method: 'GET',
   8:           url: 'http://localhost:1929/odata/CustomersO',
   9:           params: {
  10:               $top: 10,
  11:               $skip: $scope.seite*10,
  12:               $orderby: 'CustomerID',
  13:               $inlinecount: 'allpages'
  14:           }
  15:       })
  16:      .success(function (data, status) {
  17:          $scope.kunden.push.apply($scope.kunden,data.value);
  18:          $scope.records = data['odata.count'];
  19:          $scope.seite++;
  20:      })  
  21:      .error(function (data, status) {
  22:          alert(data['odata.error'].message.value );
  23:      });    
  24:      };
  25:      $scope.seite = 0;
  26:      $scope.ladeRecords();
  27:  });

 

Wer mehr zu Angular.JS lernen möchte, kann dies auf der ADC X in einem von mir persönlich gehaltenen Workshop tun. Ein kompletter Tag speziell für den WPF, Silverlight Entwickler mit dem Ziel einen Schnelleinstieg in die Welt der SPA’s zu erhalten.

ppedv Team Blog: Der Sinn vom ViewModel–oder was sagt die Fehlermeldung “No parameterless constructor defined for this object” aus

Das MVC Pattern spricht von Model – View – Controller. Jedoch kann es auch im ASP.NET MVC sinnvoll sein ein ViewModel zu verwenden. Gute Architektur ist durch nichts zu ersetzen und beim falschen oder “abgekürzten” Einsatz eines Frameworks wie zum Beispiel ASP.NET MVC stellt mal bald fest: man hält sich besser an Architektur Empfehlungen als das Rad neu zu erfinden.

Konkret wollte ich ein Eingabeformular gestalten in dem eine DropDownliste verwendet wird. Im System.Web.Mvc Namenspace gibt es eine Klasse “SelectList” und diese ist für die Darstellung einer Auswahlliste sehr gut geeignet da sie auch direkt im @Html.DropDownListFor unterstützt wird.

Der einfache und fehlerhafte Weg…

Möchte man sich die Arbeit erleichtern, kommt man schnell auf die Idee ein Model anzulegen und in diesem auch die Select Liste zu implementieren. Ich habe dies in der Klasse “DatenEingabeModel” so getan und im Konstruktor wird die Liste mit den möglichen Auswahlwerten gefüllt. Zusätzlich enthält die Klasse das Property “AusgewaehlterWert”  um die Auswahl des Benutzers abzuspeichern.

   1:      public class DatenEingabeModel
   2:      {
   3:          public string AusgewaehlterWert { get; set; }
   4:   
   5:          private SelectList werteListe = null;
   6:          public SelectList WerteListe
   7:          {
   8:              get { return werteListe; }
   9:          }
  10:   
  11:          public DatenEingabeModel()
  12:          {
  13:              var tempListe = new List<SelectListItem>();
  14:              tempListe.Add(new SelectListItem() { Text = "Wert A", Value = "A" });
  15:              tempListe.Add(new SelectListItem() { Text = "Wert B", Value = "B" });
  16:              tempListe.Add(new SelectListItem() { Text = "Wert C", Value = "C" });
  17:   
  18:              werteListe = new SelectList(tempListe, "Value", "Text");
  19:          }
  20:      }

Das Model wird im DatenEingabeController verwendet. Es gibt eine Action “Save”, diese wird vom View durch das Formular aufgerufen. Sobald die Daten gespeichert wurde, kommt es zu einem Redirect zu einer anderen Action, “OK”, die einen View liefert der die eingegeben Daten nochmals darstellt und dem Benutzer mitteilt, dass das Speichern erfolgreich war. Um die Daten anzeigen zu können, wird das Model an den View in der OK Action weitergereicht.

   1:      public class DatenEingabeController : Controller
   2:      {
   3:          // GET: DatenEingabe
   4:          public ActionResult Index()
   5:          {
   6:              DatenEingabeModel model = new DatenEingabeModel();
   7:              return View(model);
   8:          }
   9:   
  10:          [HttpPost, ActionName("Save")]
  11:          public ActionResult Save(DatenEingabeModel model)
  12:          {
  13:              // Code zum Speichern der Daten
  14:              return RedirectToAction("OK", model);
  15:          }
  16:   
  17:          public ActionResult OK(DatenEingabeModel model)
  18:          {
  19:              return View(model);
  20:          }
  21:      }

Zuletzt noch der Index View: dieser verwendet @Html.DropDownListFor um das Property “AusgewaehlterWert” als DropDownListe anzuzeigen. (Zeile 18)

   1:  @model ViewModelDemo.Models.DatenEingabeModel
   2:  @{
   3:      ViewBag.Title = "Index";
   4:  }
   5:  <h2>Index</h2>
   6:   
   7:  @using (Html.BeginForm("Save", "DatenEingabe"))
   8:  {
   9:      @Html.AntiForgeryToken()
  10:      
  11:      <div class="form-horizontal">
  12:          <h4>DatenEingabeModel</h4>
  13:          <hr />
  14:          @Html.ValidationSummary(true, "", new { @class = "text-danger" })
  15:          <div class="form-group">
  16:              @Html.LabelFor(model => model.AusgewaehlterWert, 
htmlAttributes: new { @class = "control-label col-md-2" })
  17:              <div class="col-md-10">
  18:                  @Html.DropDownListFor(model => model.AusgewaehlterWert, Model.WerteListe, 
 new { htmlAttributes = new { @class = "form-control" } })

19: @Html.ValidationMessageFor(model => model.AusgewaehlterWert, "",

new { @class = "text-danger" })

  20:              </div>
  21:          </div>
  22:   
  23:          <div class="form-group">
  24:              <div class="col-md-offset-2 col-md-10">
  25:                  <input type="submit" value="Save" class="btn btn-default" />
  26:              </div>
  27:          </div>
  28:      </div>
  29:  }
  30:   
  31:  <div>
  32:      @Html.ActionLink("Back to List", "Index")
  33:  </div>
  34:   
  35:  @section Scripts {
  36:      @Scripts.Render("~/bundles/jqueryval")
  37:  }

So weit, so gut. Eigentlich würde man hierbei keine Probleme erwarten. Doch leider kommt es anders. Sobald der Benutzer den Save Button drückt wird noch die Save Action durchgeführt, und beim Redirect zur OK-Action passiert folgendes:

image

Die Fehlermeldung “No parameterless constructor defined for this object” deutet darauf hin, einen Konstruktor vergessen zu haben. Die parameterlosen Konstruktoren werden meist beim Deserialisieren benötigt. So ist es auch hier. Nur leider haben wir das nicht unter Kontrolle. Das Problem ist die Klasse “SelectList” die keinen parameterlosen Konstruktor hat.

Dieser einfache Weg führt also bald zu einem Problem.

Was ist falsch?

Es gibt extra eine Klasse “SelectList” und diese kann man nicht im Model verwenden? Das Problem ist, das Model enthält nur die Daten die der Benutzer eingibt. Jene Daten die für die Anzeige eines Views notwendig sind, dürfen nicht im Model sein, sondern wenn man es sauber trennen möchte gibt es hier für das ViewModel. Das ist eine neue Modelklasse die neben den Userdaten (Model) auch die Daten für die Oberfläche beinhalten kann. In unserem Fall sind das die Werte die in der Werte-Liste angezeigt werden sollen.

Korrekt ist es also ein ViewModel anzulegen, welches das Model beinhaltet und zusätzlich die Werteliste. Hier die korrigierte Model Klasse und dazu passend ein ViewModel:

   1:      public class DatenEingabeModel
   2:      {
   3:          public string AusgewaehlterWert { get; set; }
   4:      }
   5:   
   6:   
   7:      public class DatenEingabeViewModel
   8:      {
   9:          private DatenEingabeModel theModel = null;
  10:          public DatenEingabeModel TheModel
  11:          {
  12:              get { return theModel; }
  13:          }
  14:   
  15:          private SelectList werteListe = null;
  16:          public SelectList WerteListe
  17:          {
  18:              get { return werteListe; }
  19:          }
  20:          public DatenEingabeViewModel(DatenEingabeModel model)
  21:          {
  22:              theModel = model;
  23:              var tempListe = new List<SelectListItem>();
  24:              tempListe.Add(new SelectListItem() { Text = "Wert A", Value = "A" });
  25:              tempListe.Add(new SelectListItem() { Text = "Wert B", Value = "B" });
  26:              tempListe.Add(new SelectListItem() { Text = "Wert C", Value = "C" });
  27:   
  28:              werteListe = new SelectList(tempListe, "Value", "Text");
  29:          }
  30:      }

Im Konstruktor des ViewModels wird das eigentliche Datenmodell übergeben. Die Werteliste ist nun im ViewModel und das Model selbst enthält nur mehr Daten die der Benutzer erfasst und die später in eine Datenbank geschrieben werden.

Der Controller übergibt in der Index und der OK Action ein “DatenEingabeViewModel”. Nur im Save wird lediglich das Model selbst übergeben und im Redirect auch an die OK Action übergeben.

   1:      public class DatenEingabeController : Controller
   2:      {
   3:          // GET: DatenEingabe
   4:          public ActionResult Index()
   5:          {
   6:              DatenEingabeModel model = new DatenEingabeModel();
   7:              return View(new DatenEingabeViewModel(model));
   8:          }
   9:   
  10:          [HttpPost, ActionName("Save")]
  11:          public ActionResult Save(DatenEingabeModel model)
  12:          {
  13:              // Code zum Speichern der Daten
  14:              return RedirectToAction("OK", model);
  15:          }
  16:   
  17:          public ActionResult OK(DatenEingabeModel model)
  18:          {
  19:              return View(new DatenEingabeViewModel(model));
  20:          }
  21:      }

Der View muss noch angepasst werden. Als Model Klasse wird nun DatenEingabeViewModel verwendet. Daher müssen die angezeigten Properties noch angepasst werden. In meinem Fall verwende ich nun die Variable vm, denn diese zeigt mir besser an, dass ich nun mit dem ViewModel arbeite.

   1:  @model ViewModelDemo.Models.DatenEingabeViewModel
   2:  @{
   3:      ViewBag.Title = "Index";
   4:  }
   5:  <h2>Index</h2>
   6:   
   7:  @using (Html.BeginForm("Save", "DatenEingabe"))
   8:  {
   9:      @Html.AntiForgeryToken()
  10:      
  11:      <div class="form-horizontal">
  12:          <h4>DatenEingabeModel</h4>
  13:          <hr />
  14:          @Html.ValidationSummary(true, "", new { @class = "text-danger" })
  15:          <div class="form-group">
  16:              @Html.LabelFor(vm => vm.TheModel.AusgewaehlterWert, 
htmlAttributes: new { @class = "control-label col-md-2" })
  17:              <div class="col-md-10">
  18:                  @Html.DropDownListFor(vm => vm.TheModel.AusgewaehlterWert, Model.WerteListe, 
new { htmlAttributes = new { @class = "form-control" } })
  19:                  @Html.ValidationMessageFor(vm => vm.TheModel.AusgewaehlterWert, "", new { @class = "text-danger" })
  20:              </div>
  21:          </div>
  22:   
  23:          <div class="form-group">
  24:              <div class="col-md-offset-2 col-md-10">
  25:                  <input type="submit" value="Save" class="btn btn-default" />
  26:              </div>
  27:          </div>
  28:      </div>
  29:  }
  30:   
  31:  <div>
  32:      @Html.ActionLink("Back to List", "Index")
  33:  </div>
  34:   
  35:  @section Scripts {
  36:      @Scripts.Render("~/bundles/jqueryval")
  37:  }

Fazit

ASP.NET MVC ist einfach zu verwenden, allerdings gilt es sich an die Regeln zu halten. In ein Datenmodell gehören nur die Daten die gespeichert werden. Daten die für die Oberfläche notwendig sind, müssen in ein ViewModel ausgelagert werden. Ein gutes Indiz wann ein ViewModel verwendet werden soll ist, wenn im View etwas angezeigt werden soll, das nicht in eine Datenbank geschrieben wird.

Norbert Eder: Link-o-licious #7

Interessante Web-Ressourcen habe ich wieder gefunden. Links zu den Themen Web, Go und Produktivität finden sich hier. Ebenfalls etwas zum Nachdenken.

The post Link-o-licious #7 appeared first on Norbert Eder.

Klaus Aschenbrenner: Why do we need UPDATE Locks in SQL Server?

(Be sure to checkout the FREE SQLpassion Performance Tuning Training Plan – you get a weekly email packed with all the essential knowledge you need to know about performance tuning on SQL Server.)

Today I want to talk about a specific question that I almost get every time when I teach about Locking & Blocking in SQL Server: Why does SQL Server need to have Update Locks? Before we go down to the details of why they are needed, I first want to give you a basic overview of when an Update (U) Lock is acquired, and how the lock itself behaves regarding its compatibility.

In general an Update Lock is used in SQL Server when performing an UPDATE statement. When you look at the underlying query plan, you can see that such a plan always consists of 3 parts:

  • Reading data
  • Calculating new values
  • Writing data

Update Query Plan

When SQL Server initially reads the data to be changed in the first part of the query plan, Update Locks are acquired on the individual records. And finally these Update Locks are converted to Exclusive (X) Locks when the data is changed in the third part of the query plan. The question that arrises with this approach is always the same: why does SQL Server acquire Update Locks instead of Shared (S) Locks in the first phase? When you normally read data through a SELECT statement, a Shared Lock is also good enough. Why is there now a different approach with UPDATE query plans? Let’s have a more detailed look at it.

Deadlock Avoidance

First of all UPDATE Locks are needed to avoid deadlock situations in UPDATE query plans. Let’s try to imagine what happens when multiple UPDATE query plans acquire Shared Locks in the first phase of the plan, and afterwards convert these Shared Locks to Exclusive Locks when the data is finally changed in the third phase of the query plan:

  • The 1st query can’t convert the Shared Lock to an Exclusive Lock, because the 2nd query has already acquired a Shared Lock.
  • The 2nd query can’t convert the Shared Lock to an Exclusive Lock, because the 1st query has already acquired a Shared Lock.

That approach would lead to a traditional deadlock situation in a relational database:
Update Deadlock
That’s one of the main reasons why implementers of relational database engines have introduced Update Locks to avoid that specific deadlock situation. An Update Lock is only compatible with a Shared Lock, but isn’t compatible with another Update or Exclusive Lock. Therefore a deadlock situation can be avoided, because 2 UPDATE query plans can’t run concurrently at the same time. The 2nd query will just wait until the Update Lock can be acquired in the 1st phase of the query plan. An unpublished study of System R also showed that this kind of deadlock was the most prominent one. System R was initially implemented without any Update Locks.

Improved Concurrency

Instead of acquiring an Update Lock during the 1st phase, it would be also a viable option to acquire an Exclusive Lock directly in that phase. This will also overcome the deadlock problem, because an Exclusive Lock is not compatible with another Exclusive Lock. But the problem with that approach is limited concurrency, because in the mean time no other SELECT query can read the data that is currently exclusively locked. Therefore there is also the need for the Update Lock, because this specific lock is compatible with the traditional Shared Lock. As a result this means that other SELECT queries can read data, as long as individual Update Locks are not yet converted to Exclusive Locks. As a side-effect this will improve the concurrency of our parallel running queries. 

In traditional relational literature an Update Lock is a so-called Asymmetric Lock. In the context of the Update Lock that means that the Update Lock is compatible with the Shared Lock, but not vice-versa: the Shared Lock is not compatible with the Update Lock. But SQL Server doesn’t implement the Update Lock as an asymmetric one. The Update Lock is a symmetric one, which means that Update and Shared Locks are compatible in both directions. This will also improve the overall concurrency of the system, because it doesn’t introduce blocking situations between both lock types.

Summary

In todays blog posting I gave you an overview of Update Locks in SQL Server, and why they are needed. As you have seen there is a really strong need for Update Locks in a relational database, because otherwise it would yield to deadlock situations and decreased concurrency. I hope that you now have a better understanding of Update Locks, and how they are used in SQL Server.

Thanks for reading!

-Klaus

Karsten Kempe: Quick Tip: Copy Work Item

Ich bin in letzter Zeit ein paar Mal gefragt worden, ob es eine Möglichkeit gibt ein Work Item zu kopieren, in andere Projekte zu verschieben oder auch ob man einen Work Item Type umwandeln kann. So eine Umwandlung könnte  z.B. dann nützlich sein, wenn ich das Feature Backlog des TFS 2013 nutzen und dafür einige Product Backlog Items in Feature Items umwandeln möchte. Für einzelne Work Items gibt es eine sehr einfache und schnelle Möglichkeit zum Kopieren, Verschieben oder Umwandeln durch die Funktion Copy Work Item in Visual Studio.

Alles was man dafür tun muss, ist das entsprechende Work Item zu öffnen und in der Menüleiste Copy Work Item aufzurufen.

Alternative: In den Ergebnissen einer Work Item Query das Work Item auswählen und im Kontextmenü den Punkt Create Copy Work Item aufrufen.

Copy Work Item

Im Copy Work Item Dialog selbst hat man dann folgende Möglichkeiten:

  • das Team Projekt auszuwählen (falls die Kopie in ein andere Projekt erstellt werden soll)
  • den Work Item Typen der Kopie auzuwählen

Durch das Bestätigen der Einstellungen wird das Work Item kopiert! Auch beim Umwandeln des Typen bleibt das ursprüngliche Work Item erhalten.

Copy Work Item Type

In der Historie des Work Items wird nun auf das ursprüngliche Work Item verwiesen und in den Links befindet sich eine „Related“-Beziehung. Damit wird sichergestellt, dass erstmal keine Information verloren geht.

Ich habe in der Einleitung schon geschrieben, dass es für einzelne Work Items einen einfachen Weg gibt, leider aber nicht für einen ganzen Satz von Work Items oder gar für einen hierarchischen Work Item Tree. Für Visual Studio und TFS 2010 gibt es in der Visual Studio Gallery eine Extension Work Item Tree Copy, geschrieben von Daniel Meixner. Für Visual Studio 2013 ist mehr derzeit leider kein Tool bekannt.

Falls Ihr eine Extension kennt, die für 2013 diese Funktionalität bietet, dann seit doch so nett und hinterlasst hier einen Kommentar.

Jürgen Gutsch: NuGet as a Package Restorer? No, Thanks ;)

Wenn ich von NuGet spreche, tue ich das eigentlich mit großer Begeisterung. Generell finde ich es super, wie ich mit NuGet externe Bibliotheken, Werkzeuge und weitere Abhängigkeiten in mein aktuelles Projekt nehmen kann und – ganz wichtig – auch aktuell halten kann. Ich habe selber ein paar Bibliotheken die ich per NuGet verteile. Mit einem eigenen NuGet Server oder einem einfachen Netzwerk Share ist es möglich interne Bibliotheken innerhalb eines größeren Unternehmens zu verteilen.

Aber…

Jetzt kommt das einzige aber große Aber ;)

Das größte und einzige Problem an NuGet ist das automatische “Package Restore”, mit dem es möglich ist, NuGet Packages automatisch per Visual Studio oder direkt per NuGet wieder herstellen oder Updaten zu lassen.

Das übliche Scenario ist ein Build-Server, der vor dem Build alle Pakete lädt und oder aktualisiert. Dadurch ist es nicht mehr nötig Pakete in das Source-Code Repository einzuchecken, wodurch sich die Größe des Repositories erheblich reduzieren kann

Im ersten Moment scheint das ein nützliches Feature zu sein. Plattenplatz sparen UND Abhängigkeiten automatisch auflösen zu lassen. Klasse :)

Wo ist der Haken? Warum sehe ich genau das als Problem?

Wie oben angedeutet, sehe ich NuGet nur als Paket-Verteilungswerkzeug. Mehr nicht. Ich verteile Bibliotheken per NuGet oder lade benötigte Bibliotheken. Mehr nicht. Auch committe ich NuGet Packages in das Source Code Repository.

Warum?

Weil für mich der Grundsatz gilt, dass ich meine Sourcen überall und immer bauen können möchte, ohne von äußeren Einflüssen daran gehindert werden zu können.

Als vor einiger Zeit der offizielle NuGet-Server einige Stunden offline war, schrie die halbe .NET-Entwickler-Welt auf, weil unter anderem keine Packages gefunden werden konnten die wiederhergestellt werden konnten. Build-Server schlugen fehl, weil ohne die Abhängigkeiten nicht gebaut werden konnte.

Alternative NuGet Services, wie z. B. myGet, boten unnötigerweise ihre Diensten an. Ohne eine Lösung für das eigentliche Problem zu liefern.

Fazit

  • Es gibt Unternehmen, in denen ein Server nichts aus dem Internet laden darf.
  • Es gibt instabile Netzwerke, mit Unterbrechungen, etc.
  • NuGet Server die nicht erreichbar sind.
  • Fehlerhafte Packages.

Das dürfen aus meiner Sicht keine Gründe sein für einen Broken-Build. Wenn ein Build fehlschlägt, dann bitte nur wenn Menschen im Spiel sind: Wenn ein Entwickler fehlerhaften Code eincheckt oder ein automatisierter Test aufgrund einer fehlerhaften Implementation nicht durchläuft.

Was bringt mir eine Automatisierung, wenn diese ebenfalls fehlerhaft sein kann?

Aus diesem Grund ist es für mich ein MUSS, dass sowohl der Build selber, als auch alle automatisierten Tests unabhängig von äußeren Einflüssen (außer menschlichen) fehlerfrei durchlaufen kann.

Dirk Primbs [MS]: Predicting soccer with science

Before you read on let me admit something: I’m not a soccer person. Even though my country’s team just won the world cup I honestly couldn’t care less about it. So if you hope for some heartwarming enthusiastic account in this blog, stop reading now! On the other hand this explains why I kept trolling my surroundings whenever a soccer discussion was forced on me. I’ll admit I’ve been probably quite a smartass most of the time, but hey – that’s what you get if you poke me with the world cup stick :-)

My favourite (yet grossly over simplifying) argument goes like this:

1. Teams that make it to the semifinals all know basically how to play.
2. Superior tactics may give you a slight edge but it es definitely not more than 1/3.
3. Success = 1/3 Skills + 1/3 Tactics + 1/3 External factors (read: Luck)
4. Thus by assuming 1 and 2 it actually is mostly luck that advances the teams.
5. Luck on the other hand (or external factors) are ruled by statistics.
6. Hence it is more important to know about large numbers of matches than to know the last one.
7. Please ignore sports commentators and analysts! Everything they tell you is made up!

Well, you might guess that not everyone likes that model but I got support from Google and Microsoft this time. As it turns out both decided to use their cloud infrastructures to predict soccer results and they quite accurately predicted winners and losers. In fact both agreed that Germany will meet Argentina in the finals and that it had a chance of winning. (have a look here and here) The basis of their predictions was hard data. Numbers of ball contacts, past successes, goal accuracy weather conditions and so on… Or in the other model: crowd wisdom derived from social media.

There are two conclusions to be drawn from that:

1. Soccer is actually much less surprising than it feels to the audience.
2. Factors such as weather are at least as important as the actual gameplay.

And as if data science would not have been enough it is also interesting to look at the Psychology of the games. Daniel Kahnemann wrote an amazing book on cognitive biases and illusions with the title Thinking, Fast and Slow. According to him we’re bound to vastly overestimate how much we control our fate and our surroundings and underestimate the role of external factors. Hence everyone seem to be sure that Germany won through talent, skills and Löw’s superior strategic skills while even an unexpected change in weather could have killed any one of the matches…
Of course it didn’t. And that enables a second mechanism, called hindsight bias: Looking at past events it all seem to lead to the final victory, almost like a chain reaction. Since we fail to see all the things that did _not_ happen but could have influenced the outcome, we feel as if everything just makes sense. Of course this only works – as the name suggests – in hindsight :-)

Or as Neuer put it: “The next game will – again – start at 0:0.”
Of course, as human beings we’ll fail to realise that and assume that Germany starts with a bonus of superior skills and tactics… They are world champions after all. That got to count for something, right?

But as my sons would say: Who cares actually as long as we’ve won? Exactly! Well… maybe their smartass dad, but that is easy to manage :-)

Achtung, lieber Leser, sei gewarnt: Ich bin kein Fußball-Fan. Auch wenn mein Land gerade Weltmeister geworden ist, könnte mir das Spiel egaler nicht sein. Wer also einen enthusiastischen fröhlichen Fussball-Blogpost erwartet sollte jetzt besser aufhören zu lesen.

Allerdings erklärt dieses Geständnis auch warum ich in den letzten Wochen der unausweichlichen Weltmeisterschafts-Hysterie mit konstantem Gegengetrolle begegntete, zumindest immer dann wenn mir ein Fussballgespräch aufgedrängt wurde. Aber – he – das ist es halt was man sich einfängt wenn man mich mit der Weltmeisterschaft als Thema anbohrt…

Mein (stark übervereinfachendes) Lieblings-Argument geht in etwa so:

1. Von einem Team, das es bis ins Viertelfinale schafft, darf wohl angenommen werden, dass es grundsätzlich spielen kann.
2. Überragende Taktik mag eine Rolle spielen, aber mehr als ein 1/3 macht es sicher nicht aus.
3. Erfolg = 1/3 Können + 1/3 Taktik + 1/3 Externe Faktoren (übersetzt: Glück/Pech)
4. Ergo: Wenn 1 und 2 gilt, dann bleibt eigentlich überwiegend Element 3 (externe Faktoren) als definierendes Element für die Meisterschaft.
5. Diese Faktoren wiederum lassen sich ganz gut durch Statistik beschreiben.
6. Wenn das aber so ist, dann ist es wichtiger, große Datenmengen aus der Vergangenheit zu kennen als das Ergebnis des letzten Spieles zu analysieren.
7. Sportkommentare und Spielanalysen sind mehr oder weniger frei erfunden und dürfen ignoriert werden.

Tja… wie nicht anders zu erwarten, stimmt nicht jeder diesem Modell frei raus zu. Allerdings gab es dieses mal auch gleich noch Unterstützung von unerwarteter Seite: Google und Microsoft übertrafen sich nämlich gegenseitig in dem Versuch, per Datenanalyse Vorhersagen über den Spielverlauf zu treffen. Wie sich herausstellte, waren die Aussagen von Cortana und Google) erstaunlich treffsicher. Aus einer großen Menge harter Daten, etwa Wetter, Ballbesitz, eingesetzte Spieler, Torschüsse etc. wurden Vorhersagen generiert, die fast durchgehend richtig waren. Beide Unternehmen sagten vor allem auch korrekterweise voraus, dass Deutschland gegen Argentinien im Finale stehen wird und eine gute Chance auf den Titel haben wird.

Daraus ergeben sich zwei Rückschlüsse:

1. Fussball ist erstaunlich vorhersagbar. Auf jeden Fall vorhersagbarer als die Zuschauer vermuten.
2. Faktoren wie etwa das Wetter oder ähnliche sind mindestens so wichtig für den Spielverlauf wie die kontrollierbaren Faktoren Taktik und Können.

Aber nicht nur die Methoden moderner Datenanalyse sind spannend. Daniel Kahnemann, ein Psychologe und Nobelpreisträger schrieb ein großartiges Buch über kognitive Illusionen und Vorurteile namens Thinking, Fast and Slow. Demnach sind überschätzen wir Menschen grundsätzlich wie viel Kontrolle wir über unser eigenes Schicksal haben und unterschätzen die Rolle unkontrollierbarer Umstände. Das erklärt auch warum praktisch jeder überzeugt davon ist, dass Deutschlands Sieg rein auf das Konto von Löw’s überragender Spieltaktik und dem Können der Mannschaft zuzuschreiben ist. Dabei geht völlig unter, dass schon ein Wechsel des Wetters dramatische Ergebnisse hätte haben können.

Aber natürlich passierte das nicht. Im Ergebnis erliegen wir einer weiteren Illusion, dem sogenannten “Hindsight bias”. In Rückschau nämlich scheinen sich alle guten Entscheidungen einander zu bedingen, ganz wie eine Kettenreaktion.

Da wir ja nicht sehen was alles _nicht_ passiert ist, scheint es in der Rückschau völlig logisch, das alles so kam wie es nun mal war. Natürlich funktioniert das nur im Rückblick :-)

Oder wie Neuer es ausdrückte: “Das nächste Spiel wird auch wieder bei 0:0 anfangen.”
Natürlich sind wir Menschen nicht wirklich gut darin, das zu sehen. Daher werden wir beim nächsten mal ganz klar annehmen, dass Deutschland einen Vorteil bezüglich Fähigkeiten und Taktik haben wird. Sie sind schließlich Weltmeister. Für irgendwas muss das schließlich gut sein, richtig?

Oder wie meine Söhne sagen würden: Wen interessiert das alles so lange wir nur gewinnen? Hm… außer ihrem schlaumeiernden Vater, aber der ist leicht zu managen :-)

picture credit

The post Predicting soccer with science appeared first on Dirk Primbs Online.

ppedv Team Blog: Scroll zum Ende der Seite mit Angular.js

Angular arbeitet ähnlich wie Silverlight mit einem ViewModel. Eine Liste kann so an einem Service gebunden werden, ohne das das UI blockiert wird. Die Daten kommen eben asynchron.

Im Beispiel sind die Daten länger als der Anzeigeplatz im Browser und man möchte automatisch ans Ende der Liste scrollen. Hier ergeben sich mehrere Detailprobleme?

  • Wann ist die Liste geladen und gerendert?
  • Wie komme ich aus dem Viewmodel in den View?

Durchaus enttäuschend ist die Antwort auf Teil 1. Man weis nicht, wann die Liste geladen und gerendert ist. Es gibt kein rendered oder loaded Event. Man kann allerdings mit einer Direktive ein HtML Element erweitern. Von der Funktion erschient mir das ähnlich eines Expression Blend Behaviours. Platt gesprochen erstellt man damit neue HTML Attribute. Angular nennt die Funktion Directive. Quasi alles aus Angular setzt auf dieses System (zb ng-repeat)

Teil 2 dreht sich darum wie scrollt man aus dem Viewmodel ohne in den HTML View JavaScript Code schreiben zu müssen. Dafür bietet Angular eine Methode anchorScroll, die mit einer vorgelagerten Methode Scope das Element wählt und dann den Browser Scroll ausführt.

Die Direktive wird ähnlich wie der Controller im App Modul erzeugt und beim erreichen des letzten Elements ausgeführt.

   1:  angular.module('kundenApp', [])
   2:    .directive('myRepeatFinished', function ($location,$anchorScroll) {
   3:        return function (scope, element, attrs) {
   4:             if (scope.$last) {
   5:                 $location.hash('pageButton');
   6:                 $anchorScroll();
   7:   
   8:            }
   9:        };
  10:    })

Der Name der Direktive sollte nicht mit ng beginnen um Konflikte mit dem Angular Namensraum zu vermeiden. Per Konvention wird bei jedem Wechsel von Klein auf Großbuchstaben ein Bindestrich eingefügt und der gesamte Namen in Lowercase gewandelt.

Im HTML5 Teil wird dann dieses neue HTML Attribut eingefügt und so das DIV um Funktion, CSS oder ein HTML Template erweitert.

   1:   <div ng-repeat="kunde in kunden" my-repeat-finished>
   2:              {{kunde.CompanyName}}
   3:   </div>

 

Dieses Beispiel zeigt zur Laufzeit für einen Sekundenbruchteil das JavaScript Objekt an

image

Dann erst die eigentlichen Werte

image

Vermutlich ist die Methode auf den letzten generierten Eintrag zu setzen einen Tick zu früh.

ppedv Team Blog: ASP.NET Web Api 2 und Odata 4

Was die Headline schon vermuten lässt, ganz schön kompliziert, dennoch löst Odata eine Reihe von Problemen im REST Web Service Umfeld. Der Open Data Standard stammt aus der Feder von Microsoft und erlaubt es auf einen Service verschiedene Abfragen durchzuführen. Anders als im “klassischen” REST Ansatz sind damit eine Vielzahl Variabler Querys möglich. Um eine Entität per Rest abzufragen folgt man in der Regel url/servicemethode/id. Mit Odata sieht die gleiche Abfrage so aus url/servicemethode[id]. Im ersten Fall muss allerdings eine spezielle Route und eine Methodenüberladung mit einem Parameter angelegt werden.

Odata ist sehr hilfreich in Sorting, Quering und Paging Szenarien, insofern lohnt sich ein Blick darauf. Leider hat Microsoft die Odata Implementierng im ASP.NET Web API Framework mehrfach und grundlegend geändert. So findet man in den Suchmaschinen meist Code der nicht funktioniert. Auch von mir stammt ein derartiger Blogartikel. Weitestgehend korrekt, aber in manchen Details nicht mehr aktuell.

Basis eines Web Api Service ist der Service Controller, der die notwendigen CRUD Operationen bereit stellt. In VB.NET werden diese Methoden mit dem Präfixen Get usw in Kombination mit dem Controllernamen benannt. Dies ist die Konvention. Mit dem neuen Attribut Routing, kann man in der Namensgebung auch kreativer sein. Sollte man aber ohne Not nicht.

Ganz wesentlich: ein API Controller und ein Odata Controller sind zwei getrennte Welten, sprich Klassen. Die eine erbt von API Controller, die andere von OdataController.

Bevor man sich lange abmüht, empfehle ich per Scaffolding (Gerüstelement) den Odata Controller anzulegen.

image

Dabei wird eine vorhandene Modelklasse ausgewählt. In diesem Projekt wurde diese per Entity Framework aus der Northwind Datenbank generiert.

image

Falls dies ein jungfräuliches Web Projekt ist, wird keine Datenkontextklasse vorhanden sein. Lassen Sie sich bequem generieren indem sie auf das + clicken.

Die Controllerklasse enthält dann u.a. folgenden .NET Code

   1:   Public Class CustomersOController
   2:          Inherits ODataController
   3:   
   4:          Private db As New NorthwindEntities
   5:   
   6:          ' GET: odata/CustomersO
   7:          <EnableQuery>
   8:          Function GetCustomersO() As IQueryable(Of Customers)
   9:              Return db.Customers
  10:          End Function

 

Um den Controller per HTTP auch aufrufen zu können, muss eine Route konfiguriert werden. In den guten alten WCF Zeiten hätte man das in eine Config Datei geschrieben. Heute werden konfigurationen in Code getippt. Im globa.asax Startup Event wird eine Methode RegisterRoutes aufgerufen, die auf eine Klasse in RouteConfig.VB verweist.

Der Code ab Zeile 17 wurde von mir eingefügt. Wirklich intuitiv finden ich den Code nicht. Die Route lauter nun odata und es ist nur der Controller CustomersO eingebunden.

   1:  Public Module WebApiConfig
   2:      Public Sub Register(config As HttpConfiguration)
   3:          ' Web-API-Konfiguration und -Dienste
   4:          ' Web-API für die ausschließliche Verwendung von 
Trägertokenauthentifizierung konfigurieren.
   5:          config.SuppressDefaultHostAuthentication()
   6:          config.Filters.Add(New HostAuthenticationFilter
(OAuthDefaults.AuthenticationType))
   7:   
   8:          ' Web-API-Routen
   9:          config.MapHttpAttributeRoutes()
  10:   
  11:          config.Routes.MapHttpRoute(
  12:              name:="DefaultApi",
  13:              routeTemplate:="api/{controller}/{id}",
  14:              defaults:=New With {.id = RouteParameter.Optional}
  15:          )
  16:   
  17:          Dim builder As ODataModelBuilder = New ODataConventionModelBuilder()
  18:          builder.EntitySet(Of Customers)("CustomersO")
  19:          '       config.Routes.MapODataRoute("ODataRoute", Nothing, builder.GetEdmModel) Obsoloet
  20:   
  21:          config.Routes.MapODataServiceRoute("odata", "odata", builder.GetEdmModel)
  22:          config.EnsureInitialized()
  23:      End Sub

 

Es gibt nun einen Pfad api/ und einen Odata/. Wenn man die ersten 5 Kunden abrufen will, schränkt man dies wie folgt in der URL ein http://localhost:1929/odata/CustomersO?$top=5. Als Ergebnis erhält man JSON Daten

{
  "odata.metadata":"http://localhost:1929/odata/$metadata#CustomersO","value":[
    {
      "CustomerID":"ppedv","CompanyName":"ppedv","ContactName":null,"ContactTitle":null,"Address":null,"City":null,"
Region":null,"PostalCode":null,"Country":null,"Phone":null,"Fax":null,"LastChange":"AAAAAAAAz3M="
    },{

André Krämer: .NET Memory Leaks in 7 einfachen Schritten finden

Viele meiner Kunden überrascht es immer wieder wenn Ihnen auffällt, dass ihre Anwendung ein Memory Leak hat, da sie dem weit verbreiteten Irrtum unterliegen, dass es unter .NET dank des Garbage Collectors gar keine Memory Leaks mehr gibt. Auf die Überraschung folgt dann meist Ratlosigkeit, da man nicht so recht weiß, wie man die Ursache für das Speicherproblem denn nun finden soll.

Aus diesem Grund habe ich ein kurzes Video aufgezeichnet, in dem ich die sieben einfachen Schritte zeige, mit denen man ein Memory Leak in einer .NET Anwendung finden kann. Im Beispiel geht es zwar um eine Windows Forms Anwendung, die Vorgehensweise ist jedoch genauso auf eine Web Anwendung mit ASP.NET, einen Windows Service, eine WCF Anwendung oder eine WPF Anwendung übertragbar.

Viel Spaß beim Ansehen. Und natürlich freue ich mich über Kommentare Zwinkerndes Smiley

Sven Hubert: Ideen für die Verwendung des MSDN Azure Freikontingents: Bereitstellung von schnellen Downloads

Bei einer entsprechenden Konfiguration eines Blob Storages kann auf die abgelegten Dateien direkt über eine statische URL zugegriffen werden. Eine Bereitstellung von Downloads ist damit einfach möglich. Aufgrund der sehr guten Anbindung der Microsoft Rechenzentren und der selbstskalierenden Architektur der Speicherlösung sind diese auch bei einer großen Anzahl an parallelen Abrufen, wie sie häufig kurz nach Releases auftreten, extrem schnell. Die Geschwindigkeit ist in der Region des Rechenzentrums, in der der Blob abgelegt wurde, zumeist nur noch von der eigenen Anbindung des Abrufenden abhängig.

Wer Daten global performant bereitstellen will, setzt noch das Azure Content Delivery Network als Performance-Booster oben drauf. Durch eine Vielzahl von global verteilten Endpunkten ist auch in der entlegensten Region, auf Wunsch unter der eigenen Domain, ein schneller Abruf möglich.

Die Einrichtung gestaltet sich wie gewohnt sehr einfach.

Der Blob-Storage wird, wie gehabt, über das Windows Azure Management Portal erstellt.

Einrichtung des Blob Storage

Der Name muss global eindeutig vergeben werden. Als Standort wählt man das Rechenzentrum aus, in dessen Nähe sich voraussichtlich die meisten Interessierten befinden.

Einrichtung des Blob Storage

Um auf Dateien direkt über einen Link zugreifen zu können, wird ein Container mit dem Zugriffsschutz „Öffentlicher BLOB“ eingerichtet.

Einrichtung des Containers

Einrichtung des Containers

Dies ermöglicht den Zugriff auf Dateien über eine URL. Eine Auflistung des Container-Inhalts wird jedoch verhindert. Den Zugriffsschutz stellt damit einzig und allein der Name der Datei dar.

Zum Hochladen von Dateien bietet sich, wie bereits im Beitrag Datensicherung gezeigt, der Azure Storage Explorer an.

Hochladen der Datei

Nach dem Upload steht die Datei direkt zum Download zur Verfügung. Die URL setzt sich dabei aus der URL des Containers sowie dem Dateinamen zusammen.

Download URL

Im dargestellten Beispiel ergibt sich die URL http://aitdownload.blob.core.windows.net/dowload/AIT.SandcastleExtension.V1_20140712.zip.

Die Kosten für die Bereitstellung setzen sich durch den verbrauchten Speicher sowie den Datentransfer zusammen. Letzterer ist abhängig von der Anzahl der Abrufe.

Für eine 100 MB Datei ergibt sich folgende Kalkulation:

100 MB x 0,0179 € pro GB = 0,00179 €
100 MB x 1000 Downloads x 0,045 € pro GB = 0,45 €

Insgesamt ergeben sich damit Kosten von knapp 50 Cent pro Monat. Über das MSDN Azure Freikontingent können damit eine Vielzahl von Downloads performant bereitgestellt werden.

Als weiteren Zusatz für einen schnellen Dateiabruf weltweit bietet sich die Konfiguration des Azure Content Delivery Networks an. Dieses repliziert die bereitgestellten Daten an eine Vielzahl von Standorten weltweit. Anfragen werden automatisch an den Knoten gesendet der am schnellsten erreicht werden kann. Bei einer Anfrage aus Australien wird z.B. der Inhalt über Sydney oder Melbourne bereitgestellt. Die Download Geschwindigkeit ist damit unabhängig von der momentanen Auslastung der Anbindung des Kontinents.

Ein Endpunkt wird wie folgt angelegt.

Anlegen eines CDN-Endpunkts

Anlegen eines CDN-Endpunkts

Der erstellte Knoten zeigt auf den zuvor erstellten Blob Storage. Abgelegte Dateien sind nun sowohl unter der ursprünglichen URL als auch über die des Content Delivery Networks abrufbar. Im vorliegenden Beispiel ergibt sich die URL http://az645735.vo.msecnd.net/dowload/AIT.SandcastleExtension.V1_20140712.zip.

Auf den ersten Teil des Namens hat man keinen Einfluss. Um diesen etwas sprechender zu gestalten, ist es möglich eine eigene Domäne zu hinterlegen.

Hinterlegung einer Domain

Hierzu muss beim Domain Provider ein C-Name eingerichtet werden, der auf den CDN-Endpunkt zeigt.

Hinterlegung einer Domain

Die Konfiguration gestaltet sich bei den einzelnen Providern unterschiedlich. Die Screenshots zeigen das Vorgehen bei 1und1.

Als erstes wird die Subdomain angelegt.

Hinterlegung einer Domain

Im Anschluss wird die Subdomain als Alias für den Endpunkt konfiguriert.

Hinterlegung einer Domain

Die Einrichtung kann dabei eine gewisse Zeit dauern. Sobald der C-Name bereitsteht, kann dieser dem CDN-Endpunkt hinzugefügt werden. Die korrekte Konfiguration wird hierbei überprüft.

Hinterlegung einer Domain

Im Anschluss sind die Dateien auch über die hinterlegte Domäne (z. B. http://download.aitgmbh.de/dowload/AIT.SandcastleExtension.V1_20140712.zip) abrufbar.

Bei der Nutzung des CDN entstehen in etwa die doppelten Kosten im Vergleich zu einer einfachen Blob-Storage Lösung. Aufgrund der relativ niedrigen Gebühren für die Bandbreite fallen diese aber erst bei bei einer sehr großen Anzahl an Downloads oder entsprechend großen Dateien ins Gewicht.

Die gezeigte Lösung eignet sich insbesondere für die Bereitstellung von Releases. Durch die selbstskalierende Architektur und die global verteilten Endpunkte kann auch bei einem großem Ansturm an Downloadern jederzeit ein schneller Abruf gewährleistet werden. Bei Nutzung des MSDN Azure Freikontingents sogar zum Nulltarif.

Ideen für die Verwendung des MSDN Azure Freikontingents: Bereitstellung von schnellen Downloads is a post from: AIT Blog

Norbert Eder: JSON-Datei mit Go einlesen

Das JSON-Format wird ganz gerne verwendet, um Daten oder Einstellungen zu übertragen bzw. zu speichern. Daher stellt sich häufig die Anforderung, JSON-Dateien einzulesen. Dieser Kurzbeitrag zeigt den notwendigen Sourcecode zum Einlesen von JSON-Dateien und dem Mapping auf einen Go Typen.

The post JSON-Datei mit Go einlesen appeared first on Norbert Eder.

codefest.at [MS]: Freies eBook Building Cloud Apps with Microsoft Azure

Ganz frisch gibt es ein neues kostenfreies eBook für Developer im Microsoft Press Blog downzuladen: Building Cloud Apps with Microsoft Azure von Scott Guthrie, Mark Simms, Tom Dykstra, Rick Anderson und Mike Wasson.

Das eBook behandelt die Themen Apps mit Azure, Best practices for DevOps, data storage, high availability und vieles mehr. Dabei werden auch wichtige Bereiche von Azure wie Automation, Web Development, Continuous integration and delivery, Source Control, Caching, Queues, Monitoring und weitere Features behandelt.

9780735695658f

Hier kann das eBook in verschiedenen Formaten geladen werden:
PDF (6.35 MB), EPUB (22.7 MB), and MOBI (12.3 MB).

Der Inhalt basiert auf mehreren Sessions von Scott Guthrie auf der Norwegian Developers Conference (NDC) und von der Microsoft Tech Ed Australia im letzten Jahr. Die Beispiele wurden von weiteren Autoren angepasst, erweitert und in schriftliche Form gebracht.

Das eBook führt durch Design-Patterns und beschreibt Architektur und Code-Beispiele und zeigt wie man Real-World Cloud-Lösungen umsetzen kann. Die Zielgruppe sind vor allem Software-Entwickler, für die Cloud-Solutions neu sind und zeigt anhand von Konzepten und Beispielen wie konkrete Anforderungen in Azure entwickelt werden können.

Auch Developer, die bereits Cloud-Lösungen entwickeln finden hier vielleicht neue Ideen. Jedes Kapitel ist eigenständig und informiert über spezielle Bereiche.

Viel Spaß mit dem freien eBook!

Code-Inside Blog: NuGet Package Restore & Build Server wie z.B. AppVeyor

image_thumb.png

NuGet ist ja mittlerweile weit verbreitet, aber eine Frage stellt sich natürlich immer noch: Checkt man die NuGet Packages ein oder nicht?

In meinem kleinen Side-Projekt, welches auf GitHub liegt und ich über AppVeyor auch bauen lasse nutze ich das Package Restore Feature von NuGet, d.h. in meinem Repository befindet sich kein NuGet Package mehr, sondern es wird vor dem Bauen geprüft ob das Package da ist und wenn nicht wird es runtergeladen.

Die “eincheck”-Variante hat auch seine Vorteile, aber in meinem Setting hier ist dies unnötig, da ich ohnehin auf diverse andere Dienste vertraue. Also zur Sache:

“Enable NuGet Package Restore” & Fertig, oder?

Nein.

Diese Option die aktuell noch im NuGet Tooling drin ist braucht es nicht mehr:

image

Diese Option benötigt es nicht mehr! Mehr dazu in diesem Blogpost “The right way to restore NuGet packages”. Um es kurz zu machen: Im Visual Studio passiert dies “automatisch” – es benötigt nicht diese Magic.

Änderung seit NuGet 2.7

Diese Änderung wurde in NuGet 2.7 gemacht. In dem Blogpost ist auch beschrieben wie man seine Projekte wieder davon befreien kann.

Und was ist nun mit den Buildservern?

Auf dem Buildserver (AppVeyor in meinem Beispiel) kam immer diese Fehlermeldung:

“C:\projects\code-inside-web\CodeInside.Web\CodeInside.Web.csproj(338,5): error : The build restored NuGet packages. Build the project again to include these packages in the build. For more information, see http://go.microsoft.com/fwlink/?LinkID=317568.”

Der Link zeigt auch wieder auf den MSDN Blog.

Der Fix für das Problem auf dem Buildserver

Generell muss man aktuell noch manuell den “nuget restore” Command aufrufen. Im Falle von AppVeyor macht man dies einfach (Doku)so:

image

Damit sollte es wieder klappen.

image

Fabian Deitelhoff: Digitale Entschlackungskur

Chrome LogoHin und wieder überprüfe ich, ob ich bestimmte Anwendungen, Dienste oder Vorgehensweisen tatsächlich noch brauche. Das findet nicht zu bestimmten Anlässen geschweige denn zu festen Zeitpunkten statt. Es fängt vielmehr mit etwas Verwunderung an, ob etwas noch wirklich notwendig ist. Manchmal haben auch IT Nachrichten damit zu tun. Wenn mal wieder Sicherheitslücken ohne Ende vorhanden sind und das Komponenten betrifft, die vielleicht gar nicht so wichtig sind wie gedacht.

Dieses Mal bezog sich das auf Anwendungen, die ich einsetze, beziehungsweise auf Plugins ebendieser Programme. Da habe ich etliche Funktionen deaktiviert. Mal schauen, ob es größere Probleme gibt oder sich meine IT Welt doch einfach weiterdreht.

Der Anfang

Vor circa 1 1/2 Jahren habe ich das Java Plugin aus dem Browser geworfen. Ich finde externe Plugins, um Inhalte anzuzeigen, ziemlich nervig. Da das Java Plugin von kaum einer Webseite genutzt wird, war das auch überhaupt kein Problem. Ist mir glaube ich nur einmal aufgefallen, als ich Tickets für eine Veranstaltung buchen wollte und die Platzwahl über ein Java-Plugin geregelt wurde. Ich habe dann woanders bestellt. An Alternativen mangelt es hier definitiv nicht.

Einige Monate danach habe ich Java komplett vom System entfernt. Das war die Zeit der vielen Sicherheitslücken und ich merkte, dass ich Java eh nicht mehr viel einsetze. Zwar noch im Rahmen meines Studiums, dafür setze ich aber sowieso auf VMs, da ich für das Studium generell viel Software benötige, die dann teilweise nach einem Semester schon wieder obsolet ist. Auf meinem Desktop-System war das Fehlen von Java hingegen kein großes Problem. Ich setze keine Software ein, die Java benötigt.

Und es geht weiter

Wie ich in der Einleitung des Artikels schon geschrieben haben, ging es nun mit diversen Plugins weiter. Das hatte ich schon lange vor. Auslöser war der folgende Tweet, der den Weg in meine Timeline bei Twitter gefunden hat:

Moment setze ich Chrome ein und so war es an der Zeit, den Browser zu entschlacken. Deaktiviert habe ich alles, was nicht niet und nagelfest ist. Die folgende Liste enthält alle Plugins, die zurzeit installiert sind. Der obere Teil zeigt die aktivierten, der untere die deaktivieren Plugins.

  • Chrome PDF Viewer (aktiviert)
  • Google Update (aktiviert)
  • Adobe Flash Player
  • Chrome Remote Desktop Viewer
  • Widevine Content Decryption Module
  • Native Client
  • Microsoft Office
  • Adobe Reader
  • NVIDIA 3D
  • Java
  • Silverlight

Viel übrig geblieben ist ganz offensichtlich nicht mehr. Den Chrome PDF Viewer habe ich nicht deaktiviert, weil ich es manchmal ganz nett finde, eine PDF Datei direkt im Browser zu betrachten. Wirklich überlebenswichtig ist das auch nicht. Vielleicht fliegt das Plugin auch irgendwann raus. Abbildung 1 zeigt die Ansicht noch mal in Chrome, die über die Adresse chrome://plugins aufrufbar ist.

Abb. 1: Aktivierte und deaktivierte Plugins in Chrome.

Abb. 1: Aktivierte und deaktivierte Plugins in Chrome.

Gespannt bin ich auf den Flash Player und vielleicht auch auf Silverlight. Gerade ersteres Plugin ist leider noch häufig Standard bei Videos oder anderen, interaktiven Inhalten. Deaktiviert habe ich die Plugins seit circa zwei Wochen. Bisher habe ich 2-3 Mal den Flash Player vermisst, weil ich mir ein Video angucken wollte. Aktiviert habe ich den Player aber trotzdem nicht. Ich habe dann auf das Video verzichtet. Viel mir nicht sonderlich schwer, da der Inhalt im Nachhinein doch nicht wirklich wichtig war.

Fazit

Bisher kann ich damit sehr gut leben und vor allem Arbeiten. Viel habe ich nicht vermisst. Lediglich den Flash Player einige Male. Aktivieren möchte ich ihn trotzdem nicht mehr. Etlicher Inhalt ist auch schon auf HTML 5 umgestellt, was auch erst auffällt, wenn der Flash Player nicht mehr direkt automatisch startet. Und auf anderen Inhalt, der zwingend Flash voraussetzt, habe ich bisher dankend verzichtet.

Ob diese Vorgehensweise eine Möglichkeit zur digitalen Entschlackung ist, muss jeder für sich entscheiden. Ich finde es bisher sehr gut und es wird sicherlich nicht die letzte Aktion dieser Art gewesen sein.

Uli Armbruster: Aus der täglichen Retrospektive #2

Aufgrund von unterschiedlichen Browserversionen und Extensions oder wegen gecachten Inhalten kam es bei der Abnahme von User Stories bei uns häufig zu Problemen. Beispielsweise wurden alte Styles oder falsche Bilder geladen. Eine weitere Störquelle waren Fehler in Fremdsprachen oder in besonderen Daten-Konstellationen (sehr lange Breadcrumb, tiefe Hierarchien).

Daraus zogen wir zwei Konsequenzen:

  • Alle Tester und der PO bekamen die PortableApps.com Plattform, in welcher Firefox und Chrome mit einheitlichen Extensions und Konfigurationen installiert sind. Zwei Konfigurationen bewirken unter anderem, dass nichts gecacht wird und nach dem Schließen des Browsers alle Daten entfernt werden. Außerdem ist das NoTracking-Kennzeichen aktiviert. Per copy & paste kann ein weiterer Tester schnell “die Testumgebung” erhalten.
  • Darüber hinaus legten wir für besagte Konstellationen Lesezeichen in den Leisten an und dokumentierten den Soll-Stand. Im Unternehmenswiki ist der formale Abnahmeablauf festgehalten.

Einsortiert unter:CIO Topics, German, Projects Tagged: Retrospektive

ppedv Team Blog: Fernsteuerung mit Outlook: Windows per E-Mail herunterfahren

Die Ausgangslage kann mal auf jeden von uns Windows-Benutzern zutreffen, man will oder muss seinen PC ausschalten, vergisst dies jedoch, was dann? Ein „Herunterfahren“ via Fernsteuerung kann per E-Mail über Outlook geschehen.

Dies wird über eine zuvor erstellte Datei gesteuert.

So wird es gemacht:

1. Ausschalt-Datei erstellen

Windows-Taste betätigen, den Editor starten und folgenden Text eingeben:

shutdown –s –f

Diese Datei wird gespeichert, allerdings nicht als „.txt“ sondern als „.bat“, z.B. „ausschalten.bat“.

2. Eine Outlook-Regel erstellen

In Outlook klicken Sie unter Start auf Regeln -> Regeln und Benachrichtigungen verwalten ->
Neue Regel -> Regel ohne Vorlage erstellen: Regel auf von mir empfangene Nachrichten anwenden und Weiter.

clip_image002[1]

Um dafür zu sorgen, dass Outlook den PC ausschaltet, aktivieren Sie das Kontrollkästchen vor „die von einer Person/öffentlichen Gruppe kommt“ und „mit bestimmten Wörtern im Betreff“.

clip_image004[1]

Unter 2. Schritt klicken Sie auf den blau unterstrichenen Text „einer Person/öffentlichen Gruppe“, dort geben Sie Ihre E-Mail-Adresse ein und bestätigen mit OK. Danach klicken Sie auf „bestimmten Wörtern“, hier geben Sie z.B. ausschalten ein und klicken auf Hinzufügen, OK und Weiter.

Im nächsten Dialogfenster setzen Sie anschließend noch einen Haken neben
Anwendung starten“, klicken unten auf Anwendung und wählen die Datei aus Schritt 1 aus. Hierzu klicken Sie auf Ausführbare Dateien, Alle Dateien und doppelt auf ausschalten.

clip_image005[1]clip_image006[1]

Abschließend klicken Sie auf Fertigstellen und zweimal auf OK.

3. PC ausschalten

Nun testen Sie es, indem Sie sich selbst eine E-Mail schicken mit dem Betreff Ausschalten. Nach dem Abruf dieser E-Mail von Outlook fährt der PC herunter.

Hinweis: Vorsicht! Alle nicht gespeicherten Dateien gehen dabei verloren.

Bitte beachten:

Die Benutzerkontensteuerung (User Account Control, UAC) kann dazu beitragen, nicht autorisierte Änderungen am Computer zu verhindern, daher ist diese meist aktiviert und sollte nicht deaktiviert werden.

Um dies zu steuern, kann sie aktiviert, bzw. deaktiviert werden:

Öffnen Sie die Einstellungen zur Benutzerkontensteuerung, indem Sie auf die Schaltfläche Start, bzw. die Windows-Schaltfläche in W8 klicken. Geben Sie dann im Suchfeld den Text UAC ein, und klicken Sie anschließend auf Einstellungen der Benutzerkontensteuerung ändern. In Windows 8 tippen Sie einfach UAC ein nachdem die Windows-Taste gedrückt worden ist.

Nun werden die Einstellungen vorgenommen:

Bewegen Sie zum Aktivieren/Deaktivieren der Benutzerkontensteuerung den Schieberegler auf die Position Nie benachrichtigen, und klicken Sie anschließend auf OK. Zum Deaktivieren der Benutzerkontensteuerung muss der Computer neu gestartet werden.

Klaus Aschenbrenner: SQLBits recap

(Be sure to checkout the FREE SQLpassion Performance Tuning Training Plan – you get a weekly email packed with all the essential knowledge you need to know about performance tuning on SQL Server.)

Wow, what an intensive last week! Last week I attended the SQLBits conference in Telford/UK – in the middle of nowhere :-) . Telford itself is around 30 minutes in the west of Birmingham (where I flew into), but there is almost no civilization – or I didn’t have found it…It was the first conference in my life where I haven’t seen anything else besides the hotel and the conference venue. This makes life very simplified.

During the conference I have presented 2 sessions. On Friday I have talked about Latches, Spinlocks, and Latch-Free Data Structures, and on Saturday I have presented my session about the Dangerous Beauty of Bookmark Lookups. Both sessions where high attended, and I had a huge fun to present them.

I have also already submitted my session materials (slides & samples) to the conference owners, so you should be able very soon to get the materials through the conference website. Besides the amazing speaker lineup, we also had a lot of fun at the Steampunk party on Friday, which was THE highlight during the conference! I can’t tell you too much about the party itself, because what happens at SQLBits stays in SQLBits ;-) .

I got pink hairs!

Thanks for reading

-Klaus

codefest.at [MS]: Cloud Service Projekte mit Visual Studio Online Teambuild

imageWer seine Cloud Service Projekte in Visual Studio Online verwaltet, hat die Möglichkeit diese mittels continuous integration automatisch bei Check-Ins bereitzustellen. Dazu ist es lediglich notwendig das Cloudservice mit dem Visual Studio Online Projekt zu verbinden.

Dazu wählt man auf der Startseite des Cloud Services im Management Portal den Link “Set up publishing with Visual Studio Online”.

image

Nachdem man die entsprechenden Berechtigungen vergeben hat kann man ein Sourcecode Repository (GIT oder imageTFSVC) wählen.

Im Normalfall (Eine Solution mit einem Cloud Projekt) läuft alles reibungslos. Allerdings gibt es auch Szenarien die etwas Konfigurationsarbeit erfordern:

Spezial Szenario 1 – Mehrere Solutions mit Cloudprojekten

Befinden sich in dem Repository mehrere Solutions mit Cloudprojekten legt Visual Studio Online trotzdem nur eine Build Definition an. Als Name wird hier scheinbar die erste gefundene Solution ausgewählt. Wer die Build-Definition allerdings öffnet und unter “Process-2.Build-Projects” hineinsieht findet kein ausgewähltes Projekt. Normalerweise können in einer Build-Definition Solutions und Projekte angegeben werden. Der Cloud Deployment Build erfordert hier allerdings immer die Bekanntgabe einer Solution! Ansonsten kommt es beim Build zu einer entsprechenden Fehlermeldung. D.h. in diesem Fall muss die gewünschte Solution ausgewählt werden.

Schwieriger gestaltet sich nachfolgendes Szenario:

Spezial Szenario 2 – Mehrere Cloud Projekte in einer Solution

In diesem Szenario möchte ich ein spezifisches Cloud Projekt auf mein Cloud Services automatisiert bereitstellen. Per Default wird wieder eine Build-Definition von Visual Studio Online angelegt die auf meine Solution verweist. Der Cloud Build/Deployment Prozess verwendet hier allerdings immer das erste gefundene Cloud Projekt für die Bereitstellung. Es gibt leider keine Möglichkeit anzugeben, welches der Cloudprojekte für die Bereitstellung ausgewählt werden soll (siehe entsprechenden Forumseintrag).

Update: Einer unserer Partner hat mich auf folgenden Artikel aufmerksam gemacht, mit dem der Build Prozess so verändert wird, dass das bereitzustellende Projekt auch explizit angegeben werden kann.

Ein möglicher Workaround ist die Erstellung einer Kopie der Solution-Datei die man auch entsprechend zusätzlich eincheckt. Aus dieser entfernt man bis auf das gewünsche Cloud Projekt alle anderen. Daraufhin gibt man diese Solution im Build-Prozess der Build-Definition an.

Auf diese Weise kann man natürlich auch mit den anderen Cloud Projekten verfahren und so die Cloud Projekte automatisiert auf unterschiedliche Cloud Services bereitstelen.

Arbeiten kann man problemlos mit der ursprünglichen Solution.

Andreas Pollak
Product Marketing Manager Visual Studio (Entwicklungs- und Testwerkzeuge)
clip_image001

Leader im Magic Quadrant für ALM

Johnny Graber: Von Zeit und Datenmengen

Bei Datenmigrationen und der Batchverarbeitung kommen 2 Bereiche zusammen bei denen viele Entwickler (mich eingeschlossen) schnell an eine mentale Grenze stossen:

  • Zeit
  • Datenmenge

Wohl ist jedem bekannt das ein Tag 24 Stunden hat und ein Terabyte aus 1024 Gigabyte besteht. Und doch kommt es immer wieder zu bösen Überraschungen.

 

Eine Optimierung am Code ist reine Zeitverschwendung, da die Arbeit in einer Sekunde erledigt ist.

 

Wer hat nicht schon solche Aussagen gehört? Das Problem von dieser einen Sekunde ist nicht die Dauer. Das Problem liegt in der impliziten Aussage über die Geschwindigkeit. Eine Sekunde ist kurz, meine Arbeit gross und daraus folgt mein Code ist schnell. Wenn es so doch nur so einfach wäre…

 

Wie lange kann es auf der Produktion schon gehen wenn es auf dem Testsystem gar nicht messbar ist?

 

Ist die wohl noch schönere Frage. Wer schon einmal versuchte eine grössere Datenmenge zu migrieren kennt die unbequeme Antwort: Lange – viel zu lange.

 

Was genau messen wir?

Wie immer wenn etwas gemessen wird kommt es auf die Relevanz an. Ob 1 Sekunde schnell oder langsam ist hängt davon ab, was darin gemacht wurde. Wie viele Datensätze wurden verarbeitet? Und in welchem Verhältnis steht diese Datenmenge zum Zielsystem?

Die einzelnen Sekunden summieren sich sehr schnell, alles was es dazu braucht ist eine unterschiedliche Menge an Daten auf dem Test- und auf dem Produktionssystem. Bei einem linearen Anstieg wächst die Verarbeitungsdauer gleichmässig mit der Datenmenge. Verdoppelt sich die Datenmenge, verdoppelt sich auch die Verarbeitungsdauer. Hat man 60x mehr Daten in der Produktion als auf dem Testsystem wird aus dieser einen Sekunde bei der Einführung eine Minute.

 

Eine Hochrechnungstabelle

Was passiert aber wenn man statt auf dem Testsystem auf dem Entwicklungssystem gemessen hat? Da ist das Verhältnis vielleicht nicht 1:60, sondern 1:1’000, 1:10’000 oder gar 1:100’000. Wie lange waren nochmal 100’000 Sekunden?

Diese kleine Hochrechnungstabelle nimmt das Rechenbeispiel auf und geht von einer Sekunde für eine bestimmte Menge von Daten aus. 10x mehr Daten ergeben den Faktor 10, der für diesen linearen Anstieg auch in einer 10x längeren Dauer resultiert:

Faktor Datenmenge Dauer
1 00:00:01
10 00:00:10
100 00:01:40
1’000 00:16:40
10’000 02:46:40
100’000 27:46:40 oder 1 Tag 03:46:40
1’000’000 277:46:40 oder 11 Tage 13:46:40

Die 100’000x mehr Daten machen aus der getesteten Sekunde bei der Einführung eine Aufgabe die sich in einem Tag nicht mehr abschliessen lässt. Aber schon bei 10’000x mehr Daten dürfte so mancher Einführungsplan in den kritischen Bereich kommen.

 

Probleme vermeiden

Diese Probleme lassen sich am einfachsten dadurch vermeiden, dass man die Performance-Messungen auf vergleichbaren Systemen mit vergleichbaren Datenmengen macht.

Ist es nicht möglich muss man die Werte entsprechend umrechnen. Dies ist viel aufwändiger und weniger genau, aber immer noch besser als sich einzureden die Arbeit sei in einer Sekunde erledigt. Dabei gilt es aber nicht nur die Datenmenge zu bedenken. SSD-Festplatten sind gerade in Kombination mit kleinen Datenmengen deutlich schneller als so manche Server-Festplatte. Von CPU, Netzwerk und Systemdiensten die auch noch laufen ganz zu schweigen.

 

Fazit

Wenn man eine Performance-Messung nur auf die Dauer reduziert führt dies zu völlig falschen Annahmen. Um lange Wartezeiten bei der produktiven Einführung zu verhindern muss daher unbedingt beachtet werden wie sich die Daten und Systeme gegenüber der Produktion unterschieden. Mehr Daten bedeutet eine längere Verarbeitungszeit. Und nur weil es auf irgendeinem System schnell ist bedeutet dies nicht automatisch das man aufs optimieren verzichten kann. Ganz abgesehen davon dass eine exponentielle Zunahme der Verarbeitungszeit alles noch viel schlimmer macht…

 


Einsortiert unter:.Net, dnugBern, webDotNet Tagged: .Net, Beschleuniger

Uli Armbruster: Checkliste für den Jobwechsel

Auf karrierebibel bin ich auf einen tollen Artikel gestoßen. Von dort stammt folgende Checkliste, ob man im aktuellen Job richtig aufgehoben ist:

[ ] Ich werde geistig gefordert.
[ ] Mir wird Autonomie bei der Arbeit zugestanden.
[ ] Ich mache wirklich das, was ich gut kann.
[ ] Meine Arbeit hat für mich und meine Werte Sinn.
[ ] Ich werde auch angemessen bezahlt.
[ ] Meine Work-Life-Balance ist ausgewogen.
[ ] Ich würde mit meinen Kollegen auch ein Feierabendbierchen trinken.

 

“Machen Sie sich keine Illusionen: Den einen Traumjob gibt es nicht. Jeder Beruf hat seine Ecken und Kanten, Sonnen- wie Schattenseiten. Wenn Sie aber rund die Hälfte der Fragen abhaken konnten, haben Sie einen Beruf gefunden, in dem Sie langfristig glücklich werden können.”

Wer mit Wechsel-Gedanken spielt, dem lege ich unser aktuelles Stellenangebot als Software Engineer ans Herz. Mehr Infos gibt es hier.


Einsortiert unter:German, Misc Tagged: Stellenausschreibung

Ralf Westphal: Zeitmanagement für Softwareentwickler

Warum kommt Clean Code oft gar nicht oder nur mit Mühe im Tagesgeschäft von Entwicklerteams an? Warum fühlen sich Entwickler oft überlastet? Abgesehen von fachlichen Problemen liegt das, so scheint mir, häufig an einer gut gemeinten, aber unsystematischen Arbeitsweise. Selbst wo man schon an Agilität geschnuppert hat, ist das persönliche Zeitmanagement weithin unterentwickelt. Deshalb hatte ich

Uli Armbruster: Aus der täglichen Retrospektive #1

Die optimale Lösung ist, wenn das komplette Scrum Team in einem Raum sitzen kann. Leider sitzen unser Kollege Freddi aus dem Marketing und unser Webentwickler Martin, beide Teil des Teams, in unterschiedlichen Gebäuden. Das steht natürlich im Impediment Backlog, lässt sich aktuell aber nicht lösen. Kürzlich kam es deshalb zu E-Mail/Telefon Ping Pong und einem enormen Zeitaufwand einer vermeintlich einfachen User Story. Weil die finale visuelle Lösung noch nicht fest in der User Story definiert werden konnte, mussten verschiedene Oberflächen-Elemente ausprobiert werden. Deshalb erläuterte Freddi Martin einen Ansatz, Martin setzte ihn um, rollte ihn auf die Preview Stage aus und Freddie schaute sich die Lösung an. Danach begann das Spielchen erneut.

Als Konsequenz zogen wir zwei Schlüsse:

  • Bei solchen User Stories setzen sich Freddi und Martin zusammen an den PC und bearbeiten die Oberfläche zusammen
  • Martin zeigt bei diesem “Pair Programming” Freddi, wie er selbst Kleinigkeiten in der UI mit einer entsprechenden Browser Extension ändern kann. Zum Beispiel Rahmendicke, Hintergrundfarbe, Schriftgröße, etc..

Einsortiert unter:CIO Topics, German Tagged: Retrospektive

codefest.at [MS]: Feststellen welche .NET Framework Version installiert ist

Manchmal entwickelt man eine App, äh, Code, auf Basis des .NET Frameworks um dann festzustellen, dass auf der Zielmaschine das entsprechende Microsoft .NET Framework nicht installiert ist. So ist es mir vor kurzem passiert, dass die Voraussetzungen für eine .NET 4.5 App am Zielrechner des Kunden nicht vorhanden waren. Dann hilft nur die App mit einer kleineren Version neu zu kompilieren oder das benötigte .NET Framework auf dem Zielrechner zu installieren.

Auch IT-Admins wissen nicht immer über den Status der Version des .NET Frameworks und über Side-by-Side Installationen und die .NET CLR 1, 2 und 4 Familien Bescheid.

Ich habe darüber auch schon vor einiger Zeit in Klarheit in .NET Versionen gebloggt, die Grafik von Scott Hanselman ist noch immer aktuell.

do-net-framework-versions

Glücklicherweise kann die installierte Version recht leicht herausgefunden werden…

Natürlich hilft zuerst mal ein Blick in C:\Windows\Microsoft.NET\Framework.

image

…bzw. für 64bit .NET Libraries in C:\Windows\Microsoft.NET\Framework64.

image

Etwas mehr Infos zu installierten .NET Frameworks findet man im MSDN:

Auch regedit aus der CommandLine kann helfen: Im Registry Editor wird in den Registry Key "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\NET Framework Setup\NDP" navigiert. Dort kann der Wert für das entsprechende .NET Framework nachgeschlagen werden.

image

Es wäre jetzt zu einfach, wenn die Werte den Versionsnummern entsprechen würden… das DWORD gibt an, welche  Version des .NET Frameworks installiert ist. Die Werte sind von hier. Gut ist, dass man auch per Code prüfen kann – nur der eigene Code muss halt am Zielsystem laufen…

Value of the Release DWORD
378389 - .NET Framework 4.5
378675 - .NET Framework 4.5.1 installed with Windows 8.1
378758 - .NET Framework 4.5.1 installed on Windows 8, Windows 7 SP1, or Windows Vista SP2
379893 - .NET Framework 4.5.2

Für .NET Framework kleiner 4 siehe hier.

Um die CLR Version zu prüfen öffnet man das Visual Studio Command Prompt und startet das CLR Version Tool clrver.

Übrigens: Die folgende Website informiert über die Vorteile des .NET Frameworks: Benefits of the Microsoft .NET Framework.

Happy checking. Zwinkerndes Smiley

Uli Armbruster: Fazit zum Open Space Süd 2014 – Mehr Nerdy Talk

Für mich unterscheidet sich der diesjährige #nossued klar von denen in den vorherigen Jahren. Es waren deutlich weniger Sessions und – rein subjektive Schätzung –  ca. 20-30% weniger Teilnehmer. Klassische Sessions wie Scrum und Deployment waren zwar vertreten, jedoch fehlten mit DDD, CQRS und Message Bus auch die ganz Großen. Da ich die Klassiker in den letzten Jahren aber ohnehin oft genug diskutiert habe, war das eher erfrischend. Besonders gelungen fand ich die “Pizza Service”-Session, die spielerisch die Materie vermittelte: Mit zwei konkurrierenden Pizza Services wurde Kanban trainiert. Tolle Sache, die ich in unserem Betrieb sicherlich ausprobieren werde.

Ausgesprochen positiv nahm ich die Zunahme der Gespräche abseits der Sessions wahr. Besonders die für mich aktuell größte Herausforderung, das Finden neuer Mitarbeiter, konnte ich mehrfach erörtern. Ich habe den Eindruck, dass speziell Firmen abseits der Großstädte Probleme haben, ohne dass es jedoch anderswo wesentlich besser wäre. In dem Kontext fand ich im Gespräch mit Vertretern von medialesson Gleichgesinnte, die ebenfalls Schüler und Studenten mit Workshops und Camps ansprechen wollen. Hierzu könnt ihr sicherlich bald mehr hören. Mit Tom Wendel konnte ich noch jemanden finden, der mit AntMe vielleicht bald einen Workshop zu der Idee beisteuert.

Zu guter Letzt will ich nächstes Jahr wieder Videos aufnehmen. Dazu wurde ich mehrfach angesprochen. Besondere Wünsche und Vorschläge könnt ihr mir gerne zuschicken.

Bis nächstes Jahr zum Open Space Süd. Noch der Hinweis auf den Developer Open Space Leipzig: Dieser findet vom 17-19 Oktober statt.


Einsortiert unter:Events, German Tagged: Community, Open Space

Uli Armbruster: Zweiter Entwicklertag für Schüler

Nach dem ersten Entwicklertag plane ich Anfang der Sommerferien (Termin folgt in den nächsten Tagen, vermutlich der 31.7.) den zweiten Entwicklertag. Das Ziel ist es den Schülern ein klares Berufsbild zu vermitteln, sodass sie nach diesem Tag klar sagen können: “Ja, der Beruf des Software Entwicklers ist etwas für mich” oder “Nein, das kann ich mir für mich nicht vorstellen”.

Ein Entwickler schreibt nicht nur Programme. Im Gegenteil: Das Gros der Arbeit besteht in der Problemanalyse, dem Entwickeln von Lösungsansätzen, dem Kommunizieren mit der Fachabteilung, dem Planen der Architektur, dem Anschauen von Technologien, uvm..

Dazu möchte ich ein vermeintlich einfaches “Projekt” nehmen und von Beginn an starten. Das könnte z.B. das Spiel 2048 sein. Alternativ ist ein Programm zum Abbilden eines betriebswissenschaftlicher Prozesses wie ein Bestellvorschlagswesen denkbar. Ich denke, dass alleine das Finden eines gemeinsamen Vokabulars und von doppeldeutigen Begriffen eine interessante Erfahrung sein wird.

Die heco wird die Räumlichkeiten wieder zur Verfügung stellen. Sowohl interessierte Schüler, als auch IT-ler, die helfen möchten, können mich über einen meiner Kanäle oder per Telefon (07232-3655-555) erreichen.


Einsortiert unter:German, Misc Tagged: Community

Uli Armbruster: Komm in unser Team

Wir suchen für unsere IT Abteilung nach neuen Software-Entwicklern. Zur Stellenausschreibung geht es hier.

Ich bin seit knapp 9 Jahren dabei und kann euch die Firma heco und die vakante Stelle wärmstens empfehlen. Wir haben ein tolles, kameradschaftliches Team. Die Aufgabengebiete sind wahnsinnig interessant und herausfordernd, sodass du am Ende des Tages auch das Gefühlt hast etwas bewegt zu haben. Die Eigenverantwortung und Entscheidungskompetenzen sind enorm hoch, was die Möglichkeit bietet alle Arten von Veränderungen anzustoßen und umzusetzen.

Wer mich aus der Community kennt, hat sicherlich schon die ein oder andere Interna gehört. Alle anderen können mich gerne direkt ansprechen und sich auf Kununu die Mitarbeiterbewertungen anschauen. Damit wurden wir zum besten Arbeitgeber 2013 im Bereich Handel/Gewerbe.


Einsortiert unter:German, Misc Tagged: Stellenausschreibung

ppedv Team Blog: WDS und SCCM oder 2x WDS parallel betreiben / Probleme mit PXE lösen

Wenn man (z.B. während der Einführungsphase vom System Center Configuration Manager) den bisherigen WDS-Server (Windows Bereitstellungsdienste / Deployment Services) weiterhin nutzen will, aber parallel die Betriebssystembereitstellung (OSD) von SCCM benötigt, dann besteht im Wesentlichen das folgende Problem:

Da PXE auf Broadcasts basiert, kann es nur einen PXE-Server geben, den der Client letztlich kontaktiert (man kann per Verzögerung dafür sorgen, dass einer immer schneller ist als der andere). Wenn man nun also PXE am SCCM aktiviert, dann ist es quasi Glückssache, ob der Client zuerst die Meldung vom WDS oder zuerst die von SCCM empfängt – in den meisten Tests war SCCM schneller. Damit bleibt also nur eine der beiden Technologien nutzbar.

Aber es gibt eine Lösung! Diese ist leider a) nicht wirklich dokumentiert und b) seitens Microsoft auch nicht unterstützt (man hört aber, dass selbst Microsoft diese Lösung intern einsetzen soll).

Die Lösung besteht darin, dem Benutzer am Client die Wahl zu lassen, welchen der gefundenen PXE-Server er nutzen will. Um dies zu erreichen, ist am WDS-Server (also derjenige, der nicht der SCCM-Server ist) ein Registry-Key zu setzen:

pxe1

Zusätzlich muss am SCCM in den Eigenschaften des Distribution-Points (Verteilungspunkt) für eine ausreichende Verzögerung gesorgt werden (würde man zuerst den PXE vom SCCM booten, dann hat der RegKey dort keine Wirkung, da dieser nur auf den WDS-eigenen PXE-Provider wirkt, nicht aber auf den vom SCCM):

pxe5

Wenn nun ein Client einen PXE-Boot versucht (und die Verzögerung ausreichend war, dass sich zuerst der Nur-WDS-PXE-Server meldet), dann bekommt der Benutzer zusätzlich zu der Möglichkeit, per F12 vom Netzwerk zu booten, eine weitere Option: F11 für eine Server-Auswahl!

pxe2

Drückt man jetzt F12, wird wie gewohnt DIESER WDS-Server genutzt und von dort mittels PXE gebootet. Drückt man jedoch F11, werden zuerst alle verfügbaren WDS-Server erkannt:

pxe3

Danach bekommt man eine Auswahl-Liste mit allen gefundenen PXE-Servern:

pxe4

Hier kann nun der jeweilige PXE-Server gewählt werden. Der WDS-Server selber steht an erster Stelle, an zweiter Stelle steht hier der SCCM mit aktiviertem PXE.

Auf diese Weise ist es möglich, WDS und SCCM oder mehrere WDS-Server parallel zu betreiben. Natürlich muss die entsprechende DHCP-Infrastruktur aufgebaut sein, damit PXE überhaupt funktionieren kann!

Sven Hubert: Rückblick: DWX 2014 in Nürnberg

Die Woche begann weltmeisterlich. Wir haben alle das Spiel um Weltmeisterschaft mit Spannung verfolgt, ebenso wie die Developer Week 2014 (DWX) in Nürnberg. Erfahren Sie mehr über unsere Highlights und laden Sie sich die Vortragsfolien herunter.

113 Minuten … so lange haben wir alle auf das erlösende wie auch entscheidende Tor im Finalspiel bei der Weltmeisterschaft in Brasilien gewartet. Götzseidank musste in den interessanten wie auch unterhaltsamen Sessions der DWX nicht so lange auf die ersten Erkenntnisse gewartet werden. Die DWX 2014 fand vom 14. bis 17. Juli 2014 auf dem Gelände der NürnbergMesse statt.

image

In den ersten drei Tagen fanden bis zu zehn Session parallel statt. Da die meisten Beiträge (blaue und rote Tracks im Programm) aufgezeichnet wurden, können nicht besuchte Sessions im Nachhinein angesehen werden. Die Aufnahmen werden bis Mitte August allen Konferenzteilnehmern kostenlos auf der Webseite der DWX zum Download bereitgestellt. Am vierten Tag konnten die Besucher in neun Workshops Praxiserfahrung mit unterschiedlichen Technologien und Tools sammeln. Ein Großteil der Referenten wurde den Besuchern am Montagmorgen vorgestellt (u. a. Thomas Rümmler 3. v. r.; Christian Schlag 4. v. r., Nico Orschel 6. v. r.).

DWX-Referenten 2014

Auch wir waren mit fünf Beiträgen im .NET-Umfeld vertreten. Begonnen hat am Montagnachmittag mein Kollege Thomas Rümmler gemeinsam mit  mir. In unserer Session haben wir den Zuhörern den aktuellen Stand der Integration von Release Management for Visual Studio (ehemals InRelease) in die Team Foundation Server-Landschaft (TFS) vorgestellt und eine Beispiel-Deployment demonstriert.

image

image

image

Zur gleichen Zeit hat Nico Orschel in seiner Session einen Überblick zur Planung, Strukturierung und Ausführung von Tests mittels Microsoft Test Manager (MTM) den Teilnehmern näher gebracht. In einem weiteren Beitrag hat er die Herausforderungen und mögliche Lösungen bei automatischen UI-Tests mittels CodedUI in Visual Studio dargestellt.

image

image

Den Dienstag eröffnete Benjamin Boost mit dem notwendigen Spagat zwischen einer Individuallösung und einem Standardprodukt, um eine flexibel konfigurierbare und erweiterbare Standardsoftware zu entwickeln. Zu regelrechtem Sitzplatzmangel führte Benjamins zweiter Beitrag. Seine aufgestellte These: “Code Reviews – Jeder kennts, keiner machts?” schien insofern nicht zu weit hergeholt und deckt sich mit unserer täglichen Erfahrung.

image

image

Insgesamt waren alle Session (nicht nur unsere) sehr gut besucht.

image

In den Pausen konnten die Besucher Informationen an verschiedenen Ständen einholen. Unseren Publikumsmagneten, den heißen Draht, hatten wir auch wieder dabei.

image

image

Zu gewinnen gab es bei uns einen Microsoft Surface 2. Wir wünschen dem Gewinner Philipp Gall von der Firma Baerlin EDV in Ludwigsburg viel Spaß damit.

image

image

image

Auch wir konnten uns an einem spontanem, AIT-internen Kickerspiel erfreuen. Leider konnten wir den Kicker nicht mitnehmen.

image

Abschließend danken wir allen Zuschauern und Besuchern der Konferenz, den Veranstaltern der Neuen Mediengesellschaft Ulm mbH, dem delikaten Catering, den fleißigen und stets lächelnden Servicekräften, sowie allen Helfern der NürnbergMesse, dem unermüdlichen Shuttlebus-Service und allen anderen fleißigen Helfern. Es war eine tolle Woche! Wir freuen uns auf die nächste DWX.

Abschließend die Folien zu unseren Vorträgen:

Für Rückfragen zu unseren Beiträgen stehen wir gern zur Verfügung.

Rückblick: DWX 2014 in Nürnberg is a post from: AIT Blog

Dirk Primbs [MS]: Running a multilingual blog…

… just doesn’t work for me.
As much as I hate to admit it: This blog would have seen already twice as many posts if not for the crazy idea to write in English as well as in German. I just don’t. Sometimes I even draft a post in one of the two languages but never find the time to translate it into the other.

So I had a long hard look at my statistics, thought about the people I try to reach and reminded myself that I also like to practice my English writing skills. The result: My posts will be from now on consistently in English. Unless an uproar in my comments suggests otherwise in which case I’ll consider to provide translated versions with delay… :-)

Picture credit: Flickr

The post Running a multilingual blog… appeared first on Dirk Primbs Online.

David Tielke: DWX 2014: Projektdateien & Folien

UnbenanntVom 14.07. bis 16.07.  findet (bzw. fand) in Nürnberg wieder die Developer Weeks in Nürnberg statt. Neben den Themen Visual Studio 2013 und Visual Studio Online durfte ich außerdem die Themen “Softwarekomponenten”, “Inversion of Control” und “Softwarearchitekturen für die Praxis” beleuchten. Neben den Folien waren aber vor allem für sehr viele Teilnehmer die erstellen Projekte von Interesse. Hier nun also die jeweilig erstellten VS-Projekte für die einzelnen Vorträge sowie die Folien für Softwarekomponenten.

An dieser Stelle an alle Teilnehmer noch einmal vielen Dank für das zahlreiche Erscheinen in den Sessions und dem wahnsinnigen Feedback danach, so machen Konferenzen wirklich Spaß.

 

Links
IoC - VS 2013 Projekt
SA für die Praxis - VS 2013 Projekt
Softwarekomponenten - VS 2013 Projekt
Softwarekomponenten - Folien

ppedv Team Blog: IIS Express und FQDN

Mein aktueller Anwendungsfall einer ASP.NET Web-Anwendung benötigt statt dem üblichen localhost eine echte Domain als Namen. Wer aus Visual Studio eine Website startet, tut dies in der Regel mit IIS Express (früher Cassini Web Develeopment Server).

Dort wird ein zufälliger Port ausgewählt und in die universelle Konfiguration des IIS Express Web Servers in applicationhost.config eingetragen.

image

Den Speicherort der Datei findet man im Screenshot  des IIS Express (Task Bar – Notification Icons- Item- Rechtsklick). Um ein zweites Mapping einzutragen, wird diese Config Datei direkt per Notepad geöffnet. In den Bindings wird eine oder mehrere Bindungen aktiviert.

   1:    <bindings>
   2:  <binding protocol="http" bindingInformation="*:5376:localhost" />
   3:   <binding protocol="http" bindingInformation="*:80:ppedv.localtest.me" />
   4:  </bindings>

Die Adresse localtest.me zeigt immer auf die lokale localhost Adresse 127.0.0.1. und ist damit ein Trick eine echte Internet-Domain zu nutzen, ohne die Hosts-Datei oder schlimmeres zu beanspruchen. Port 80 ist nicht unbedingt erforderlich.

Wenn ein lokaler IIS installiert ist, fängt dieser per Universal Binding alle Domains auf der IP 127.0.0.1 ab. Auch ein Stoppen des www-Publishingdienst löst das Problem nicht. Erst wenn man einen Hostname im IIS Manager vorgibt, ist es anderen Websites möglich ein Binding durchzuführen.

image

Stolperstein #2 ist, dass Binden abseits von localhost auf IIS Express nur möglich ist, wenn IIS Express im Administrator Context läuft. Dazu muss Visual Studio als Administrator (rechte Maustaste runas Administrator) ausgeführt werden.

Im Ergebnis lauscht nun die Web App auf beiden URI

image

Dirk Primbs [MS]: Magic and Technology

I’m on my way to the developer week where I’ll have the opportunity to give the closing session. Usually this means quite tired developers who were running from tech session to tech session the entire day. Also it means that I’ll have the job to entertain while hitting a cord.

Alright. I’m up to the challenge :-)
My topic today: “Magic & Technology”

Truth is: When I’ve seen my first computer 25 years ago (oh boy… Now I’m feeling old), it seemed almost magical that there was a machine that could operate autonomously and follow detailed instructions. But even then at a time where I was not really skilled in tech it felt as if I could quickly figure out how the machine works. In fact after 3 years owning my zx81 I was sure that I knew anything worth knowing about it. I’m sure that this was the same for most people at the time. The computer was just a piece of complicated electronics but still quite dumb when it came to it.

The fascination never left me, but the magic did return only rare occasions. Until a few years ago the first intelligent assistants started appearing and technologies such as Kinect hit the shelves. There is just sooo much potential! And it indeed felt powerful to just say a command into thin air and see the technology respond. Then companies like Google came along and created huge data centers available to the everyday developer. Smartphones in everyone’s hands gave access to the world’s knowledge and the library of Alexandria looks like a toy compared to the databases available online.

This is impressive, but not quite magic yet.
What is magic then?

I think three components need to get together to create the feeling that something magical happens:

1. Performance – things are magic if they push the limits of today’s world. To be able to see through solid surfaces, move objects with a thought or span continents in milliseconds are in the league we’re looking for. Also if it happens practically effortless without waiting time… well… we’re not quite there yet of course :-)

2. Intelligence – magic usually comes with a positive surprise. It is either something unexpectedly useful or something really missing that is provided. Also with intelligence the lines between everyday environment and technology blur since it also means that technology should not require us to adapt to it. Hence it is in the background, understands our situation and offers help just in the right moment (or even helps completely unnoticed).

3. Simplicity – no matter how complex something is. If you’ll need to earn a degree to follow, it probably doesn’t feel all that magic. More importantly it is also less useful in the everyday world. Everything truly impressive I came across so far had a very simple idea and was accessible. This does not mean that it is easy to pull it off, I’m merely talking about the experience as user. Have a look at your navigational system and you understand what I mean. It takes satellite technology to route us through the world, yet I just say “Ok Google, bring me home” to my wrist watch to set it in motion.

The essence is this: If all these factors come together then the result feels like magic.
With the blink of an eye we’re today able to look at the world from space, talk to someone thousands of miles away, navigate unknown cities, see through solid materials, have machinery weighting tons following our commands. We make the lame walk, the dumb speak and the blind see. If this is not magic then nothing is!

And this is what changed in the last 3 years or so: I’ve seen more and more examples of the above being realised. And I’ll share some of them with the audience today. Boy, will I have fun!

Picture source: Flickr

The post Magic and Technology appeared first on Dirk Primbs Online.

Fabian Deitelhoff: Raspberry Pi: Speicherplatz der SD-Karte ausnutzen

Raspberry Pi LogoZum Raspberry Pi habe ich vor kurzem einen Beitrag verfasst, der sich um die Headless WIFI Konfiguration dreht. Nun geht es um vorgefertigte Distributionen, an die das Raspberry Pi Team ebenfalls gedacht hat. Die können kostenfrei heruntergeladen und mit einem beliebigen Tool auf die SD-Karte entpackt werden. Einfaches kopieren reicht nicht aus, denn die Distributionen liegen als Image vor.

Das Problem daran: diese vorgefertigten Distributionen sind an zwei Gigabyte große SD-Karten angelehnt. Die Partitionen, die erzeugt werden, sind dementsprechend nicht größer. Ich nutze für Tests gerne acht Gigabyte große Karten. Mehr Platz brauche ich, in der Testphase, nämlich selten. Wenn die Partitionen aber zu klein sind, wird viel Platz verschwendet. Genau dieses Problem hatte ich jetzt einige Male. Die Schritte, um das zu ändern, sind nicht sonderlich schwierig oder umfangreich. Deshalb bietet dieser Beitrag eine kleine Zusammenfassung der Kommandos und deren Bedeutung, um die Partitionen der SD-Karte auf die tatsächliche Größe der Karte anzupassen.

Das hier vorgestellte Verfahren funktioniert bei der original “Raspbian” Distribution, die von der Raspberry Pi Foundation heruntergeladen werden kann. Es besteht die Möglichkeit, dass alle Daten der SD-Karte verloren gehen beziehungsweise nicht mehr lesbar sind. Dazu reicht es aus, irgendwo eine Zahl falsch einzutippen. Wichtige Daten müssen vorher gesichert sein!

Der aktuelle Stand

Listing 1 zeigt den aktuellen Stand der Partitionen und deren Größe. Diese Tabelle lässt sich durch das Kommando df- h anzeigen.

Filesystem      Size  Used Avail Use% Mounted on
rootfs          2.6G  2.1G  430M  83% /
/dev/root       2.6G  2.1G  430M  83% /
devtmpfs        215M     0  215M   0% /dev
tmpfs            44M  228K   44M   1% /run
tmpfs           5.0M     0  5.0M   0% /run/lock
tmpfs            88M     0   88M   0% /run/shm
/dev/mmcblk0p1   56M  9.5M   47M  17% /boot

Die ersten beiden Einträge zeigen deutlich, dass die jeweiligen Partition nur circa 2,6 Gigabyte groß ist. Und zudem schon zu 83% belegt ist. Für kleinere Spielereien reicht das. Für mehr allerdings nicht.

Partitionen löschen und neu anlegen

Bevor irgendwelche Partitionen in der Größe angepasst werden können, müssen die vorhandenen Partitionen zunächst gelöscht und neu erzeugt werden. Das bereitgestellte Image der Raspbian Distribution erzeugt von sich aus zwei Partitionen.

  • Eine Boot-Partition mit FAT32
  • Eine Linux-Partition mit EXT4

Die erste Partition ist von Windows Systemen aus sichtbar, wenn die SD-Karte gelesen wird. Sie enthält nur ein paar Dateien. Das eigentliche Linux-Dateisystem ist versteckt. Das folgende Kommando startet das Programm fdisk zum Partitionieren der SD-Karte, die mit mmcblk0 angegeben ist.

sudo fdisk /dev/mmcblk0

Einige Distributionen benötigen noch die -uc Option. Bei Raspbian ist das nicht der Fall. Erscheint eine Fehlermeldung, eventuell einfach noch mal mit -uc ausprobieren. Wenn fdisk gestartet ist, bitte p eingeben, um eine Liste aller Partitionen zu erhalten. Bei einer frischen Raspbian Installation sieht die Ausgabe wie in Listing 2 aus.

        Device Boot      Start         End      Blocks   Id  System
/dev/mmcblk0p1            8192      122879       57344    c  W95 FAT32 (LBA)
/dev/mmcblk0p2          122880     5785599     2831360   83  Linux

In der ersten Zeile wird die Boot-Partition angezeigt. Das ist diejenige, die wir auch sehen, wenn wir die SD-Karte unter Windows öffnen. Diese Partition soll intakt bleiben. Aber Nummer zwei geht es an den Kragen. Genauer gesagt muss die Partition gelöscht werden. Dabei gehen keine Daten verloren. Vorher aber bitte den Startsektor dieser Partition notieren. In dem Beispiel aus Listing 2 ist das der Wert 122880. Hier aber auf jeden Fall den Wert aus eurer Ausgabe notieren! Anschließend können wir mit dem Kommando d für delete, gefolgt von der Nummer der Partition, in diesem Fall also 2, die Partition löschen.

Jetzt brauchen wir eine neue, zweite Partition. Das gelingt über die Kommandos n für new, p für primär und dann die Nummer 2. Damit wird eine neue, primäre Partition mit der Nummer zwei angelegt. Das Programm fdisk fragt noch den Start- und den Endsektor ab, um die neue Partition anlegen zu können. Bei Start ist zwingend der oben notierte Wert einzugeben. Auf keinen Fall den Standard übernehmen, da es ansonsten mit der korrekten Zuordnung der Partitionen vorbei ist. Passiert das, am besten gleich die SD-Karte neu bespielen :). Als Endsektor einfach den Standard übernehmen. War das erfolgreich, sieht die neue Übersicht wie das Listing 3 aus. Diese Übersicht, wir erinnern uns, wird mit dem Kommando p ausgegeben.

        Device Boot      Start         End      Blocks   Id  System
/dev/mmcblk0p1            8192      122879       57344    c  W95 FAT32 (LBA)
/dev/mmcblk0p2          122880    15407103     7642112   83  Linux

Die beiden markierten Endsektoren aus Listing 2 und Listing 3 unterscheiden sich deutlich in dem Sinne, dass letzterer wesentlich größer ist. Genau das wollten wir auch erreichen. Zu guter Letzt bitte die neue Partitionstabelle mit dem Kommando w schreiben. Dabei sollte eine Fehlermeldung erscheinen, dass die Geräte in Verwendung sind. Hier hilft nur ein Reboot über das folgende Kommando:

sudo reboot

Dateisystem anpassen

Wenn der Raspberry Pi gebootet hat, ist es an der Zeit, dass Dateisystem an die neuen Gegebenheiten anzupassen. Dazu reicht das folgende Kommando, dass diese Aufgabe übernimmt:

sudo resize2fs /dev/mmcblk0p2

Grundsätzlich kann diese Aktion etwas Zeit in Anspruch nehmen. In diesem Beispiel, bei einer acht Gigabyte großen SD-Karte, dauerte der Vorgang aber lediglich einige Sekunden. Nun folgt die Endkontrolle. Der Befehl df -h zeigt wieder die Größe der vorhandenen Partitionen an. Die Ausgabe darf in Listing 4 bewundert werden.

Filesystem      Size  Used Avail Use% Mounted on
rootfs          7.2G  2.1G  4.8G  30% /
/dev/root       7.2G  2.1G  4.8G  30% /
devtmpfs        215M     0  215M   0% /dev
tmpfs            44M  228K   44M   1% /run
tmpfs           5.0M     0  5.0M   0% /run/lock
tmpfs            88M     0   88M   0% /run/shm
/dev/mmcblk0p1   56M  9.5M   47M  17% /boot

Und wie wir sehen, ist die Root-Partition nun 7,2 Gigabyte groß, was einer acht Gigabyte großen SD-Karte entspricht. War doch gar nicht so schwer :).

Und nun der einfachere Weg

Jetzt kennen wir den manuellen Weg, der immer funktioniert. Zumindest so lange, wie alle notwendigen Tools vorhanden sind. Was aber bei normalen Distributionen immer der Fall sein sollte. Das oben beschriebene Prozedere kann allerdings auch voll automatisch durchgeführt werden. Dazu nach dem Anmelden am Raspberry Pi einfach das Kommando

sudo raspi-config

eingeben. Dadurch wird eine grafische Oberfläche zur Konfiguration gestartet, die in Abbildung 1 zu sehen ist.

Abb. 1: Die Raspberry Pi Konfiguration.

Abb. 1: Die Raspberry Pi Konfiguration.

Der wichtige Eintrag ist schon markiert. Mit “Expand Filesystem” werden genau die oben beschriebenen Aktionen ausgeführt. Es dauert auch nur ein paar Sekunden, bis die Meldung erscheint, dass die Partitionen angepasst wurden und nach einem Neustart aktiv werden. Wird die Konfiguration über finish beendet, erscheint auch direkt die Nachfrage, ob ein Neustart gewünscht ist. Nach diesem sind die neuen Partitionen in der richtigen Größe vorhanden. Auch das Dateisystem ist bereits angepasst. Es geht also auch einfacher :).

Fazit

Für mich ist das mittlerweile einer der ersten Schritte bei einem neuen Raspberry Pi beziehungsweise einer neuen SD-Karte. Da die meisten Distributionen auf zwei Gigabyte ausgerichtet sind, wird einfach zu viel Platz verschwendet. Im Moment nehme ich die Änderungen automatisch durch die Raspberry Pi Konfiguration vor. Ich hatte damit aber auch schon Probleme, weswegen ich hier den manuellen Weg beschrieben habe. In der Regel sollte die automatische Konfiguration aber keine Probleme bereiten.

Viel Spaß mit dem Raspberry Pi und den größeren Partitionen :).

Sven Hubert: Ideen für die Verwendung des MSDN Azure Freikontingents: Datensicherung

Unter Windows Azure steht für die Speicherung von Daten ein sogenannter Blob-Storage zur Verfügung. In diesem können vergleichbar zum Dateisystem Daten jeglicher Art abgelegt werden. Der Blob Storage ist auf hohe Performance und extrem hohe Verfügbarkeit ausgelegt. Er bildet das Rückgrat von Windows Azure für das Hosting von virtuellen Maschinen und den stabilen Betrieb von Diensten. Verglichen mit einfachen Speicheranbietern wie OneDrive oder Dropbox ist er aufgrund der völlig anderen Ausrichtung deutlich teurer. Abgerechnet wird abhängig vom Datenvolumen. Es fallen sowohl Kosten für die Speicherung als auch den Transfer aus dem Azure Rechenzentren heraus an. Die Speicherung von 100 GB Daten wird aktuell mit 1,79 € pro Monat veranschlagt. Das Freikontingent eines MSDN Premium Abo in Höhe von 75€ ermöglicht damit ein kostenloses Backup von bis zu 4 TB an Daten. Eine lohnenswerte Verwendung des MSDN Azure Freikontingents.

Der Blob-Storage wird über das Windows Azure Management Portal eingerichtet.

Einrichtung eines Blob-Speichers

Der Name muss wie gewohnt global eindeutig vergeben werden. Als Standort wählt man das nächstgelegene Rechenzentrum oder eine zuvor definierte Affinitätsgruppe aus.

Einrichtung eines Blob-Speichers

Es werden verschiedene Optionen für die Replikation der Daten angeboten. Unter lokal redundant wird eine mehrfache Spiegelung innerhalb eines Rechenzentrums verstanden. Bei einer Auswahl von Georedundant werden Kopien auch in einem zweiten Rechenzentrum, im Falle einer Auswahl von Westeuropa als primären Standort in Nordeuropa, vorgehalten. Die Kosten einer Georedundanten Datenhaltung liegen mit 3,58 € pro 100 GB deutlich höher. Die garantierte Verfügbarkeit steigt nur geringfügig von 99,9% auf 99,95%. Für die meisten Anwendungsfälle ist lokal redundant sicherlich ausreichend.

Für den Zugriff auf den Blob-Storage steht eine Vielzahl von Werkzeugen zur Verfügung. Eine einfache Möglichkeit stellt der Azure Storage Explorer dar. Zur Einrichtung des Zugriffs werden der Speicherkontoname sowie ein Zugriffsschlüssel benötigt.

Abruf der Zugriffsschlüssel

Einrichtung der Verbindung

Nach dem Herstellen der Verbindung kann ein Container eingerichtet werden.

Anlegen eines Containers

Für die Verwendung als Backup-Speicher ist eine Auswahl von Private sinnvoll. In diesem Fall hat nur der Inhaber des Schlüssels Zugriff auf die Daten.

Hochladen einer Date

Dateien können einzeln durch einen Klick auf Upload übertragen werden.

Deutlich komfortabler gestaltet sich die Arbeit mit dem Dienst durch eine Einbindung als Windows Laufwerk. Aktuell ist hierfür noch der Zugriff auf kommerzielle Lösungen wie z.B. dem CloudBerry Explorer notwendig.

Als skriptfähige Alternative biete sich das Kommandozeilenwerkzeug CloudCopy an. Durch eine Integration eines Aufrufs in den Windows Task Scheduler lässt sich somit z.B. der Inhalt eines ganzen Verzeichnisses sichern.

CloudCopy.exe "c:\backup\*.*" "https://aitbackup01.blob.core.windows.net/backup" "DefaultEndpointsProtocol=https; YccountName=aitbackup01; AccountKey=d5Icqp2Zlb1I/VmipH4TSKF/cagQ+wHTQ+y0rEX4aZ3fxFM/bteHk74 XmLBDl2UJchoYVgrNvb5zJ33Hp+Kn4Q=="

Die Einrichtung einer Backup-Lösung unter Windows Azure gestaltet sich damit sehr einfach. Dank dem Freikontingent ist diese zudem in den meisten Fällen kostenfrei.

Ideen für die Verwendung des MSDN Azure Freikontingents: Datensicherung is a post from: AIT Blog

Jürgen Gutsch: Fußball, Azure und ASP.NET

Eine Fußballreiche Zeit liegt hinter mir und ganz Fußballfrei wird es auch nicht weitergehen. Das WM Fieber hat meinen ersten Sohn gepackt und er spielt jetzt bei den Bambini im örtlichen Fußballverein mit.

Fußballreich, nicht nur durch die Weltmeisterschaft, welche die Deutsche Nationalmannschaft am 13. Juli mit einem hart erkämpften Erfolg für sich entschieden hat, sondern auch durch den Launch der neuen Website, des 17fachen Schweizer Meisters. Den Launch der Website des FC Basel.

Das gesamte Team der YooApps, von den Konzeptlern, den HTML- und .NET-Entwicklern bis hin zur QS und Projektleitung haben es in den letzten zwei Monaten in einem fast unmöglichen Kraftakt geschafft die Website online zu stellen. Wie auch bei der Deutschen Nationalelf wurde gekämpft, geschwitzt und geschimpft. Dennoch freute man sich über jeden noch so kleiner Erfolg, in dem das Projekt ein Stück vorwärts gebracht wurde. Einfach große Klasse, was in der Zeit entstanden ist, obwohl wir noch nicht komplett durch sind. So werden in den nächsten Tagen und Wochen unter anderem, der Fanbereich und der Liveticker nachgeliefert, um die Website weiter abzurunden. Und natürlich bin ich ein kleines bisschen Stolz auf das was wir als Team geleistet haben.

Infrastruktur auf Microsoft Azure

Nicht nur wir als Team mussten einen Kraftakt leisten, auch die Website selber muss während den Saisonspielen – vor allem während eines Champions League Spiels – enorme Zugriffszahlen aushalten und stabil laufen. Besonders der Liveticker ist von großem Interesse.

Um das zu erreichen, mussten wir, wie auch Yogi Löw, taktisch vorgehen. Pros und Cons gut abwägen. Hilfsmittel und Systeme mit Bedacht auswählen.

Dennoch war schnell klar, dass wir die Website auf Microsoft Azure heben müssen um die Website selber, aber auch die Datenbanken und File Storages beliebig skalieren zu können. Wir haben uns dafür entschieden, das CMS auf einer Azure Website zu hosten um einfacher und schneller skalieren und deployen zu können, auch wenn wir dadurch weniger Kontrolle über die Plattform haben als z. B. in einer Web Role.

Continouous Integration und Deployments per Git auf eine Azure Website ist einfach genial uns schnell. So lassen wir drei Haupt-Branches (Dev, QA und Master) auf drei entsprechende Website Instanzen laufen, um uns abzusichern. Entwickelt wird ausschließlich isoliert in Feature-Branches.

Das CMS setzt eine SQL Database voraus, in die wir 10.000 News aus dem alten System migrieren mussten. Über 80.000 Bilder mussten aus dem alten System in das neue Migriert werden. Zum Glück ist das CMS “Azure-Ready” und die Anbindung an den Azure Blob Storage war ein Kinderspiel.

Auf diese Art haben wir die komplette Website auf drei verschiedene, schnelle, hochverfügbare und skalierbare Azure Services verteilt: Azure Website, SQL Database und Azure Blob Storage.

Ein schönes Feature, dass wir für das Deployment auf die Produktiv-Instanz nutzen sind die Deployment-Slots die man für eine Website einrichten kann. So wird der Master-Branch nie direkt auch die Produktiv-Instanz publiziert, sondern erst auf den freien Deployment-Slot. Bei Erfolg, können dann – ähnlich wie bei einer Web-Role – die Instanzen “geswopt” werden. Der Deployment-Slot wird unterbrechungsfrei zur Produktiv-Instanz.

Etappensieg

Der Launch letzte Woche war, im Gegensatz zum gestrigen WM-Finale, aber noch kein Endspiel, sondern ein nur ein – wenn auch ein wichtiger – Etappensieg. Ab jetzt wird nicht mehr mit Fußball verglichen, sondern eher mit der Tour de France: Die schwierigste Bergetappe haben wir hinter uns, es folgt eine weitere kurze Etappe mit Einzelsprints, um dann auf weiteren eher flachen Etappen auf die Zielgerade zu kommen. ;)

Johnny Graber: SyncToy: Ein kleiner Backup-Helfer

An Backups denkt man in der Regel erst wenn es zu spät ist. Vorher ist es immer zu aufwändig und man glaubt eh keine wichtigen Dateien zu haben die nicht noch irgendwo anders gespeichert sind. Um nicht eines besseren belehrt zu werden würde es genügen die Daten nur regelmässig auf ein externes Laufwerk zu sichern.
Mit SyncToy gibt es ein kleines Programm das genau für diesen Zweck gemacht wurde.

 

Download und Installation

SyncToy kann bei Microsoft als 32 oder 64bit Version heruntergeladen werden. Welche Version man wählt spielt keine Rolle. Bisher konnte ich keine Unterschiede feststellen die über das Installationsverzeichnis hinausgehen (Programme oder Programme (x86)).

Der Installationsdialog ist erstaunlich umfangreich und will sehr viele Bestätigungen. Wiederholtes klicken auf “Next” genügt zur Installation allerdings vollkommen.

 

Konfigurieren

Nach dem ersten Start begrüsst einem dieser einfache Dialog mit der Aufforderung ein Verzeichnispaar zu erstellen:

SyncToy

Der Dialog zum Erzeugen des Verzeichnispaares unterscheidet zwischen linkem (Left) und rechtem (Right) Ordner. Um meinen persönlichen Ordner zu sichern wähle ich diesen als linken Ordner aus und als rechten Ordner das Verzeichnis “Z:\Air\User.JGR”:

SyncToy 1. Schritt

Rechter und linker Ordner sind wenig gebräuchliche Begriffe. “Quelle” und “Ziel” dürfte einem bekannter sein. Allerdings kommt es sehr darauf an was man mit diesen beiden Ordnern machen will. Dies legt man auf dem 2. Schritt fest:

SyncToy 2. Schritt

Zur Auswahl stehen einem diese Optionen:

  • Synchronize: Neue, veränderte und gelöschte Dateien werden in beide Richtungen synchronisiert.
  • Echo: Veränderungen (hinzufügen, löschen, verändern) im linken Ordner werden auf den rechten appliziert.
  • Contribute: Nur neue und veränderte Dateien aus dem linken Ordner werden in den rechten Ordner übertragen. Im linken Ordner gelöschte Dateien bleiben im rechten Ordner erhalten.

Welche Option man auswählen soll hängt ganz vom Einsatzweck ab. Echo und Synchronize unterscheiden sich dadurch ob auch mit dem rechten Ordner gearbeitet wird. Ist dies der Fall sollte man Synchronize verwenden. Will man die Ordner nur 1:1 sichern genügt Echo.
Contribute kann einem dabei helfen auch gelöschte Dateien zurück zu holen. Da keine Dateien gelöscht werden muss man sich selber ums aufräumen kümmern.

 
Zum Abschluss benötige dieses Ordnerpaar noch einen Namen, unter dem man später die beiden Verzeichnisse identifizieren kann:

SyncToy 3. Schritt

 

Ordner synchronisieren

SyncToy lässt einem die Ordnerpaare einzeln oder gemeinsam ausführen. Letzteres ist vor eine Arbeitserleichterung da man sich nicht an alle gemachten Änderungen erinnern muss. Je nach Anzahl Änderungen, der Datenmenge oder der Geschwindigkeit des angehängten Laufwerkes muss man sich nach einem Klick auf “Run all” ein wenig gedulden:

SyncToy Resultat

 

Fazit

Mit SyncToy gibt es ein kleines Tool mit dem man einfach seine Daten sichern kann. Nach einer einmaligen Konfiguration genügt dafür ein einziger Klick. Dennoch ist es flexibel genug um auch spezielleren Wünschen bei der Synchronisierung Rechnung zu tragen.

Empfehlung: Unbedingt ausprobieren!


Einsortiert unter:dnugBern, Tools, webDotNet Tagged: .Net, Beschleuniger

Norbert Eder: Link-o-licious #6

Ich habe wieder einige interessante Links, die ich mit euch teilen möchte. Dieses Mal dreht sich sehr viel um das Thema *Go*, da ich mich aktuell vermehrt damit beschäftige und den einen oder anderen Test damit mache.

The post Link-o-licious #6 appeared first on Norbert Eder.

ppedv Team Blog: MVC 4 – Ein View für Create und Edit

Meist sind die Datenoperationen für Create und Edit gleich. Zumindest an der Oberfläche. Daher ist es naheliegend wenn man einen View für beide Operationen nutzen möchte. Das Visual Studio legt aber per Default immer zwei Views an.

Mit wenig Änderungen kann jedoch ein simpler Edit-View auch für die Datenanlage verwendet werden. Die Grundidee ist, dass es eine Eigenschaft im Datenobjekt gibt, die anzeigt, ob das Objekt neu ist, oder bereits vor dem Aufruf des Views bestand. In meinem BSP verwende ich den ID, der bei einem neuen Objekt auf –1 gesezt wird. Dafür habe ich eine “mini-Factory”-Klasse geschrieben:

    public static class DatenFactory
    {
        public static Daten GetDatenForID(int i)
        {
            Daten dat= new Daten()
            {
                ID =i,
                Name="Martin Groblschegg",
                Firma ="ppedv"
            };
            return dat;
        }

        public static Daten GetNewDaten()
        {
            Daten dat = new Daten()
            {
                ID = -1,
                Name = "",
                Firma = ""
            };
            return dat;
        }
    }

Das Datenobjekt selbst (die Klasse Daten) ist so simpel, dass ich sie hier nicht zeigen muss. Eine Klasse mit den Properties ID, Name und Firma.

DatenController

Nun zum DatenController. Dieser bekommt drei Actions: Edit, New und CreateOrEdit. CreateOrEdit ist die Action, die vom Formular aufgerufen wird.

Edit bekommt die ID des zu editierenden Objekts. Mit Hilfe der Factory wird das Objekt aus dem Store geholt und danach dem View mit dem Namen “EditOrCreate” übergeben:

        public ActionResult Edit(int id)
        {
            Daten daten = DatenFactory.GetDatenForID(id);
            return View("EditOrCreate",daten);
        }

 

Die Action New arbeitet ähnlich, jedoch wird keine ID übergeben und das Objekt neu gebildet und diese hat daher den ID –1:

        public ActionResult New()
        {
            Daten daten = DatenFactory.GetNewDaten();
            return View("EditOrCreate", daten);
        }

Die letzte Action ist die CreateOrEdit-Action, die vom Formular aufgerufen wird:

        [HttpPost]
        public ActionResult CreateOrEdit(Daten dat)
        {
            
            if(dat.ID <0)
            {
                // Neu in DB Anlegen
                DBHandling.NewDaten(dat);
            }
            else
            {
                // Update in Datenbank
                DBHandling.UpdateDaten(dat);
            }
            return RedirectToAction("Index", "Home");
        }

Create-Or-Edit-View

Als nächstes muss der View angelegt werden. Im Add View Dialog wird der ViewName auf “EditOrCreate” gesetzt. Als Template wird das “Edit” Template gewählt und die Model-Class ist die Daten-Klasse:

image

Im View ist die Zeile "@using (Html.BeginForm())” zu finden. Diese muss angepasst werden, sodass im Post die “CreateOrEdit” Action des Daten-Controllers aufgerufen wird. Die Zeile lautet also richtig:

@using (Html.BeginForm("CreateOrEdit", "Daten"))

 

Mit diesen Änderungen ist der Controller und View fertig. Um es zu testen, wird nach der Start-View angepasst, um die beiden Actions aufzurufen. New wird ohne Parameter aufgerufen und Edit erhält einen Parameter ID mit dem Wert 123.

<ul class="nav navbar-nav">
<li>@Html.ActionLink("Neu", "New", "Daten")</li>
<li>@Html.ActionLink("Edit", "Edit", "Daten", new {ID=123}, null)</li>
</ul>

 

Sourcecode

Den gesamten Code gibt es in meinem OneDrive zum Download. (Link zum Download). Die NuGet-Packs wurden entfernt und müssen neu geladen werden.

Klaus Aschenbrenner: My upcoming speaking schedule

(Be sure to checkout the FREE SQLpassion Performance Tuning Training Plan – you get a weekly email packed with all the essential knowledge you need to know about performance tuning on SQL Server.)

Over the next weeks and months I have an incredible speaking schedule around the whole world. You have plenty of opportunities to meet me in person, and discuss performance related topics in SQL Server.

My first stop is this week in Telford/UK for the SQLBits conference. I have been speaking at SQLBits for ages – I think my first conference participation was around 2009. On Friday I speak about “Latches, Spinlocks & Latch Free Data Structures” in SQL Server. If you have followed my blog postings over the last weeks, you can already see what to expect in this session. I will also show you a concrete example of Spinlock Contention in SQL Server, how you can troubleshoot, and finally solve it. On Saturday I have an additional session where I talk about “The Dangerous Beauty of Bookmark Lookups”. Bookmark Lookups are very powerful in SQL Server, but have a huge amount of side-effects. In this session we will concentrate on these side effects, and I will show you how you can overcome the various problems.

At the beginning of September I’m traveling for the first time in my life to the other hemisphere of the Earth – my trip brings me to Cape Town in South Africa, where I’m speaking at the SQLSaturday #301. On Friday, September 5 I give a whole day-long precon about “Practical SQL Server Performance Troubleshooting“, the precon that I have delivered in 2012 at the SQLPASS Summit in Seattle. In this precon we will work with a typical OLTP workload (based on the TPC-E benchmark), and work throughout the whole day to identify performance bottlenecks in SQL Server, and finally solve them – completely transparent to the workload itself. Be sure to register for my precon.

Two weeks after Cape Town, I’m flying to Dublin and delivering a precon and a regular session at the SQLSaturday #310 (September 19 – 20). My precon is about “Performance enhancements in SQL Server 2014“. Large parts are about the new cardinality estimator, and of course I will also talk about In-Memory OLTP and “Clustered” Column-Store Indexes. Dublin is always fun, and the recovery process afterwards is always very long – there is no fast recovery available ;-)

For the first time in my life I have also the honor to be a speaker at the SQL Server Days in Belgium (September 30 – October 1). I have already heard a lot of good things about this conference, so I’m really looking forward to be part of it, and delivering a session. My session will be about “Latches, Spinlocks and Latch Free Data Structures” – the same that I deliver this week in Telford/UK. If you miss SQLBits, there is still a chance to see this session once again in Europe.

The week after Belgium I will present at the SQLdays Conference in Munich/Germany (October 7 – 9), where I will deliver on Tuesday the same precon as in Cape Town – “Practical SQL Server Performance Troubleshooting“. I’m already looking forward to this conference, because I have known the owners for ages, and it’s a very nice conference in Germany.

At the beginning of November I travel to the west and flying to Seattle for the SQLPASS Summit, where I will also deliver my Latches, Spinlocks, and Latch Free Data Structures session. For me it’s the 7th participation as a speaker (I delivered my 1st session in 2006), so I’m already feeling like an old-timer. Maybe it is a coincidence that I’m losing hairs, and turning grey… ;-) The SQLPASS Summit is always an amazing experience, because it’s the largest SQL Server conference in the universe (assuming that only we on earth use SQL Server). I’m already looking forward to meeting all my friends and Twitter followers again in person.

As you can see I have an amazing and very time consuming speaking schedule over the next weeks and months. But that’s not all. In addition to my conference participations I also deliver 5 SQLpassion workshops in the autumn. I’m very proud to deliver my brand new SQL Server Query Tuning Workshop 4 times across Europe. This workshop covers the query tuning aspect of SQL Server, without going into DBA related topics. The main audience for the workshop is a SQL Server developer. I talk for 4 days about execution plans, execution plans, and execution plans. If you want to know the nasty details about SQL Server, I have my more in-depth SQL Server Performance Tuning & Troubleshooting Workshop, which covers all aspects of how to tune your complete SQL Server installation. It’s a more DBA centric course. Here are the dates and locations for my SQLpassion workshops:

  • SQL Server Query Tuning Workshops
    • October 13 – 16 in Zurich/Switzerland
    • October 20 – 23 in London/UK
    • October 27 – 30 in Vienna/Austria
    • November 17 – 20 in Utrecht/Netherlands
  • SQL Server Performance Tuning & Troubleshooting Workshop
    • November 24 – 28 in Antwerpen/Belgium

Besides that I’m using the quiet summer time for the various session preparations, and of course for preparing my new SQL Server Query Tuning Workshop. But my life doesn’t consist only of SQL Server. Besides my SQL Server business I run my Boeing 737-800 flight simulator, and I do a huge amount of DSLR photo/video shooting. I just ordered some books about Astrophotography and Time Lapsing. Imagine what you can expect when you combine both things together. Do you have ever seen stars during the day?

Stars in the Sky

And because I’m crazy I have started to run about a month ago. I already make a very good progress, and I’m hopefully able to complete my first 5k run in a few weeks!

Thanks for reading!

-Klaus

codefest.at [MS]: Visual Studio Engineering Stories

imageAgile Development ist eine Reise, und nicht etwas auf das man von einem auf den anderen Tag einfach komplett umstellt. Die Engineering Story “Scaling Agile accross the enterprise” zeigt wie die Reise der Developer Division bei Microsoft ausgesehen hat, wie unsere Kunden von den schnelleren Release Zyklen profitieren und wie man selbst in diese Richtung gehen kann, um in letzter Konsequenz effizienter und wettbewerbsfähiger zu sein.

P.S: Runterscrollen hilft Smile with tongue out 

Andreas Pollak
Product Marketing Manager Visual Studio (Entwicklungs- und Testwerkzeuge)
clip_image001

Leader im Magic Quadrant für ALM

Karsten Kempe: TFS2013 – Test WorkItem Creator

Neulich habe ich für ein internes Projekt ein Test-Set mit einer großen Anzahl von Work Items benötigt. Und da ich nicht nur einfach Product Backlog Items und ein paar Bugs anlegen wollte, sondern eine komplette Work Item Hierarchie benötigte, beschloss ich eine kleine Konsolenanwendung zu schreiben, die das für mich erledigt. Wie sonst hätte ich zweitausend Work Items innerhalb weniger Minuten anlegen sollen?

Für ein internes Projekt benötigte ich eine durchgängige Work Item Hierarchie, angefangen bei Feature Work Items bis hin zu Task Work Items. Ausserdem sollten auch Bug Work Items unterhalb eines Features auftauchen und Test Cases zu einigen Product Backlog Items verlinkt sein.

TFS Backlog ViewAbbildung 1: TFS2013 Feature to Task drill-down

Für eine kleine Menge kann man das noch gut mit der Hand erledigen, aber wenn man mehrere Hundert Work Items haben will, dann lohnt es sich ein kleines Tool zu schreiben, welches die Arbeit für einen erledigt.

Die wichtigsten Zeilen Code möchte ich hier kurz vorstellen. Das Programm könnt Ihr Euch gerne von hier runterladen und verwenden. Ebenso könnt Ihr mir gerne auch Anregungen hinterlassen, wie sich das Tool sinnvoll erweitern lässt. Wahrscheinlich kann man noch ordentlich an der Performance-Schraube drehen, aber das war mir jetzt nicht so wichtig…

Folgende References werden für die Anwendung benötigt:

  • Microsoft.TeamFoundation.WorkItemTracking.Client.dll
  • Microsoft.TeamFoundation.Client.dll

Für das Erzeugen von Work Item Titeln und Beschreibungen verwende ich eine externe Komponente, den NLipsum-Generator, die ich über NuGet in mein Projekt eingebunden habe.

Als Erstes muss eine Verbindung zur Team Project Collection aufgebaut und der Work Item Store geladen werden, bevor man das Team Projekt auslesen kann:

TfsTeamProjectCollection tfsCollection = new TfsTeamProjectCollection(
   new Uri(serverUrl));
tfsCollection.EnsureAuthenticated();
WorkItemStore store = (WorkItemStore)tfsCollection.GetService(
   typeof(WorkItemStore));
Project project = store.Projects[projectName];

Zum Anlegen eines Work Items muss zunächst der Work Item Typ ausgewählt und dann ein Work Item für diesen Typ erstellt werden.

WorkItemType wiType = project.WorkItemTypes[workItemType];
 
// Create the work item.
{
Title =  string.Join(" ", gen.GenerateWords(4)),
Description =  gen.GenerateLipsum(4, Features.Sentences"")
};

Je nachdem welches Work Item gerade erstellt worden ist, muss noch eine Verknüpfung zu anderen Work Items vorgenommen werden. Um eine Hierarchie von Feature, Product Backlog Item und Task aufzubauen, wird eine Parent-Child-Beziehung benötigt.

WorkItemLinkType hierarchyLinkType = 
   store.WorkItemLinkTypes[CoreLinkTypeReferenceNames.Hierarchy];
subItem.Links.Add(new WorkItemLink(hierarchyLinkType.ReverseEnd, parentId));
return hierarchyLinkType;

Eine Verknüpfung von einem Test Case zu einem Product Backlog Item erfolgt in der Regel über eine “Tests”-Beziehung oder von PBI zu Test Case über eine “Tested by”-Beziehung.

WorkItemLinkTypeEnd hierarchyLinkType = 
   store.WorkItemLinkTypes.LinkTypeEnds["Tests"];
workItem.Links.Add(new WorkItemLink(hierarchyLinkType, relationId));

Den Source Code gibt es hier zum Download.
Viel Spaß und happy testing!

friends header

bloggers headline

links header

 
Don't contact us via this (fleischfalle@alphasierrapapa.com) email address.