C# SignalR IProgress werkt niet?

Pagina: 1
Acties:

Vraag


Acties:
  • +1 Henk 'm!

  • ZaZ
  • Registratie: Oktober 2002
  • Laatst online: 27-09 00:06

ZaZ

Tweakers abonnee

Topicstarter
Mijn vraag
...
In een project (console .net 4.52) waarin SignalR prima werkt moet ik wat toevoegen.
Ik heb een een long running Task<T> die ik als parameter een IProgress<T> meegeef.
Dit zou volgens de documentatie gewoon moeten kunnen.
Ik krijg alleen de meest wazige errors en het wil gewoon niet werken.
In de console applicatie knalt ie er zonder melding uit. Het proces wordt gewoon afgesloten op het moment dat ik de hub invoke. Er valt kennelijk niets te catchen.


Wat ik al gevonden of geprobeerd heb
...
Om het probleem te isoleren heb ik het na proberen te bootsen in een los project.
Ik heb een server hub gemaakt in een console applicatie met OWIN
Een client in een console applicatie
Een client in een Winforms applicatie

In de console client knalt ie eruit als ik een await gebruik op de hub.invoke
Doe ik echter een .Wait() of een .Result() op een functie uit de hub (eentje zonder Progress<T>) dan werkt het wel.
In de Winforms applicatie heb ik dit probleem niet. Dit zal waarschijnlijk iets te maken hebben met de synchronizationcontext vermoed ik.
Maar dit is niet het ergste probleem, wilde het alleen even opnoemen voor als iemand daar een makkelijk antwoord op heeft.


In mijn hub heb ik de volgende testmethode:
C#:
1
2
3
4
5
6
7
8
9
10
        public async Task<string> DoLongRunningThing(IProgress<int> progress)
        {
            for (int i = 0; i <= 100; i += 5)
            {
                await Task.Delay(200);
                progress.Report(i);
            }

            return "Job complete!";
        }


Die roep ik in mijn client als volgt aan:
C#:
1
2
            IProgress<int> progress = new Progress<int>(value=>Console.WriteLine(value));
            var result = await HubProxy.Invoke<string>("DoLongRunningThing", progress);

Die knalt er vrijwel direct uit met:
code:
1
2
System.InvalidOperationException: ''DoLongRunningThing' method could not be resolved. Potential candidates are: 
DoLongRunningThing():Task`1'

Alsof ie geen parameter verwacht.

Verander ik in de hub de IProgress parameter met een int (waar ik vervolgens niets mee doe) dan krijg ik gewoon netjes "Job complete" terug na even gewacht te hebben. Het is dus echt een probleem van de parameter.

Puur om te testen heb ik de functie eens aangepast om na de IProgress ook nog een dummy int mee te geven. Ik was benieuwd waar ie dan mee zou komen.

Dus

C#:
1
2
3
4
5
6
7
8
9
10
        public async Task<string> DoLongRunningThing(IProgress<int> progress,int dummy)
        {
            for (int i = 0; i <= 100; i += 5)
            {
                await Task.Delay(200);
                progress.Report(i);
            }

            return "Job complete!";
        }

En in de client
C#:
1
2
            IProgress<int> progress = new Progress<int>(value=>Console.WriteLine(value));
            var result = await HubProxy.Invoke<string>("DoLongRunningThing", progress,1234);

Dan klapt ie eruit met
code:
1
2
3
System.InvalidOperationException
  HResult=0x80131509
  Message=Could not create an instance of type System.IProgress`1[System.Int32]. Type is an interface or abstract class and cannot be instantiated. Path '', line 1, position 2.


Dat zegt me al een stuk meer. Hij ziet ineens de parameters (wel heel vreemd natuurlijk) en de melding is volgens mij van de deserializer die een instance probeert te maken van een interface (wat natuurlijk niet gaat)
Maar goed, voordat ik hier allemaal trucs op ga verzinnen krijg ik het vermoeden dat ik ergens gewoon iets niet goed aan het doen ben.
Zijn er hier mensen die met IProgress werken in SignalR en met een .Net client?

Ik heb voor hulpvaardigen die zelf een kijkje willen nemen wel een testproject.
Liever heb ik gewoon een duw of stomp in de goede richting :+

Lekker op de bank

Beste antwoord (via ZaZ op 17-05-2018 15:58)


  • Xiphalon
  • Registratie: Juni 2001
  • Laatst online: 14:48
Ik krijg een exceptie op 'DoLongRunningThing' cannot be resolved.

Het lijkt erop dat er een DolongRunningThing wordt verwacht met void als return type en de huidige doet een Task retourneren.

Tip:
Ik denk dat je de exceptie kan afvangen door van het huidige AppDomain de UnhandledException eventhandler te gebruiken.

De exceptie:
code:
1
2
3
4
5
6
7
8
9
10
System.InvalidOperationException: 'DoLongRunningThing' method could not be resolved. Potential candidates are: \nDoLongRunningThing():Task`1
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
   at ClientConsole.Program.<Main>d__0.MoveNext() in C:\\Projects\\Bla\\ClientConsole\\Program.cs:line 29
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
   at ClientConsole.Program.<Main>(String[] args)


Edit:
Ik heb even gekeken in de tests van SignalR, en daar doen ze het aanroepen anders. Het blijkt dat je het zo moet doen:
code:
1
            var tot = await HubProxy.Invoke<string, int>("DoLongRunningThing", p => Console.WriteLine(p));

Dus niet een IProgress<int> als parameter, maar de juiste overload gebruiken:
code:
1
        Task<TResult> Invoke<TResult, TProgress>(string method, Action<TProgress> onProgress, params object[] args);


Is dit helder @ZaZ ?

[ Voor 74% gewijzigd door Xiphalon op 17-05-2018 13:04 ]

Alle reacties


Acties:
  • 0 Henk 'm!

  • vickypollard
  • Registratie: Juni 2009
  • Laatst online: 13:08
Ik heb geen oplossing voor je probleem (te lang geen C# en geen IDE meer), maar misschien is het nog handig om te verduidelijken of het probleem met IProgress bij zowel de console als de Winforms client optreedt.

Acties:
  • 0 Henk 'm!

  • ZaZ
  • Registratie: Oktober 2002
  • Laatst online: 27-09 00:06

ZaZ

Tweakers abonnee

Topicstarter
vickypollard schreef op donderdag 17 mei 2018 @ 09:15:
Ik heb geen oplossing voor je probleem (te lang geen C# en geen IDE meer), maar misschien is het nog handig om te verduidelijken of het probleem met IProgress bij zowel de console als de Winforms client optreedt.
Ja het werkt bij beide niet, met als verschil dat een await in de console er ook nog eens voor zorgt dat ie crasht zonder een foutmelding die niet af te vangen is met een try/catch.

Lekker op de bank


Acties:
  • 0 Henk 'm!

  • ThomasG
  • Registratie: Juni 2006
  • Laatst online: 23-09 14:00
ZaZ schreef op donderdag 17 mei 2018 @ 09:53:
[...]

Ja het werkt bij beide niet, met als verschil dat een await in de console er ook nog eens voor zorgt dat ie crasht zonder een foutmelding die niet af te vangen is met een try/catch.
Je kunt het wel afvangen met een try/catch, alleen niet binnen diezelfde method. In plaats van dat je de code in je Main methode hebt, moet je die verplaatsen naar een andere method, bijv. Foo. en dan aanroepen als:
code:
1
2
3
4
5
try {
    await Foo();
} catch (Exception ex) {
    //...
}
Waarbij foo dan de await invoke e.d. aanroept.

Acties:
  • 0 Henk 'm!

  • ZaZ
  • Registratie: Oktober 2002
  • Laatst online: 27-09 00:06

ZaZ

Tweakers abonnee

Topicstarter
ThomasG schreef op donderdag 17 mei 2018 @ 10:19:
[...]
Je kunt het wel afvangen met een try/catch, alleen niet binnen diezelfde method. In plaats van dat je de code in je Main methode hebt, moet je die verplaatsen naar een andere method, bijv. Foo. en dan aanroepen als:
code:
1
2
3
4
5
try {
    await Foo();
} catch (Exception ex) {
    //...
}
Waarbij foo dan de await invoke e.d. aanroept.
Doel je op mijn test of gewoon het bekende probleem dat je geen await kan gebruiken in een void main?
Je kan namelijk tegenwoordig prima "void main" veranderen in "async Task main" en dan gewoon await gebruiken. Moet je alleen in je project zetten dat ie bijv C# 7.2 gebruikt.

Lekker op de bank


Acties:
  • +1 Henk 'm!

  • ThomasG
  • Registratie: Juni 2006
  • Laatst online: 23-09 14:00
ZaZ schreef op donderdag 17 mei 2018 @ 10:29:
[...]

Doel je op mijn test of gewoon het bekende probleem dat je geen await kan gebruiken in een void main?
Je kan namelijk tegenwoordig prima "void main" veranderen in "async Task main" en dan gewoon await gebruiken. Moet je alleen in je project zetten dat ie bijv C# 7.2 gebruikt.
Nee, waar ik op doel is dat je exception bubbled naar buiten je methode. Maar omdat dit nu toevallig je main methode is, en je geen try/catch om je main kunt zetten, kun je het in dit geval niet catchen; maar zal de applicatie crashen. Door het te verplaatsen naar een andere methode, kun je er wel een try/catch die het met await aanroept omheen zetten, en kun je hem wel catchen.

Acties:
  • Beste antwoord
  • +1 Henk 'm!

  • Xiphalon
  • Registratie: Juni 2001
  • Laatst online: 14:48
Ik krijg een exceptie op 'DoLongRunningThing' cannot be resolved.

Het lijkt erop dat er een DolongRunningThing wordt verwacht met void als return type en de huidige doet een Task retourneren.

Tip:
Ik denk dat je de exceptie kan afvangen door van het huidige AppDomain de UnhandledException eventhandler te gebruiken.

De exceptie:
code:
1
2
3
4
5
6
7
8
9
10
System.InvalidOperationException: 'DoLongRunningThing' method could not be resolved. Potential candidates are: \nDoLongRunningThing():Task`1
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
   at ClientConsole.Program.<Main>d__0.MoveNext() in C:\\Projects\\Bla\\ClientConsole\\Program.cs:line 29
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
   at ClientConsole.Program.<Main>(String[] args)


Edit:
Ik heb even gekeken in de tests van SignalR, en daar doen ze het aanroepen anders. Het blijkt dat je het zo moet doen:
code:
1
            var tot = await HubProxy.Invoke<string, int>("DoLongRunningThing", p => Console.WriteLine(p));

Dus niet een IProgress<int> als parameter, maar de juiste overload gebruiken:
code:
1
        Task<TResult> Invoke<TResult, TProgress>(string method, Action<TProgress> onProgress, params object[] args);


Is dit helder @ZaZ ?

[ Voor 74% gewijzigd door Xiphalon op 17-05-2018 13:04 ]


Acties:
  • 0 Henk 'm!

  • ZaZ
  • Registratie: Oktober 2002
  • Laatst online: 27-09 00:06

ZaZ

Tweakers abonnee

Topicstarter
Ah de unit tests als documentatie gebruiken. Doe ik normaal gesproken alleen bij projecten zonder documentatie. Ik ga er vanavond mee spelen. Maar klinkt hoopvol, bedankt!

Lekker op de bank

Pagina: 1