Web Designer öffnet Projektdatei nicht

Click here for the English article.

Symptom

Soll beim Starten des Web Designers (Windows Desktop-Version) ein vorbelegtes Projekt geladen werden, gelingt das nicht und es wird stattdessen der Projekt-Assistent für ein neues Projekt gestartet.

Dabei kann aber das identische Projekt sowohl im Web Report Designer als auch im Web Report Viewer erfolgreich geladen und verwendet werden. In einer Debwin-Logdatei wird symptomatisch folgendes angezeigt:

...
[clsLoadManagerThread] RemoteRepository: LoadItem('repository://{XXXXXX-XXXX-XXXX-XXXX-XXXXXXXXX}') 
[clsLoadManagerThread] RemoteRepository: **Received 0 bytes**
=0;file 'repository://{XXXXXX-XXXX-XXXX-XXXX-XXXXXXXXX}' exists, and is empty. I assume I shall use this...
-22 (FFFFFFEA) ( **No project exists with the specified file name.** ) 
...

Ursache

In früheren Programmierbeispielen zeigen wir eine SQLite-basierte Repository-Implementierung mit synchronen E/A-Vorgängen.

Unter Verwendung von “reinem” Kestrel, also ohne einen Proxy für die Anfragen, kann es zu diesem Fehler kommen, da das .NET Core (Kestrel) Webhosting standardmäßig nur asynchrone Vorgänge (E/A) unterstützt (siehe
KestrelServerOptions.AllowSynchronousIO Eigenschaft).

Diese Einstellung ist bei Microsoft Standard, da sehr viele blockierende synchrone E/A-Vorgänge zu einem Ressourcenmangel im Threadpool führen können. Weitere Informationen unter Konfigurieren von Optionen für den ASP.NET Core-Kestrel-Webserver.

Lösung

Lösungsweg 1

Anpassen der Repository-Implementierung für LoadItem() auf asynchrone Anfragen - hier am mitgelieferten Beispiel SQLiteFileRepository.cs:

...
public void LoadItem(string itemID, Stream destinationStream, CancellationToken cancelToken)
 {
     object fileContent = _db.CreateCommand(
          "SELECT FileContent FROM RepoItems WHERE ItemID = @ItemID")
             .SetParameter("ItemID", itemID).ExecuteScalar();

     byte[] content = new byte[0];

     if (fileContent is byte[] byteContent)
     {
         content = byteContent;
     }

     destinationStream.Write(content, 0, content.Length);
}
...

ersetzt durch:

...
public void LoadItem(string itemID, Stream destinationStream, CancellationToken cancelToken)
 {
     object fileContent = _db.CreateCommand(
          "SELECT FileContent FROM RepoItems WHERE ItemID = @ItemID")
             .SetParameter("ItemID", itemID).ExecuteScalar();

     byte[] content = new byte[0];

     if (fileContent is byte[] byteContent)
     {
         content = byteContent;
     }

    destinationStream
    .WriteAsync(content, 0, content.Length, cancelToken)
    .GetAwaiter()
    .GetResult(); 
}
...

Lösungsweg 2

Alternativ kann man stattdessen die Anfragen über eine andere Webhosting-Umgebung laufen lassen, z. B. IIS (Internet Information Services) oder IIS Express. IIS verwaltet oft den Synchronisationskontext strenger, was manchmal Probleme mit nicht vollständig für asynchrone Operationen optimiertem Code “beheben” kann.

Lösungsweg 3

Eine weitere Möglichkeit ist es, Kestrel für synchrone E/A-Vorgänge zu konfigurieren. Das folgende Beispiel aktiviert synchrone E/A-Vorgänge:

builder.WebHost.ConfigureKestrel(serverOptions =>
{
serverOptions.AllowSynchronousIO = true;
});

Weitere Informationen unter Konfigurieren von Optionen für den ASP.NET Core-Kestrel-Webserver.

Achtung: Dies kann zu o. g. Ressourcenmangel führen!