Archiv der Kategorie: .NET

Exception „No web service found at:“ with /js or /jsdebug

With „ASP.NET 2.0 AJAX Extensions 1.0“ (I love this name with two version numbers inside…) you can request a JavaScript Proxy for a web service with appending /js or /jsdebug to the web service path, like „http://my-server/my-service.asmx/js“.

When I deployed my application I’ve got the InvalidOperationException „No web service found at:“. When I called the web service without /js or /jsdebug the web service worked (at least the generated forms in the Browser where shown).

I’ve spent a lot of time ‚til I recognized the File my-service.asmx was not included in the deploy package. So without the /js appended the my-service.asmx file was not needed, the code from the compiled dll was executed directly, but when I added /js it needed the file.

Conclusion:
Don’t forget to add the .asmx files in the deployment of your ASP.NET AJAX Project!

Verhalten des System.Timers.Timer bei neusetzen des Intervalles

Nur als kleine Gedächnisstütze für mich:

Wenn man beim System.Timers.Timer das Interval neu setzt, wird dieses nach dem nächsten Auslösen des Timers verwendet:

Kurzer Bespielcode:

using System;
public class MyClass
{
  private static System.Timers.Timer timer = new System.Timers.Timer();

  public static void Main()
  {
    timer.Elapsed += new System.Timers.ElapsedEventHandler(OnTimer);
    timer.Interval = 3000;
    timer.Start();

    System.Threading.Thread.Sleep(7000);
    timer.Interval = 2000;

    Console.ReadLine();
  }

  protected static void OnTimer(object sender, System.Timers.ElapsedEventArgs e)
  {
    Console.WriteLine(DateTime.Now.ToString());
  }
}

Ambiguous match found nach Publishen der ASP.NET Webseite

Beim Entwickeln einer Web-Anwendung bin ich über eine Eigenart von ASP.NET 2.0 gestolpert. Beim Testen auf dem lokalen Rechner lief alles wie gewünscht. Nach dem Publishen der Webseite erhielt ich dann aber den Fehler:

Ambiguous match found

Als Fehlerzeile war die erste Zeile mit der

@Page

-Direktive angegeben.

Nach einigem Suchen im Internet fand ich bei Peter Johnson den Blog-Eintrag Ambiguous match found, der mich auf das Problem aufmerksam machte:

Innerhalb meiner Seite hatte ich ein hiddenField mit der ID

MyIdentifier

und eine

protected

Member-Variable mit dem Namen

myIdentifier

. Diese beiden Variablen lebten während dem Entwickeln in friedlicher Koexistenz, nach dem Publizieren der Webseite vertrugen sie sich aber nicht mehr. Leider liefert das Visual Studio weder während des Entwickelns noch beim Publizieren irgendwelche Warnungen oder Fehler. Nachdem ich

myIdentifier

in

theIdentifier

umbenannt hatte lief dann alles reibunglos.

Fehlendes ImageUrl bei asp:image erzeugt unerwünschtem Seitenaufruf

Stundenlang habe ich gesucht, bis ich den Fehler gefunden habe:

Unter bestimmten Umständen wurde meine Seite bei einem Postback erneut aufgerufen, aber mit

IsPostBack == false

. Bis ich dann herausgefunden habe, dass es am

asp:image

Tag liegt. Wenn das Attribut

ImageUrl

nicht angegeben wird, wird das Tag image mit einem leeren

src

Attribut erzeugt, was den Browser dazu veranlasst, als Bild die Seite laden zu wollen. Dies erzeugte den nicht gewünschten Seitenaufruf. Nachdem ich ein leeres Bild als Standardbild angegeben habe, passiert dieser unerwünschte Seitenaufruf nicht mehr.

ScottGu’s Blog : „Atlas“ 1.0 Naming and Roadmap

Wie Scott Guthrie in seinem Blog schreibt, erhält Atlas mit der Version 1.0, die Ende Jahr erscheinen soll, einen ofiziellen Namen (bzw. drei):

  • Die Client-seitige Javascript Bibliothek wird zu Microsoft AJAX Library
  • Die Server-seitige Bibliothek (UpdatePanel) wird zu ASP.NET 2.0 Ajax Extensions
  • Das jetztige Atlas Control Toolkit wird zu ASP.NET Ajax Control Toolkit

Mit der Version 1.0 wird es auch ofiziellen Support für die Bibliotheken geben.

NCover: Attribute-Based Type Exclusion, but the wrong way round…

With Version 1.5.2, NCover introduced a Attribute-Based Type Exclusion. It works, but not as I expected.

I normally use NCover together with my test code. When I have a class to test that throws an exception the test function is not covered completely.
Code Coverage without excluded Attribute
The function BarTest() is only covered to 67% as the end of the function is not executed because the Exception was thrown. I was waiting for the possibility to define that a function does not need to be covered with adding an attribute to the Function. But when I add an Attribute to the function and define this Attribute to be excluded (with //ea AttributeName), the Function is not covered.
Code Coverage with excluded Attribute
So instead of having a higher code coverage I have a lower code coverage. Not the result I was expecting from this feature.
But maybe this will change in the future. I keep watching…

Code Coverage mit NCover und NCoverExplorer

Grant Drake hat sein Tool NCoverExplorer vorgestellt. NCoverExplorer ist ein Tool, um die Reports von NCover komfortabel zu begutachten.Wer NCoverBrowser von Jeff Key kennt wird sich damit sofort zurechtfinden, da NCoverExplorer nach dem Vorbild von NCoverBrowser erstellt wurde. Grant betrieb diesen Aufwand, da der Sourcecode von NCoverBrowser nicht frei verfügbar ist (wegen komerzieller Komponenten, die eingesetzt wurden).
NCoverExplorer wird mit TestDriven.NET ausgeliefert, benötigt aber noch NCover. Dann kann aber direkt aus der Entwicklungsumgebung (auch aus Visual C# Express Edition) der Coverage Report geöffnet werden:
Code Coverage mit TestDriven.NET

Fallstricke bei NMock mit Klassen

Mit NMock können Attrappen (Mock Objetcs) für Klassen und Interfaces erzeugt werden. Ein deutscher Artikel über NMock ist beim dot.net magazin online abrufbar. Falls Mock Objects für Klassen erstellt werden, müssen einige Dinge beachtet werden.

1. Funktionen und Properties müssen virtual sein
Funktionen und Properties, welche durch das Mock Object ersetzt werden sollen, müssen als

virtual

deklariert werden, da NMock diese sonst nicht überschreiben kann.
NMock wirft normalerweise eine Exception, falls man eine Funktion mit

Expect()

,

ExpectAndReturn()

,

ExpectAndThrow()

oder

ExpectNoCall()

konfiguriert und diese nicht virtual ist.
Wenn aber ein Property nicht als

virtual

deklariert wird und mit

SetupResult()

das Verhalten überschrieben werden soll, wird keine Exception generiert. Es wird aber beim Aufrufen des Properties der Code der zu ersetzenden Klasse aufgerufen:

public class ClassToMock
{
public string Bar
{
get { return "bar"; }
}
}
[TestFixture]
public class TestingClass
{
[Test]
public void MockTest()
{
DynamicMock mock = new DynamicMock(typeof(ClassToMock));
mock.SetupResult("Bar", "foo");
ClassToMock c = (ClassToMock)mock.MockInstance;
Assert.AreEqual("foo", c.Bar);
}
}

Dieser Test schlägt Fehl, da immer noch der Code der Klasse

ClassToMock

aufgerufen wird und somit der Text „bar“ und nicht der Text „foo“, wie eigentlich erwartet, zurückgegeben wird. Sobald das Property als

virtual

deklariert wird, kann der Test erfolgreich ausgeführt werden.

Nicht virtuelle Funktionen können auch nicht durch den „Strict“ Mechanismus überwacht werden. Folgender Test sollte eigentlich fehlschlagen, da es nicht erwartet wird, dass die Funktion

Foo()

aufgerufen wird (kein

Expect...()

). Der Test wird aber erfolgreich ausgeführt.

public class ClassToMock
{
public void Foo() {}
}
[TestFixture]
public class TestingClass
{
[Test]
public void MockTest()
{
DynamicMock mock = new DynamicMock(typeof(ClassToMock));
mock.Strict = true;
ClassToMock c = (ClassToMock)mock.MockInstance;
c.Foo();
mock.Verify();
}
}

Sobald man Foo als

virtual

deklariert schlägt der Test wie erwartet fehl.

2. Wenn Strict benutzt wird muss Finalize als expected angegeben werden
Durch setzten des Attributes

Strict

auf

true

wird NMock angewiesen, dass alle Funktionsaufrufe vorher angemeldet werden müssen. Das gilt auch für den Destruktor, der am Ende der Lebenszeit des Objekts aufgerufen wird.
Da der Destruktor (Methode

Finalize()

) durch den Garbage Collector aufgerufen wird hat man keine Kontrolle darüber, wann der Destruktor aufgerufen wird. Bei den Tests wird er normalerweise nach den eigentlichen Tests aufgerufen. Deshalb wird eine fehlende Finalize-Anmeldung auch nicht als fehlschlagender Test gewertet, es wird aber eine Fehlermeldung auf die Error-Konsole ausgegeben, was nicht sehr Vertrauenserweckend wirkt.
Wenn man die Tests mit NUnit-GUI oder mit dem Visual-Studio-PlugIn TestDriven.NET ausführt ist diese Fehlermeldung nicht sichtbar.
Folgender Code erzeugt die Fehlermeldung:

public class ClassToMock
{
}
[TestFixture]
public class TestingClass
{
[Test]
public void MockTest()
{
DynamicMock mock = new DynamicMock(typeof(ClassToMock));
mock.Strict = true;
ClassToMock c = (ClassToMock)mock.MockInstance;
mock.Verify();
}
}

Wenn der Test mit nunit-console aufgerufen wird, wird am Schluss folgende Fehlermeldung ausgegeben:

Unbehandelte Ausnahme: NMock.VerifyException: Finalize() called too many times
expected:0
but was:<1>
at NMock.Mock.Invoke(String methodName, Object[] args)
at ProxyClassToMock.Finalize()

Wenn man nun aber am Anfang des Codes folgende Zeile einfügt:

mock.Expect("Finalize");

reklamiert die Funktion

Verify()

zu Recht, da der Destruktor zu diesem Zeitpunkt noch nicht aufgerufen wurde.
Man kann die oben angegebene Zeile aber nach den letzten

Verify()

-Aufruf setzten. Dadurch wird das Mock Object auf den Destruktor vorbereitet, welcher dann am Ende des Testes aufgerufen wird. Dieser Code kann z.B. in den

[TearDown]

-Bereich eines Testes geschrieben werden.

Owen Rogers, auch bekannt unter dem Namen exortech, hat diesen Bug auch entdeckt und in seinem Blog beschrieben. Scheinbar hat aber diese Änderung den Weg in den Code von NMock nicht gefunden…

Mit .NET den Fuss wegschiessen und es nicht bemerken

Mit C war es einfach, sich selber in den Fuss zu schiessen. Durch C++ wurde es erschwert, aber nicht verhindert, und es hatte schwerwiegendere Konsequenzen.

Mit C# / .NET ist es noch schwieriger, sich selbst in den Fuss zu schiessen; und falls man es tut, merkt man es noch nicht einmal.

So habe ich heute ein Problem behoben, bei welchem der Speicherverbrauch einer Anwendung (eines Services, um genauer zu sein) stetig zunahm. Natürlich habe ich als erstes überprüft, ob irgend ein Array immer mehr Objekte beinhaltet. Aber erst, als ich mit einer Testversion des ANTS Profiler von Red Gate Software den Speicherverbrauch überprüft hatte, entdeckte ich das Problem:

Ich erzeugte ein Objekt, welches einen Timer besitzt. Die Referenz auf dieses Objekt speichere ich in einem Array. Wenn das Objekt nicht mehr benötigt wird, lösche ich das Objekt aus dem Array. Es existiert keine Referenz mehr auf dieses Objekt, somit kann der Garbage Collector das Objekt aus dem Speicher entfernen.

Oder auch nicht …

Da das Objekt einen Timer enthält und sich beim Timer mit einem Eventhandler angemeldet hat, referenzieren sich das Objekt und der Timer gegenseitig. Der Garbage Collector kann somit weder Timer noch Objekt löschen und sie schwirren bis in alle Ewigkeit (bzw. bis zum Programmende), losgelöst von allen anderen Objekten, im Speicher umher.

Solche oder ähnliche Probleme entdeckte man unter Visual C++ um einiges schneller, da die Entwicklungsumgebung nicht freigegebene Objekte reklamierte, wenn man die Anwendung im Debug Modus testete. Mit dem Garbage Collector ist diese Kontrollinstanz entfernt worden, was mich in diesem Fall einiges an Zeit gekostet hat.

Ich könnte mir vorstellen, dass noch in einigen .NET-Anwendungen solche Speicherfresser vorhanden sind. Wenn es normale Desktop-Anwendungen sind, wird dies auch nicht so bald auffallen, da diese Anwendungen normalerweise nicht über mehrere Tage laufen.

Es wäre schön, wenn man, falls man dies möchte, den Garbage Collector ausschalten könnte und den Speicher wieder selber kontrollieren könnte, da man nur so sicher sein kann, dass auch alle Objekte wieder gelöscht werden. Ob mein Wunsch in einer der nächsten .NET-Framework-Versionen wohl erfüllt wird? Wahrscheinlich eher nicht.

Somit werden wir wohl mit ungeladenen Waffen umherlaufen müssen und immer wieder kontrollieren, ob wir nicht ein Loch in unserem Fuss haben…

Workaround bei RAS via .NET unter Windows Server 2003

Für eine .NET-Anwendung musste ich via Modem eine Internet-Verbindung aufbauen. Dabei wird das Password Authentication Protocol (PAP) verwendet. Da in der Version 1.1 des .NET-Frameworks keine RAS-Unterstützung vorhenden ist, schrieb ich eine .NET-Wrapper-Klasse, die den Zugriff auf die benötigten Funktionen des RASAPI (rasapi32.dll) ermöglicht.
Die Anwendung funktionierte auf meinem Entwicklungssystem (Windows 2000 Server) ohne Probleme. Als die Anwendung dann auf dem Einsatzsystem unter Windows 2003 Server getestet wurde, klappte die Einwahl nicht. Untersuchungen mit dem PortMon von sysinternals ergaben, dass alles funktionierte, aber bei der Authentifizierung zwar der Benutzername, nicht aber das Passwort übermittelt wurde.
Verschiedene Versuche mit .NET und C++-Programmen zeigten dann, dass die Einwahl mittels RASAPI mit einem C++-Programm funktioniert, mit einem .NET-Programm das Passwort aber jedesmal fehlte.
Recherchen im Internet ergaben keine Lösung, nicht einmal ein vorheriges Auftreten dieses Problems konnte ich finden.
Um dieses Problem in den Griff zu bekommen, schrieb ich eine kleine DLL, welche die RASAPI-Aufrufe kapselt. Die Funktionen dieser DLL sehen wie folgt aus:


#define DLLEXPORT extern "C" __declspec(dllexport)
DLLEXPORT DWORD Dial(
    LPCSTR connectionName,
    LPCSTR number,
    LPCSTR username,
    LPCSTR password,
    LPVOID callback,
    LPHRASCONN hRas)
 {
    RASDIALEXTENSIONS rasDialExtensions;
    rasDialExtensions.dwSize = sizeof(RASDIALEXTENSIONS);
    rasDialExtensions.hwndParent = NULL;
    rasDialExtensions.reserved = 0;
    rasDialExtensions.dwfOptions = 0;
    RASDIALPARAMS rasDialParams;
    rasDialParams.dwSize = sizeof(RASDIALPARAMS);
    strcpy(rasDialParams.szPhoneNumber, number);
    strcpy(rasDialParams.szCallbackNumber, "");
    strcpy(rasDialParams.szDomain, "");
    strcpy(rasDialParams.szEntryName, connectionName);
    strcpy(rasDialParams.szUserName, username);
    strcpy(rasDialParams.szPassword, password);
    DWORD res = RasDial(&rasDialExtensions, 0, &rasDialParams, 1, callback, hRas);
    return res;
}
DLLEXPORT DWORD HangUp(HRASCONN hRas)
{
    return RasHangUp(hRas);
}
DLLEXPORT DWORD GetConnectStatus(
    HRASCONN hrasconn,
    LPRASCONNSTATUS lprasconnstatus)
{
    return RasGetConnectStatus(hrasconn, lprasconnstatus);
}

Die .NET-Anwendung ruft nun die Funktionen dieser DLL auf. Dadurch funktioniert die Einwahl nun auch unter Windows 2003 Server.