NULL-Referenz im Web-API-Aufruf

Dies ist eine seltsame Sache, und ich kann nicht verstehen, was hier vor sich geht. Ich habe ein Web-API-Projekt, bei dem in einem Controller ein Aufruf einer bestimmten Methode eine Funktion in einem Dienst aufruft, der wie folgt aussieht:

    public MyClassBase GetThing(Guid id)
    {
        if (cache.ContainsKey(id))
        {
            return cache[id];
        }
        else
        {
            var type = typeof(MyClassBase).Assembly.GetTypes().FirstOrDefault
            (
                t => t.IsClass &&
                    t.Namespace == typeof(MyClassBase).Namespace + ".Foo" &&
                    t.IsSubclassOf(typeof(MyClassBase)) &&
                    (t.GetCustomAttribute<MyIdAttribute>()).GUID == id
            );
            if (type != null)
            {
                System.Diagnostics.Debug.WriteLine(string.Format("Cache null: {0}",cache == null));
                var param = (MyClassBase)Activator.CreateInstance(type, userService);
                cache[id] = param;
                return param;
            }
            return null;
        }
    }

cache ist einfach ein Wörterbuch:

 protected Dictionary<Guid, MyClassBase> cache { get; set; }

Das wird im Konstruktor für diese Klasse erzeugt:

 cache = new Dictionary<Guid, MyClassBase>();

Dies funktioniert in 99,9% der Fälle einwandfrei, aber gelegentlich wird beim ersten Start der App die erste Anforderung a ausgelöstNullReferenceException - und der seltsame Teil ist, es behauptet, dass die Quelle diese Zeile ist:

cache[id] = param;

Aber die Sache ist, wenncache ist null (was nicht sein kann, es wurde im Konstruktor gesetzt, es ist privat, und dies ist dasnur Methode, die es sogar berührt), dann sollte es geworfen haben:

if (cache.ContainsKey(id))

und wennid war null, dann hätte ich eine schlechte Anfrage von der API bekommen, weil sie nicht zugeordnet werden würde, plus meiner Linq-Anweisung, um den Typ mit einer Übereinstimmung zu erhaltenGUID hätte null zurückgegeben, auf die ich auch teste. Und wennparam ist null, es sollte keine Rolle spielen, Sie können einen Wörterbucheintrag auf einen Nullwert setzen.

Es fühlt sich an wie ein Rennzustand, bei dem etwas nicht vollständig initialisiert ist, aber ich kann nicht sehen, woher es kommt oder wie man sich dagegen verteidigen kann.

Hier ist ein Beispiel dafür, was es (gelegentlich) auslöst (als JSON, weil die Web-API json zurückgibt und ich aktuell habe, dass es mir Fehlermeldungen zurückspuckt, damit ich sie finden kann):

{
    "message": "An error has occurred.",
    "exceptionMessage": "Object reference not set to an instance of an object.",
    "exceptionType": "System.NullReferenceException",
    "stackTrace": "   at System.Collections.Generic.Dictionary`2.Insert(TKey key, 
        TValue value, Boolean add)\r\n   at 
        System.Collections.Generic.Dictionary`2.set_Item(TKey key, TValue value)\r\n   
        at MyNameSpace.Services.MyFinderService.GetThing(Guid id) in
        c:\\...\\MyFinderService.cs:line 85\r\n   
        at MyNameSpace.Controllers.MyController.GetMyParameters(Guid id) in 
        c:\\...\\Controllers\\MyController.cs:line 28\r\n   at 
        lambda_method(Closure , Object , Object[] )\r\n   at
        System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ActionExecutor.<>c__DisplayClass13.
        <GetExecutor>b__c(Object instance, Object[] methodParameters)\r\n   at
        System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ActionExecutor.Execute(Object instance,
        Object[] arguments)\r\n   at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.
        <>c__DisplayClass5.<ExecuteAsync>b__4()\r\n   at
        System.Threading.Tasks.TaskHelpers.RunSynchronously[TResult](Func`1 func, CancellationToken
        cancellationToken)"
}

Dasline 85 ist die Linie, die ich oben hervorgehoben habe.

Es ist eine ArtHeisenbug, aber ich habe es gerade geschafft, es zum Werfen zu bringen, indem ich dies sofort auf meiner HTML-Seite tat (natürlich funktionierte es ein zweites Mal einwandfrei):

    $.ajax({
        url: "@Url.Content("~/api/MyController/GetMyParameters")",
    data: { id: '124c5a71-65b7-4abd-97c0-f5a7cf1c4150' },
    type: "GET"
    }).done(function () { console.log("done"); }).fail(function () { console.log("failed") });

    $.ajax({
        url: "@Url.Content("~/api/MyController/GetMyParameters")",
        data: { id: '7cc9d80c-e7c7-4205-9b0d-4e6cb8adbb57' },
    type: "GET"
    }).done(function () { console.log("done"); }).fail(function () { console.log("failed") });

    $.ajax({
        url: "@Url.Content("~/api/MyController/GetMyParameters")",
        data: { id: '1ea79444-5fae-429c-aabd-2075d95a1032' },
    type: "GET"
    }).done(function () { console.log("done"); }).fail(function () { console.log("failed") });

    $.ajax({
        url: "@Url.Content("~/api/MyController/GetMyParameters")",
        data: { id: 'cede07f3-4180-44fe-843b-f0132e3ccebe' },
    type: "GET"
    }).done(function() { console.log("done"); }).fail(function() { console.log("failed")});

Vier Anfragen in schneller Folge abfeuern, zwei davon scheiterten am selben Punkt, aber hier wird es wirklich wütend, es bricht in dieser Zeile ab und ich kann darüber schwebencache, id undparam in Visual Studio und sehen ihren aktuellen Wert undkeiner von ihnen sind null! Und in jedem Fallcache hat einCount von 1, was auch seltsam ist, weil dies ein Singleton sein soll, der von Ninject erstellt wurde, also fange ich an, etwas zu vermuten, das mit Ninject zu tun hat.

Als Referenz registriere ich in NinjectWebCommon diesen Dienst folgendermaßen:

kernel.Bind<Services.IMyFinderService>().To<Services.MyFinderService>().InSingletonScope();

Hinweis: Ich habe das auch gepostetSingletons und Rennbedingungen weil ich nicht 100% sicher bin, dass das Problem nicht Ninject ist, aber ich wollte diese Frage nicht mit zu vielen Vermutungen darüber verwechseln, waskönnte die Ursache sein.

Antworten auf die Frage(1)

Ihre Antwort auf die Frage