How do you await long running process from ASPNET CORE SignalR Hub - asp.net-core

I have an application flow where:
Clientside Javascript triggers signalR Hub
Asynchronous Call is made
for long running operation
When operation is complete Clientside JavaScript is notified by signalR
I had assumed this would be as simple as:
Server Side:
public async void SendMessage()
{
await Task.Delay(1000);
Clients.All.SendAsync("ReceiveMessage");
}
Client Side:
var hub = new signalR.HubConnectionBuilder().withUrl('/disposeBugHub').build();
this.hub.on("ReceiveMessage", function () {
alert('Message Received');
});
this.hub.start()
.then(function () {
hub.invoke("SendMessage");
})
.catch(function (err) {
return console.error(err.toString());
});
However the Clients.All.SendAsync("ReceiveMessage"); call always throws a System.ObjectDisposedException: 'Cannot access a disposed object.' Exception.
This appears to be expected behavoir and not a bug, so my question is how do i programmatically acheive the desired workflow? I assume there must be a well known pattern to acheive this but I cant find it online.

First of all , remove void method for long running process. use Task return type method .
Try this.
public async Task SendMessage()
{
await Task.Delay(10000);
Clients.All.SendAsync("ReceiveMessage");
}

Related

Vue2 Composition Api - How do I fetch data from api?

I am using Vue2.6 with composition api.
I need to reroute to different pages depends on an api response.
Can someone please guide me, please?
I tried using onBeforeMount but it renders the UI elements then rerouted to the corresponding page to the api response..so I can see a flash of the wrong UI..
setup() {
const myData = 'myData';
onBeforeMount(async () => {
try {
const results = await fetchData();
// do reroute depends on results response
}
} catch (err) {
console.log(err);
}
});
return {
myData,
};
I also tried adding async in the setup method but it errored saying my ref variables "Property or method "myData" is not defined on the instance but referenced during render."
async setup() {
const myData = 'myData';
onMounted(async () => {
try {
const results = await fetchData();
// do reroute depends on results response
}
} catch (err) {
console.log(err);
}
});
return {
myData,
};
It looks like you're trying to handle routing (re-routing) dynamically from inside a component. I can't see the rest of the apps, so can't speak to the validity of such a solution, but would like you dissuade you from doing that. routing logic should, IMO, not be handled in a component. The components should mostly just handle the template and user interaction. By the time you're rendering a component, that API should have been resolved already.
I would recommend to resolve the API response before the route is
even completed. You or use a navigationGuard to resolve the API during the route execution. This functionality is asynchronous, so you can await the response before proceeding.
Alternatively, if you really want to handle it in the component, you will have that delay while the API is resolving, but you can implement some loader animation to improve the experience.

How to stream a video between two clients using SignalR (.NET Core 3.0)?

I'm trying to create an application that records a video with a RaspberryPi camera and streams that video to a web browser.
I've managed to establish the individual Client->Server streaming and Server->Client streaming connections using the JavaScript SignalR libraries and now I need to somehow connect those two functions on the server in order to send a chunk of data to the browser as soon as it is recieved from the RaspberryPi.
These are the two examples from the Microsoft’s documentation:
public async Task UploadStream(IAsyncEnumerable<string> stream)
{
await foreach (var item in stream)
{
// Here I need to send the chunk (item) to the browser
Debug.WriteLine(item);
}
}
public async IAsyncEnumerable<string> DownloadStream(CancellationToken cancellationToken)
{
while (true) // While there is data to stream
{
yield return "data"; // Return chunks to the browser
}
}
Those examples are written using C# 8.0 IAsyncEnumerable and I would like to use them this way.
I tried to use a Queue to store data (enqueue on upload and dequeue on download) but the memory between the two connected clients is not shared.
Also, I don't know if I can use the method Clients.Others.SendAsync() because it is used to invoke a method from a normal SignalR app and in the documentation for the JavaScript library it uses the method stream().
connection.start().then(() => {
console.log("connected");
connection.stream("DownloadStream").subscribe({
next: item => {
console.log(item);
},
complete: () => {
console.log("completed");
},
error: err => {
console.log(err);
}
});
});

How to do error handling with bloc pattern in flutter?

Imagine I'm using a bloc to handle a network request. If the request fails, the way to handle the failure would be different depending on the platform. On my web app, I would like to redirect the user to an error page while on my IOS app I would like to show a dialog.
As bloc should only be used and shared to handle the business logic, and the error handling part has nothing to do with the business logic, we should ask the UI part to take care of the error handling.
The UI can send error callback to the bloc and the bloc will run it when an error happens. We can also handle the error in a platform-specific way by sending different callbacks in different platforms.
Then there come my two questions:
Is there a more appropriate way to do this?
How to send the callback to the bloc?
In flutter, we only have access to bloc after the initState life cycle method(for we get bloc from builder context, which only comes after initState). Then we can only send callback in the build method.
In this way, we will repetitively send callback to bloc every time rebuilding happens(these repetitions make no sense).
With react, such one-time initialization could be done in life cycles such as componentDidMount.
In flutter how do we reach the goal of running these initialization only once?
This is how we handle it in my team:
First we build our main page (The navigation root) like this:
#override
Widget build(BuildContext context) {
return BlocBuilder<SuspectEvent, SuspectState>(
bloc: _bloc,
builder: (context, state) {
if (state.cameras.isEmpty) _bloc.dispatch(GetCamerasEvent());
if (!_isExceptionHandled) {
_shouldHandleException(
hasException: state.hasException,
handleException: state.handleException);
}
return Scaffold(
...
We declare the _shouldHandleException like this (still on the main page):
_shouldHandleException(
{#required bool hasException, #required Exception handleException}) {
if (hasException) {
if (handleException is AuthenticationException) {
_isExceptionHandled = true;
SchedulerBinding.instance.addPostFrameCallback((_) async {
InfoDialog.showMessage(
context: context,
infoDialogType: DialogType.error,
text: 'Please, do your login again.',
title: 'Session expired')
.then((val) {
Navigator.popUntil(context, ModalRoute.withName('/'));
this._showLogin();
});
});
} else if (handleException is BusinessException) {
_isExceptionHandled = true;
SchedulerBinding.instance.addPostFrameCallback((_) async {
InfoDialog.showMessage(
context: context,
infoDialogType: DialogType.alert,
text: handleException.toString(),
title: 'Verify your fields')
.then((val) {
_bloc.dispatch(CleanExceptionEvent());
_isExceptionHandled = false;
});
});
} else {
_isExceptionHandled = true;
SchedulerBinding.instance.addPostFrameCallback((_) async {
InfoDialog.showMessage(
context: context,
infoDialogType: DialogType.error,
text: handleException.toString(),
title: 'Error on request')
.then((val) {
_bloc.dispatch(CleanExceptionEvent());
_isExceptionHandled = false;
});
});
}
}
}
On our block we have:
#override
Stream<SuspectState> mapEventToState(SuspectEvent event) async* {
try {
if (event is GetCamerasEvent) {
... //(our logic)
yield (SuspectState.newValue(state: currentState)
..cameras = _cameras
..suspects = _suspects);
}
... //(other events)
} catch (error) {
yield (SuspectState.newValue(state: currentState)
..hasException = true
..handleException = error);
}
}
In our error handling (on main page) the InfoDialog is just a showDialog (from Flutter) and it gets on top of any route. So the alert just needed to be called on the root route.
You can access the BLoC in the initState method if you wrap it in a scheduleMicrotask method, so that it runs after the initState method completed:
#override
void initState() {
super.initState();
// Do initialization here.
scheduleMicrotask(() {
// Do stuff that uses the BLoC here.
});
}
You can also check out this answer to a different question outlining the Simple BLoC pattern, which just calls asynchronous methods directly on the BLoC instead of putting events into sinks.
That would allow code like this:
Future<void> login() {
try {
// Do the network stuff, like logging the user in or whatever.
Bloc.of(context).login(userController.text, emailController.text);
} on ServerNotReachableException {
// Redirect the user, display a prompt or change this
// widget's state to display an error. It's up to you.
}
}
You can use superEnum package to create states and events for a Bloc.(and here you will declare a state for the Error by doing this :
#Data(fields: [DataField<Error>('error')])
OrderLoadingFailedState,
(If anyone need an example of how to use it, please tell me i will show you an example)

rxjs - handling async errors in finally

I'm interested in invoking an async function when I unsubscribe from an observable, however, I'm unable to handle the errors in a synchronized way once unsubscribing, for example I have the following piece of code, and the error isn't catched.
const observable = Observable.of(true).finally( async () => {
throw new Error('what');
});
try {
observable.subscribe().unsubscribe();
} catch (e) {
console.log('We did not capture this');
}
What are the possible ways of handling async errors in finally?
I think there're two problems:
Calling unsubscribe() won't do anything because of(true) sends next and complete notification immediately on subscription. So when you call unsubscribe() yourself you're already unsubscribed because of the complete notification. Then the actual call unsubscribe() won't do anything in this case.
When you mark a method as async it means you're in fact returning a Promise that is rejected. But .finally is expecting a function that it invokes and does nothing with it's return value. When you mark it as async it returns a Promise but nobody listens to it.
If you want to catch the error you will have to remove the async keyword,
const observable = Observable.of(true).finally(() => {
throw new Error('what');
});
with async it will turn it into an asynchronous function and won't be caught. What you are doing now is similar to
try {
setTimeout(()=> {throw new Error('what');},10)
} catch (e) {
console.log('We did not capture this');
}
Throw will excuted in the eventloop instead of the main thread.

Jasmine testing AngularJS $http

I'm trying to test a REST API wrapped in an AngularJS service using Jasmine async testing. I know, I should be, and am, running tests on the API on the server, and using a mock $httpBackend to test the service, but I'd still like to know how to do async tests where I need to.
The problem I'm having is my deferreds (returned by $http) never seem to resolve. Going past the service and trying to simply use $http has the same problem. What am I doing wrong here:
describe('Jasmine + Angular + $http', function() {
var injector;
beforeEach(function() {
injector = angular.injector(['ng']);
});
it('can be tested', function() {
injector.invoke(function($http) {
var complete = false;
runs(function() {
$http.get('...').then(function(res) {
complete = true;
});
});
waitsFor(function() {
return complete;
}, 'query to complete', 5000);
runs(function() {
expect(complete).toEqual(true);
});
});
});
});
(I'm using grunt-contrib-jasmine to run the tests)
The HTTP requests will not fire unless you call $httpBackend.flush().
More information can be found here: http://docs.angularjs.org/api/ngMock.$httpBackend