Angular Controller as

Wenn ich so darüber nachdenke, gibt es in Angular.js mindestens immer zwei Möglichkeiten eine Aufgabe zu lösen. Eine Direktive kann eine Funktion oder ein JSON Objekt zurück liefern. Ein Service kann eine Factory oder ein Service sein. Alles leicht verwirrend. In die Kategorie, muss das auch noch sein, fällt die Scope Freiheit eines Controllers.

Üblicherweise wird jedem Controller der Scope per Dependency Injection üergeben und damit eine Singleton Instand erstellt. Es gib auch eine Alternative Variante ohne $Scope.

Dabei wird eine “Controller as” Syntax eingesetzt, die sozusagen einen Alias erzeugt, der dann als Objektträger herhalten muss.

   1:    <div ng-controller="mycontroller as hannes">
   2:          {{hannes.count}}
   3:          <button ng-click="hannes.click()">plus</button>
   4:    </div>
   5:    

 

Der zugehörige Controller wird aber gänzlich anders codiert. Zunächst wird wie üblich die App als Modul erzeugt und darin ein Controller definiert, der auf eine Controller Methode verweist. Dieser ist allerdings Parameterlos. Die Propertys des Scopes werden per this erzeugt. Das fühlt sich aus C# Entwicklersicht fast natürlich an.

JavaScript typisch, wird per Prototype das bestehende Objekt um eine Funktion, hier click, erweitert.

   1:    var App = angular.module('App', []);
   2:    App.controller('mycontroller', myfunction)
   3:    function myfunction() {
   4:        this.count = 0;
   5:      };
   6:   myfunction.prototype.click= function () {
   7:       this.count++;
   8:     };

 

Auch wenn ich die Variante mit $scope optisch weniger gelungen finde, werde ich wegen der Konsistenz innerhalb von Angular diesen Weg nicht verwenden.

Fileupload mit Angular.JS

Eigentlich müsste es heißen: Der Kampf des Dateiuploads gegen Angular.JS. Das Framework unterstützt uns dabei in keiner Weise. Wir müssen es an mehreren Stellen sogar austricksen.

Die Programmieraufgabe umfasst folgende Bauteile

  • Webservice, der die Datei entgegennimmt und speichert
  • HTML User Interface mit Input Type File
  • JavaScript Code für Ajax und Scope

Um volle Kontrolle über die hochgeladenen Dateien zu haben, bezüglich Typ, Größe oder Speicherort, bleibt nur ein Service am Web Server. In diesem Fall ist dieser per ASP.NET Web API und VB.NET erstellt.

   1:  Public Class fileuploadController
   2:      Inherits ApiController
   3:      <HttpPost>
   4:      Public Sub UploadFile()
   5:          If HttpContext.Current.Request.Files.AllKeys.Any() Then
   6:              Dim f = HttpContext.Current.
Request.Files("UploadedImage")
   7:              If f IsNot Nothing Then
   8:                  Dim fp = Path.Combine(
HttpContext.Current.Server.MapPath("~/upload"),
Path.GetFileName(f.FileName))
   9:                  f.SaveAs(fp)
  10:              End If
  11:          End If
  12:      End Sub
  13:  End Class

 

Das UX wird aus optischen Gründen auf einen Button reduziert, der den Betriebssystem-File-Open-Dialog öffnet und beim Schließen den Upload anstößt. Dadurch können wir das UI per Bootstrap schöner stylen, da die leere Textbox des Type File nicht angezeigt wird. Allerdings muss ein zweiter Button das Click Event des Fileupload-Buttons auslösen.

Das geschieht in Zeile 6 und zeigt bereits eine Reihe von Problemen mit Angular. Eigentlich sollte der Onclick-Handler nicht im HTML-Code auftauchen. Die Alternative ist eine Angular-Direktive mit rund 15 Zeilen Code, mit der dann auch noch ein zusätzliches Attribut in das Input-Element gelegt werden muss. Das ist mehr Aufwand und der Code ist dadurch leider auch nicht besser lesbar.

Außerdem kann man leider die DOM-Selector-Eigenschaften nicht verwenden, auch wenn ein angular.element(‘#id’) uns dies glauben lässt. Damit dieser Code funktioniert, muss erst noch jQuery eingebunden werden, da JQLite diese Selektoren nicht beinhaltet. Dann könnte man aber gleich $(‘#id’) nutzen. Es drängt sich die Frage auf, warum das komplette jQuery Framework überhaupt Overhead erzeugen soll, wenn es die benötigte JavaScript-Funktion bereits gibt. Also wird hier document.querySelector verwendet, um das Click Event durchzureichen.

Noch schlimmer wird es, wenn man aus normalem JavaScript auf das Angular Framework zugreifen muss. Dies ist hier nötig, da die Value Eigenschaft, die die ausgewählte Datei beinhaltet, nicht per ng-model bindbar ist. Auch für dieses Problem lässt sich eine umfangreiche Direktive schreiben oder einfach das OnChange-Event auf ein Event des $Scope durchreichen (Zeile 4)

   1:  <body  ng-controller="uploadController">
   2:      <label for="fileUpload"></label>
   3:      Select <input id="fileUpload" type="file" 
style="visibility:hidden;position:absolute;top:0;left:0"
   4:         onchange="angular.element(this).scope().onChange(this)" />
   5:   
   6:      <input id="btnUploadFile" type="button" value="Upload File"
onclick="document.querySelector( '#fileUpload').click()" />
   7:  <img id="progress" src="img/upload-indicator.gif"  ng-show="warten"/>

 

image

Zusätzlich wird noch ein animiertes GIF verwendet, das die Uploadaktivität visualisiert und hier durchaus elegant per ng-show an die Eigenschaft Warten des $scope gebunden wird.

Zuletzt muss noch die eigentliche Upload-Logik geschrieben werden. Dazu wird ein Formdata-Objekt erstellt und mit den Datei-Meta-Informationen und dem Stream gefüllt. Dann wird per $http der Post auf den Upload-Service angestoßen. Um ein Problem mit Angular und dem benötigten Multipart-Formdata-Header zu umgehen, fügen wir in Zeile 17-19 Workaround-Code ein.

   1:  var app=angular.module("App",[])
   2:    .controller("uploadController", function ($scope,$http) {
   3:     $scope.warten = false;
   4:     $scope.onChange = function (dateien)
   5:      {
   6:        $scope.warten = true;
   7:        var file = dateien.files[0];
   8:        var data = new FormData();
   9:        data.append('UploadedImage', file);
  10:        data.append('FileName', file.name);
  11:        data.append('fileSize', file.size);
  12:        data.append('fileType', file.type);
  13:   
  14:        $http
  15:          .post('/api/fileupload/uploadfile' , data,
  16:                 {
  17:                   transformRequest: angular.identity,
  18:                    headers: { 'Content-Type': undefined }
  19:                  })
  20:                 .success(function () {
  21:                       $scope.warten = false;
  22:                  })
  23:                 .error(function () {
  24:                       $scope.warten = false;
  25:                          alert('error');
  26:                      });
  27:              };
  28:     });

Angular 1.3 .. is not a function

Die seit wenigen Tagen verfügbare Angular.JS 1.3 Version bringt eine Reihe von Änderungen mit sich, die auch in fertigen Anwendungen negative Auswirkungen haben können. In diesem Fall war es ein Code-Beispiel für meinen Angular.JS Workshop auf der ADC X in Mannheim und Wien. Das JavaScript Code Beispiel für eine globale Controller-Funktion liefert eine Fehlermeldung im F12 Browser Developer Tools Command Window.

image

Nicht, dass man damit schönen Code schreibt. Aber vorher funktionierte

   1:   <div ng-app="" ng-controller="personController">
   2:      ...
   3:  </div>
   4:   
   5:  <script>
   6:          function personController($scope) {
   7:              $scope.firstName = "John",
   8:              $scope.lastName = "Doe"
   9:          }
  10:  </script>

 

Jetzt muss ein Angular Modul und ein expliziter Controller darin definiert werden.

Wer unbedingt das alte Verhalten benötigt, kann dies erzwingen per

   1:   angular.module('myModule').config(['$controllerProvider',
function ($controllerProvider) {
   2:                        $controllerProvider.allowGlobals();
   3:          }]);

 

Das ist allerdings nicht besonders sinnvoll. Wenn man schon ein Modul anlegt, ist der Controller auch gleich gecoded.

Das Problem entsteht für Anfänger, da viele Blogs diesen kurzen Weg für Angular Sample Code verwenden.

Angular.JS heilt Krebs

Das könnte man meinen, wenn man Tweets verfolgt. Kaum ein kritisches Wort. Und wenn doch wird der Urheber einmal durch Twitter rauf und runter gebasht. So passiert einem Daniel Steigerwald. Man könnte durchaus Parallelen zu Religionsgemeinschaften ziehen. Kritiker werden darauf verwiesen, eben nicht richtig verstanden zu haben. Der einzige wahre Weg führt durch dieses Framework (und noch 42 andere, die man braucht).

Für die Schmerzen, die man jetzt gerade erleidet, wird eine wundersame Zukunft, oder Jungfrauen, in einem späteren Leben versprochen.

Lebensrealitäten werden ausgeblendet und Absurdes zu State of the Art erhoben. Gestandene Mittfünfziger erscheinen plötzlich im Superman-Kostüm an ihrem Arbeitsplatz in der Bank, um auch mal ein Superheld zu sein.

image

Natürlich nicht zu vergessen, dieser sagenhafte neue Sprit, der 20 Cent mehr kostet an der Tankstelle und bis zu 10% mehr Leistung verspricht. Super Power eben- darunter machen wir's nicht mehr.

image

Derartig ausgestattet flutscht das Leben quasi von selbst. Und den letzten Zweiflern wird der Open Source-getränkte Lappen in den Mund gestopft, um sie zum verstummen zu bringen.

Um meinen Sohn zu zitieren:

“Hallo! gehts noch?”

Angular ist eines von hunderten JavaScript Frameworks. JavaScript ist per Definition immer quelloffen. Es stammt von einer Firma, die ihr Geld mit dem Ausspähen Deiner Daten verdient. Angular ist bei weitem nicht perfekt und fehlerfrei. Es ist schwer zu erlernen. Die meisten Entwickler schreiben nach wie vor keinen Testcode. Es existiert schlicht, weil JavaScript eine geliebte Missgeburt aus dem letzten Jahrtausend ist.

Trotzdem kommt man bei Web-Applikationen nicht daran vorbei. Es wird breit verwendet und hat wirklich nette Funktionen. Aber Angular.js ist keine Silberkugel und heilt keine Programmiererkrankheit. Persönlich finde ich auch, dass man mit Angular und JavaScript im allgemeinen besonders scheusslichen unlesbaren Code schreiben kann.

Wer sich für das Thema aus einer weniger glorifizierenden Perspektive interessiert, kann an meinen beiden ganztägigen #ADCX Hands On Angular Workshops teilnehmen. Am 20.Oktober in Frankenthal http://adc.ms und am 6. November in Wien http://adc.ms/wien.

Lync Meetings, Headset Mikro funktioniert nicht

Seit einiger Zeit fällt mir in Online-Meetings Tastaturgeklappere auf. Ein untrügliches Indiz, dass der andere Teilnehmer kein Headset benutzt. Ich frage dann gerne nach und immer öfter versichert mir mein Gegenüber ein Headset zu benutzen. Für einen schnellen Test kann man das Lautsprechersymbol aus der Taskleiste nutzen. Einfach mit einem harten Gegenstand auf das Mikrophon klopfen.

lync1

In meinem Fall gab es keinen erkennbaren Ausschlag. Also ist das interne Mikro an, obwohl ein Headset in der 3,5 mm-Kombibuchse steckt.

Für einen Aufnahme-Qualitätstest nehme ich gerne die Windows 8.1 (vormals METRO) Recorder App.

lync2

Auch diese liefert das meinen Erwartungen entsprechende Ergebnis.

Die Lösung findet sich an der Buchse. Immer mehr Notebooks haben nur eine Buchse für Kopfhörer und Mikrofon. Ein entsprechender Adapter leitet dann zwar auf einen vierpoligen 3,5 mm-Klinkenstecker, aber das Notebook scheint das Device so nicht richtig zu erkennen.

Die Lösung bringt die Installation des Realtek HD Audio Managers, der beim Einstecken eines Gerätes einen zusätzlichen Dialog anbietet. Um auf den Dialog zu kommen, klicken Sie einfach rechts die Buchse an. Dann wählen Sie: Audioeingang, Mikrofoneingang, Kopfhörer, Audioausgang oder Headset.

lync3

Ich suche noch die Option, beziehungsweise das Setting ohne die Zusatzsoftware.

Globale Variable mit Angular

Ein Set von global gültigen Variablen legt man gerne in eine static oder shared Klasse. Wer bereits mit ASP.NET gearbeitet hat, weiß, dass dies nicht so einfach ist. Noch schwieriger wird es mit Masterpage (Webforms) oder Layout Pages (MVC). In beiden Fällen werden aus mindestens zwei Dateien Vorlagen und Inhalt vereint.

Es erscheint wenig sinnvoll einen einzigen Controller in die Masterpage zu legen und darin die Logik aller Content Pages einzubauen. Folglich wird man einen  Controller für die Masterpage definieren und einen für jede Content Page. Bleibt die Frage, wie diese miteinander sprechen und wie man Daten im Sinne einer globalen Variable austauscht. Wir müssen den Connection-Status einer Websockets-Verbindung an vielen Stellen prüfen können.

Um gemeinsam nutzbaren Code und damit auch eine Variable anlegen zu können, sieht Angular einen Service, Factory oder Provider vor.  Die Factory erhält den Namen global und liefert den Connection Status mit false zurück.

   1:  App.factory('global', function () {
   2:      var global = {
   3:          isConnected: false
   4:      };
   5:      return global;
   6:  });

Per Depedency Injection wird die Factory im Controller verfügbar. Konkret werden alle Objekte in den Funktonsaufruf des Controllers gepackt, hier der $scope für das Viewmodel und die Factory global.

Die Direktive enthält noch eine Command-Funktion, um den Status ändern zu können.

   1:  app.controller('contentController', function($scope,global) {
   2:    $scope.isConnected = global.isConnected;
   3:    $scope.connect=function()
   4:    {
   5:   
   6:      $scope.isConnected=global.isConnected=true;;
   7:    }
   8:  });

Gebunden wird das Angular typischerweise im HTML Code, in der Praxis in der Content Page.

   1:  <div  ng-controller="contentController">
   2:    Content Page Dingens
   3:      
   4:       <p>Status {{isConnected}}!</p>
   5:       <button ng-click="connect()">connect</button>
   6:    </div>

 

In der Masterpage wird ein eigener Controller definiert, der z.B. Elemente im Menübereich bindbar hält. Der JavaScript-Code unterscheidet sich auf den ersten Blick nicht vom vorigen Codebeispiel. Allerdings muss der Main Controller die Änderung in global.isConnected auch mitgeteilt bekommen. Dazu kann man einen Watcher einrichten, der die Änderung des Wertes zum Anlass nimmt, den lokalen Scope neu zu schreiben.

   1:  app.controller('mainController', function($scope,global) {
   2:   
   3:    $scope.isConnected = global.isConnected;
   4:    
   5:     $scope.$watch(
   6:          function(){ return global.isConnected},
   7:   
   8:          function(newVal) {
   9:            $scope.isConnected = newVal;
  10:          }
  11:        )
  12:   
  13:  });

 

Das lauffähige Sample.

Sehr oft sieht man für diese Aufgabenstellung auch einen Ansatz mit Broadcast auf den $rootscope. Dieser erfordert deutlich mehr Code. Alternativ kann man auch im $Rootscope eine Variable nutzen, davor wird jedoch allerseits gewarnt.

Windows METRO Apps Cross Plattform mit WinJS 3

Cross-Plattform-Development ist in aller Munde. Nun steigt Microsoft mit der neuen WinJS-Bibliothek in den JavaScript-Framework-Markt ein. Konkurrenz sind JQuery Mobile, AngularJS oder auch Kendo UI. Das sind alles Themen auf der ADCX Konferenz.

Der erste Blick auf die JavaScript für Windows Bibliothek per Nuget Download in Visual Studio 2013 eröffnet ernüchternde Downloadzahlen: nur 167 in der ersten Woche.

winjs

In meinem Fall hat zwar die Installation scheinbar geklappt, es waren aber keine neuen CSS oder JS Dateien im Projekt angelegt. Erst der Manuelle Download des Zip mit anschließendem Entpacken in das Web-Projekt, hat den Start ermöglicht. Nebenbei bemerkt: der Download kommt von Amazon Web Services.

winjs2:

Die Zeiten haben sich geändert, gut finde ich das nur bedingt.

Als nächstes wird WinJS in eine ASPX Seite eingebunden. Es gibt zwei Design-Varianten: dark und light.

   1:   <link href="Content/ui-dark.css" rel="stylesheet" />
   2:   <script src="Scripts/WinJS.js"></script>

Die Controls sind in einzelne JavaScript-Dateien aufgeteilt und sozusagen modularisiert. Für den Einstieg packe ich die komplette Bibliothek, immerhin unkomprimiert 3,7 MB groß, in die HTML-Seite.

Ans Ende des HTML-Blockes wird der Script Block gecoded. Die Daten kommen per Json aus einem REST ASP.NET Web Api Service und beinhalten die Northwind Customer Daten. Nachdem die Daten geladen sind, wird die Bindung aktualisiert (Zeile 11)

   1:<script>
   2: WinJS.Application.onready = function () {
   3:              var options = {
   4:                  url: '/api/kunden',
   5:                  type: 'GET'
   6:              };
   7: WinJS.xhr(options).done(
   8:      function (result) {
   9:     callback(result.responseText, result.status);
  10:                      //Binding aktivieren
  11:     WinJS.UI.processAll();
  12:                  },
  13:                  function (result) {
  14:                      callback(null, result.status);
  15:                  }
  16:              );
  17:          };
  18:   
  19: WinJS.Application.start();
  20:   
  21: function callback(responseText, status) {
  22:     if (status === 200) {
  23:       var customers = JSON.parse(responseText);
  24:       WinJS.Namespace.define("DataExample", {
  25:          data: new WinJS.Binding.List(customers)
  26:                  });
  27:   
  28:              } else {
  29:   
  30:              }
  31:          }
  32:   </script>

Etwas ungewohnt werden die empfangenen JSON Array Daten in eine bindbare WinJS Liste umgewandelt. Der Namensraum kombiniert mit dem Eigenschaftsnamen, ergänzt um das Schlüsselwort dataSource, wird im HTML Code per win-Options an das Listview Control gebunden.

Das eigentliche Itemtemplate wird extern, das heißt außerhalb des Listview DIV extra deklariert. Die Feldbindung erfolgt per win-bind Attribut.

   1:  <h1>Demo METRO Liste</h1>
   2:  <div class="customerTemplate" 
data-win-control="WinJS.Binding.Template" style="display: none;">
   3:      <div style="height:80px">
   4:          <h4 data-win-bind="textContent: CompanyName"></h4>
   5:     </div>
   6:  </div>
   7:  <div id="basicListView"
   8:     data-win-control="WinJS.UI.ListView"
   9:     data-win-options="{ itemDataSource : DataExample.data.dataSource, 
  10:     itemTemplate: select('.customerTemplate') }">
  11:  </div>

Richtig erstaunlich wird es im Browser. Die Website sieht aus wie Windows 8.1 und ist genauso bedienbar. In dem Fall mit einem horizontalen Scrolling.

image

Nun hat Microsoft also mit WinJs 3.0 Yet Another JavaScript Library für mobile Clients. Stellt sich die Frage: wer braucht das, wer wird das nutzen? Konzeptionell ist die Architektur gut gelungen und einfach nutzbar. Die für JavaScript typischen Probleme mit Asynchronität finden sich auch hier. Hervorzuheben ist, dass der HTML Code und vermutlich weitestgehend der JS Code aus einer nativen Windows 8.1 Anwendung übernommen werden können. Die HTML5 Designer aus Blend sind das Beste, was der Markt aktuell an WYSIWYG zum Thema zu bieten hat. In der Gesamtbetrachtung bin ich vorsichtig optimistisch, würde aber aktuell kein Projekt damit starten.

Mehr zum Thema Cross Plattform auf der ADCX in Mannheim und Wien.

Deklarativ Variablen in HTML mit Angular zuweisen

Ein Angular Controller enthält eine Liste, die per Ajax Call aus einem Json REST Service gefüllt werden. Dies ist eine Eigenschaft des Scope.

   1:  App.controller('chatController', function ($scope, $http) {
   2:      $scope.chatUsers = [];

In diesem Array sollen bestimmte Werte vorbelegt werden. Als Anwendungsfall kann man eine Dropdownliste nennen. In dieser sollen drei Länder vorbelegt werden, wie z.B. DE, AT und CH. Der Rest soll aus der Datenbank bzw. aus einem Service zusätzlich angezeigt werden. Wie kann man also Werte vorbelegen? Natürlich geht das im JavaScript Code des Controllers. Auch ein Modul lässt sich dafür einsetzen. Wenn man allerdings mit ng-init arbeitet, kann man mit einem deklarativen Ansatz dem UI Designer die Entscheidung überlassen. Das Array wird im HTML Code befüllt.

   1:   <div ng-controller="chatController">
   2:          <div ng-init="chatUsers = [ {name:'Franz',  userId:700000},
   3:                                   {name:'Laura',  userId:1300000}
   4:      ]"></div>

Diesen Ansatz könnte man durchaus auch für Offline-Daten nehmen, um etwas anzuzeigen, solange der Service nicht bereit ist.

JQuery Mobile mit ASP.NET Intro Video

Auf der #NRWConf hatte ich das Vergnügen einen Vortrag zu mobile Web Apps mit JQM und ASP.NET halten zu dürfen. Morgens beim ersten eMail check, der Schock. Nach Bluescreen bootet mein Fujitsu T904  bzw. Windows 8.1 in den Reparaturscreen. Ein kryptische Meldung, das kein lokaler Admin Account vorhanden ist, verhindert sämtliche Reparatur Optionen. Auch ein Boot von USB ist nicht möglich.

Dank wunderbarer Hilfe von Daniel Fischer, Melanie Eibl und Stefan Lange konnte ich die Session um 14:50 doch noch durchführen. Letztlich dank eines brandneuen Surface Pro 3 time sponsored bei Stefan. Sozusagen Live Test von Tastatur und Device. Wer schon mal Ad Hoc eine andere Umgebung und vor allem anderes Keyboard benutzt hat, kennt die Stolpersteine.

Also aus vier per Facebook angekündigten Slides wurden 0. Beinahe 60 Minuten Live Coding mit Northwind rund um

  • Visual Studio 2013 und ASP.NET Webforms Modelbinding
  • Jquery Mobile Einrichten und Funktion Intro
  • Listen- Relationen, Suchen, nummerisches Paging- Forward Paging
  • Formular- Anzeigen Edit

Die Stimmung im Publikum war großartig. Szenenapplaus, aktives mitgehen- You Rock!

Um allen das Nachlesen, bzw. nachschauen zu ermöglichen share ich hier das Recording, das ich Tage vorher aufgenommen habe, um das Timing der Demos zu testen. (Produktwarnung VB.NET und Webforms Zwinkerndes Smiley)

Übrigens Vimeo weil ohne störende Werbung und fern der Datenkrake Google.

Die NRWConf war ein Superevent, sehr persönlich und engagiert. Danke an Daniel Fischer und Kostja Klein. Es war mir eine Ehre.

SQL Azure Login failed for User..

This session has been assigned a tracing ID of 'f3254cff-3db3-46e6-b6d1-22cb065a633f'.  Provide this tracing ID to customer support when you need assistance.

Beinahe hätte ich zwei Azure-Experten verschlissen. Eine Website mit EF6 und ASP.NET Identity soll auf einer Azure Website gehostet werden. Visual Studio 2013 bietet dazu die Möglichkeit per veröffentlichen die Website per Click zu Azure zu deployen.

Die Anwendung wurde lokal entwickelt mit einer lokalen SQL Server Datenbank. Das Kopieren und Migrieren der Anwendung war nicht ganz problemlos. Es mussten sogar Änderungen am Code durchgeführt werden, weil die JSON Serialisierung auf Azure plötzlich nicht mehr funktionierte. (Lacyloading).

Die Connection Strings werden in der Datei Web.Config gespeichert. Hier wurden der Default Wert (DefaultConnection) für die ASP.NET Identity Datenbank (von mir aspnetdb genannt) und einer für die Anwendungsdaten. Der Wizard tauscht dann beim Deployment die Connection Strings aus. Damit nutzt man zur Laufzeit die Echtdaten. Das Schema ergänzt den Wert von Inital Catalog Projektnamen um _db für defaultConnection.

image

Als nächstes sollte man sich auf der bei Azure gehosteten Website anmelden können. Das funktionierte aber nicht. Seltsamerweise zeigt das Portal keine fehlerhaften Anmeldeversuche.

image

Die Lösung fand sich im Visual Studio Server Explorer. Dort gibt es einen Bereich für Azure, in dem man auch die Einstellungen zur Website vornehmen kann. Dort findet sich noch einmal die Verbindungszeichenfolge DefaultConnection mit dem Verweis auf die vorhin erwähnte Datenbank xxxx_db. Geändert, gespeichert - geht.

image

Training, Schulung, JavaScript, HTML, CSS, Dot Net, Asp Net

Month List