Der neue C# Compiler bzw. die .NET Compiler Platform Roslyn bietet eine C# Scripting API an. Diese Scripting API ermöglicht das erstellen von Scripts mit C# und das ausführen von C# in einem “Interactive Window” (REPL – Read–eval–print loop).
Continue reading
Entity Framework “In Code” konfigurieren
Wird das Entity Framework (kurz EF) über NuGet installiert, so werden im “App.config” diverse EF spezifische Konfigurationseinträge angelegt. Es gibt jedoch Szenarien, da sollen diese Konfigurationseinträge nicht im “App.config” oder “Web.config” vorkommen, sondern mittels Code gesetzt werden. Denn die Einträge im “App.config” oder “Web.config” verlangen zwangsläufig eine Referenz auf das EF im jeweiligen Project.
Ein mögliches Szenario ist, dass eine austauschbare Komponente das EF für den Datenzugriff verwendet. Die Komponente selbst soll dann für die Konfiguration des EFs zuständig sein.
Das folgende Bild zeige eine Applikation mit einer 3-Layer Architektur. Der UI-Layer beinhaltet die App.config-Datei. Der Core-Layer enthält sämtliche Bussines Logik. Der Data-Layer ist für den Datenzugriff zuständig und bietet drei Komponenten an. Eine der Komponenten greift auf Webservices zu, einer verwendet einen In Memory Speicher und einer verwendet das Entity Framework für den Zugriff auf eine (SQL) Datenbank. Die drei Komponenten können zur Design- oder Laufzeit ausgetauscht werden (beispielsweise unter Verwendung eines IoC-Containers).
Der rote Pfeil “App.config” symbolisiert die nicht gewollte Referenz. Damit diese Referenz verhindert werden kann, wird das EF explizit im Code konfiguriert. Dafür wird eine Klasse erstellt welche von der Klasse “DbConfiguration” ableitet. Diese Konfiguration wird zur Laufzeit vom Entity Framework über Reflection geladen. Sie enthält die Konfiguration welche sich üblicherweise im “App.config” befindet.
Das folgende Code-Beispiel zeigt eine Klasse welche von “DbConfiguration” ableitet und das EF für die Verwendung einer SQLite-Datenbank konfiguriert.
using System.Data.Common; using System.Data.Entity; using System.Data.Entity.Core.Common; using System.Data.SQLite; using System.Data.SQLite.EF6; namespace Data.SqliteStorage { /// <summary> /// Konfiguriert das Entity Framework zur Verwendung mit SQLite. /// Diese Klasse wird automatisch instanziiert, weil von sie von <see cref="DbConfiguration"/> abgeleitet ist. /// Die Konfiugration stellt sicher, dass keine Anpassungen an einer Web.config oder App.config Datei nötig sind. /// </summary> public class SqLiteDbConfiguration : DbConfiguration { private const string DbFileName = "db.sqlite3"; /// <summary> /// Eine <see cref="DbConnection"/> zur SQLite-Datenbank. /// </summary> /// <value> /// Die Connection. /// </value> public static DbConnection Connection { get; } = new SQLiteConnection(GetConnectionString(), true); public SqLiteDbConfiguration() { // Das Entity Framework wird in dieser Applikation immer mit SQLite benützt // Es ist kein Umschalten in der Konfiguration (web.config oder app.config) angedacht. SetProviderFactory("System.Data.SQLite", SQLiteFactory.Instance); SetProviderFactory("System.Data.SQLite.EF6", SQLiteProviderFactory.Instance); SetProviderServices("System.Data.SQLite", (DbProviderServices)SQLiteProviderFactory.Instance.GetService(typeof(DbProviderServices))); } private static string GetConnectionString() { // foreign key=true aktiviert die FK-Constraints return "Data Source=" + GetFilePath() + ";foreign keys=true"; } private static string GetFilePath() { return "|DataDirectory|" + DbFileName; } } }
SQLite, EntityFramework und Cascade Delete
SQLite bzw. eine SQLite Datenbank unterstützt, um die referentielle Integrität sicherzustellen, eine das Konstrukt einer Löschweitergabe (“ON DELETE CASCADE” beim erstellen eines Fremdschlüssels).
Während diese Löschweitergabe mit einem Tool z.B. SQLite Studio wie erwartet funktioniert, passiert beim Einsatz des Entity Frameworks nichts.
Die Ursache dieses Verhaltens ist, dass SQLite aus Kompatibilitätsgründen die Foreign Key Constraints ignoriert. Mehr dazu hier.
Man kann, im Falle von ADO.NET bzw. Entity Framework, die Foreign Key Constraints über den Connection String einschalten. Das kann mit dem Flag “foreign keys=true” gemacht werden.
Hier ein Beispiel, eines ConnectionStrings, ohne das entsprechende Flag:
<connectionStrings> <add name="ApplicationDbContext" connectionString="data source=|DataDirectory|\data.sqlite3" providerName="System.Data.SQLite" /> </connectionStrings>
Und hier eines mit eingeschaltenen Foreign Key Constraints:
<connectionStrings> <add name="ApplicationDbContext" connectionString="data source=|DataDirectory|\data.sqlite3;foreign keys=true" providerName="System.Data.SQLite" /> </connectionStrings>
CORS für IE8 und IE9
IE8 und IE9 unterstützten CORS für XMLHttpRequests nicht. Cross Domain AJAX Requests lassen sich jedoch mit dem “XDomainRequest” abbilden (siehe hier).
Ich habe vieles ausprobiert und habe schlussendlich mit diesem JQuery Plugin eine elegante und funktionierende Lösung gefunden:
https://github.com/MoonScript/jQuery-ajaxTransport-XDomainRequest
Das Plug-in registriert eine jQuery ajaxTransport Extension welche für den IE8/9 einen XDomainRequest anstatt einen XMLHttpRequest verwendet. Somit können die jQuery AJAX Functions (ajax, get, post usw.) normal verwendet werden. Für alle anderen Browser wird der normale XMLHttpRequest verwendet.
Damit das funktioniert müssen jedoch die XDomainRequest Einschränkungen beachtet werden!
– Only GET or POST
— When POSTing, the data will always be sent with a Content-Type of text/
– Only HTTP or HTTPS
— Protocol must be the same scheme as the calling page
– Always asynchronous
(Quelle: https://github.com/MoonScript/jQuery-ajaxTransport-XDomainRequest/blob/master/README.md, 07.08.2015)
KO Support für IE8
Leider ist der Internet Explorer 8, trotz des hohen Alters, noch immer nicht ausgestorben*. Deshalb sollte eine Website auch mit dem IE8 “zugänglich” sein.
Das MVVM Framework Knockout unterstütz offiziell den Internet Explorer v6 – v11.
Knockout is tested on the following browsers:
•Mozilla Firefox (versions 3.5 – current)
•Google Chrome (current)
•Microsoft Internet Explorer (versions 6 – 11)
•Apple Safari for Mac OS (current)
•Apple Safari for iOS (versions 6 – 8)
•Opera (current version)It’s very likely that Knockout also works on older and newer versions of these browsers, but there’s only so many combinations we can check regularly! At the time of the last check, Knockout was also found to work on the following browsers (though we don’t recheck these for every release):
•Google Android OS browser
•Opera Mini
•Google Chrome 5+
•iOS Safari 5
•Mac OS X Safari 3.1.2+To get a good idea of how Knockout will run on another browser or platform, simply run the test suite. This will validate hundreds of behavioral specifications and produce a report of any problems. The pass rate should be 100% on all the browsers listed above.
Quelle: Knockout: Browser Support
Als ich jedoch meine auf Knockout basierte Webapplikation auf IE8 ausprobiert habe, hat das Databinding nicht funktioniert. Der Grund ist realtiv simpel. Dennoch ist es verwunderlich, dass auf der Knockout Seite kein Wort darüber verloren wird.
Der Internet Explorer 8 interpretiert einige Keywords im “data-bind”-Attribut falsch und das Binding schlägt fehl. Das lässt sich verhindern in dem diese Keywords mit einfachen Anführungszeichen (‘) umschlossen werden. Das hat keine Auswirkung auf die Funktionalität und die Bindings funktionieren so auch in anderen Browsern.
Beispiel:
<!-- Funktioniert im IE8 nicht --> <div data-bind="template: { if: myViewModel.templateName, name: myViewModel.templateName }"></div> <div data-bind="with: myViewModel, css: { 'cssClassToApply': myViewModel.isXyAvailable }"> <!-- Funktioniert im IE8 --> <div data-bind="template: { 'if': myViewModel.templateName, name: myViewModel.templateName }"></div> <div data-bind="'with': myViewModel, 'css': { 'cssClassToApply': myViewModel.isXyAvailable }">
Meine Versuche haben ergeben, dass folgende Bindings nur mit einfachen Anführungszeichen verwendet werden können**:
– for
– foreach
– css
– with
* Gemäss statcounter kommt der IE8 immer noch auf einen Marktanteil von ~5% (27.06.2015).
Als der IE8 released wurde (19. März 2009) war Google Chrome in der Version 1 verfügbar. Die aktuellste Chrome Version ist v43.
** Nicht systematisch getestet. Ohne Gewähr.
jQuery UI und Knockout – “You cannot apply bindings multiple times to the same element”
Um jQuery UI zusammen mit Knockout zu verwenden habe ich ein eigenes Knockout Binding erstellt. In der Init Methode wird das jQuery Widget erstellt.
Beim folgenden Beispiel handelt es sich um TypeScript Code.
ko.bindingHandlers["dialogBox"] = { init(element: any, valueAccessor: any) { $(element).dialog({ autoOpen: false, modal: true, resizable: false, // More Code }); return false; }, update(element: any, valueAccessor: any) { // Update Code } }
In diesem Beispiel verwende ich den jQuery UI Dialog. Der Content des Dialogs befindet sich in einem Div. Natürlich wurde der Inhalt dieses Divs über ein Knockout-Template erzeugt (“template”-Binding). Mit dieser Methode lässt sich der Code welcher das DOM kennt, auf die Bindings beschränken.
Bei einigen Widgets, darunter auch der Dialog, tritt jedoch der folgende Fehler auf:
“You cannot apply bindings multiple times to the same element.”
Um die Ursache zu verstehen, muss man wissen, dass jQuery UI beim initialisieren bzw. erstellen des Widgets das Div, welches den Content des Dialogs darstellt, ausschneidet, etwas verpackt und ganz unten in der Seite einfügt.
Vorher:
<html> <head> <!-- jQuery und jQuery UI includen --> <script> $(function() { $( "#dialog" ).dialog(); }); </script> </head> <body> <div id="insideMeIsTheDialog"> <div id="dialog" title="Basic dialog"> <p>This is the default dialog which is useful for displaying information. The dialog window can be moved, resized and closed with the 'x' icon.</p> </div> </div> </body> </html>
Nachher:
<html> <head> <script> $(function() { $( "#dialog" ).dialog(); }); </script> </head> <body style="cursor: auto;"> <div id="insideMeIsTheDialog"></div> <div aria-labelledby="ui-id-1" aria-describedby="dialog" role="dialog" tabindex="-1" style="position: absolute; height: 143.6px; width: 299.6px; top: 203px; left: 660px; display: block; right: auto; bottom: auto;" class="ui-dialog ui-widget ui-widget-content ui-corner-all ui-front ui-draggable ui-resizable"> <div class="ui-dialog-titlebar ui-widget-header ui-corner-all ui-helper-clearfix ui-draggable-handle"> <span class="ui-dialog-title" id="ui-id-1">Basic dialog</span> <button title="Close" role="button" class="ui-button ui-widget ui-state-default ui-corner-all ui-button-icon-only ui-dialog-titlebar-close" type="button"> <span class="ui-button-icon-primary ui-icon ui-icon-closethick"></span> <span class="ui-button-text">Close</span> </button> </div> <div style="width: auto; min-height: 105px; max-height: none; height: auto;" class="ui-dialog-content ui-widget-content" id="dialog"> <p>This is the default dialog which is useful for displaying information. The dialog window can be moved, resized and closed with the 'x' icon.</p> </div> <div style="z-index: 90;" class="ui-resizable-handle ui-resizable-n"></div> <div style="z-index: 90;" class="ui-resizable-handle ui-resizable-e"></div> <div style="z-index: 90;" class="ui-resizable-handle ui-resizable-s"></div> <div style="z-index: 90;" class="ui-resizable-handle ui-resizable-w"></div> <div style="z-index: 90;" class="ui-resizable-handle ui-resizable-se ui-icon ui-icon-gripsmall-diagonal-se"></div> <div style="z-index: 90;" class="ui-resizable-handle ui-resizable-sw"></div> <div style="z-index: 90;" class="ui-resizable-handle ui-resizable-ne"></div> <div style="z-index: 90;" class="ui-resizable-handle ui-resizable-nw"></div> </div> </body> </html>
Ist nun der Knockout Binding Scope der Body oder allgemeiner gesamt, der gemeinsame Parent des Dialog Divs und des “ausgeschnittenen” Dialog Divs, so probiert Knockout die Bindings mehrmals auszuführen.
Die Lösung:
1. Den Scope für das Knockout Binding einschränken
2. jQuery UI das Widget ausserhalb dieses Scopes platzieren lassen
<html> <head> <script> $(function() { $( "#dialog" ).dialog({ appendTo: "#container" }); }); </script> </head> <body> <div id="container"> <div id="bindingRoot"> <div id="dialog" title="Basic dialog"> <p>This is the default dialog which is useful for displaying information. The dialog window can be moved, resized and closed with the 'x' icon.</p> </div> </div> </div> </body> </html>
RESX Dummy Dateien generieren
Wird die Lokalisierung bzw. Internationalisierung einer Applikation mit Hilfe von resx Dateien gemacht, kann es nützlich sein, sich von einer bestehenden resx Datei eine neue resx Datei zu erstellen, welche die Grundlage für die Unterstützung einer neuen Sprache darstellt.
Zu Testzwecken wird dann jeder Eintrag in der neuen resx Datei mit zwei Zeichen ISO Code der neuen Sprache prefixt.
So kann rasch getestet werden ob die Lokalisierung funktioniert.
PowerShell eignet sich wunderbar zur erstellen eines solchen Scripts.
$neutralResxFileName = "MyResxFilenNameWithoutExtension"; $lang = "en" $text = Get-Content $neutralResxFileName + ".resx" foreach($val in $text.root.data) { $str = ($val.value + " " + $lang) $val.value = $str } $text.Save(".\" + $neutralResxFileName + "." + $lang + ".resx")
JSON oder XML zu C# Klassen
Häufig kommt es vor das man gerne JSON oder XML deserialisieren möchte, um die Vorteile einer stark typisierten Sprache wie C# zu nutzen. Nun es ist jedoch relativ langweilig die entsprechenden Struktur aus einem JSON oder XML File zu lesen und daraus Klassen zu erstellen.
Continue reading
Build für mehrere Konfigurationen ausführen
Es gibt diverse Szenarien wo es nützlich sein kann, mit einem MSBuild Aufruf mehrere Konfigurationen eines Projektes zu builden. So z.B. wenn ein NuGet-Package erstellt werden soll welches DLLs für mehrere .NET Versionen enthält. Im folgenden Post zeige ich auf wie sich das bewerkstelligen lässt.
Continue reading
How to (re)install Windows Feedback App
1. PowerShell (Admin)
2. cd c:\program files\windowsapps
3. Search for something like “Microsoft.MicrosoftFeedback_*”
4. Change into this “Microsoft.MicrosoftFeedback_*”\AppxMetadata
5. Run “Add-AppxPackage -DisableDevelopmentMode -Register AppxBundleManifest.xml