I am working on a Blazor Server app over a SignalR connection with an ASP.NET Core API, this code works fine in WebAssembly but for some reason it doesn't works in Blazor Server app.
I suspect the problem is that StateHasChanged() isn't making effect, since Console.WriteLine($"Company Name: {item.CompanyName}, Volumn: {item.Volume}"); is printing in console but MarketData isn't updating UI at StateHasChanged().
For more context; full code is explained here:
https://www.webnethelper.com/2022/01/aspnet-core-6-signalr-creating-real.html
But I guess it's just a common fix; but I can't find the solution.
I've also tried with InvokeAsync(() => StateHasChanged()); as mentioned here in stackoverflow but It didnt work. Can anyone help me fix this issue?
private HubConnection? hubConn;
private string? ConnectionStatusMessage;
public List<Market> MarketData = new List<Market>();
public List<Market> MarketReceivedData = new List<Market>();
private List<string> xSource;
private List<int> ySource;
private List<object> source;
protected override async Task OnInitializedAsync()
{
xSource = new List<string>();
ySource = new List<int>();
source = new List<object>();
await service.GetMarketEndpoint();
await Start();
}
private async Task Start()
{
hubConn = new HubConnectionBuilder().WithUrl("https://localhost:7193/marketdata").Build();
await hubConn.StartAsync();
if(hubConn.State == HubConnectionState.Connected )
ConnectionStatusMessage = "Connection is established Successfully...";
else
ConnectionStatusMessage = "Connection is not established...";
}
private void MarketDataListener(string chartType)
{
hubConn.On<List<Market>>("SendMarketStatusData", async (data) =>
{
MarketData = new List<Market>();
foreach (var item in data)
{
Console.WriteLine($"Company Name: {item.CompanyName}, Volumn: {item.Volume}");
xSource.Add(item.CompanyName);
ySource.Add(item.Volume);
}
source.Add(ySource);
source.Add(xSource);
MarketData = data;
StateHasChanged();
await js.InvokeAsync<object>(chartType, source.ToArray());
xSource.Clear();
ySource.Clear();
});
}
private void ReceivedMarketDataListener()
{
hubConn.On<List<Market>>("CommunicateMarketData", (data) =>
{
MarketReceivedData = data;
StateHasChanged();
});
}
public async Task Dispose()
{
await hubConn.DisposeAsync();
}
async Task generateLineChartTask()
{
MarketDataListener("marketLineChart");
ReceivedMarketDataListener();
await service.GetMarketDataAsync();
}
async Task generateBarChartTask()
{
MarketDataListener("marketBarChart");
ReceivedMarketDataListener();
await service.GetMarketDataAsync();
}
The main difference here is that Blazor Serverside is multithreaded so the callbacks from the circuit will execute on a different Thread.
StateHasChanged() has to be executed on the main (UI) thread so call it like InvokeAsync(StateHasChanged), which is short for InvokeAsync(() => StateHasChanged()).
And be aware of other threading risks. Ie, don't share data like Lists between threads.
Related
my question is how to manage really large images on the web. Im using blazor, I need to send and recive Images of 100mb or more, And im using webshockets to send btwin the web and the server that send images. Im using base64 but its also slow.
this is how i send and i recive an image
public class WebshocketConections
{
public Uri _Uri = new Uri("ws://xxx.xxx.xxx.xxx:YYYY");
ClientWebSocket _cliente;
CancellationTokenSource cts = new();
public delegate void CheckNewMessage(string a);
public event CheckNewMessage? onMessage;
public WebshocketConections(ClientWebSocket client, Uri uri)
{
_cliente = client;
_Uri = uri;
}
public async Task Connect()
{
cts.CancelAfter(TimeSpan.FromSeconds(100));
await _cliente.ConnectAsync(_Uri, cts.Token);
await Echo(_cliente);
}
public async Task SendString(string message)
{
ArraySegment<byte> byteToSend = await Task.Run(() => { return new ArraySegment<byte>(Encoding.UTF8.GetBytes(message)); });
await _cliente.SendAsync(byteToSend, WebSocketMessageType.Text, true, cts.Token);
}
public async Task SendImage(byte[] Base64)
{
ArraySegment<byte> byteToSend = await Task.Run(() => { return new ArraySegment<byte>(Encoding.UTF8.GetBytes("img" + Convert.ToBase64String(Base64))); });
await _cliente.SendAsync(byteToSend, WebSocketMessageType.Binary, true, cts.Token);
}
public async Task<ClientWebSocket> GetClient()
{
return _cliente;
}
private async Task Echo(ClientWebSocket client)
{
var buffer = new byte[1024 * 1000000];
var receiveResult = await client.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
while (!receiveResult.CloseStatus.HasValue)
{
onMessage(Encoding.UTF8.GetString(buffer, 0, receiveResult.Count));
await client.SendAsync(
new ArraySegment<byte>(buffer, 0, receiveResult.Count),
receiveResult.MessageType,
receiveResult.EndOfMessage,
CancellationToken.None);
receiveResult = await client.ReceiveAsync(
new ArraySegment<byte>(buffer),
CancellationToken.None);
}
await client.CloseAsync(
receiveResult.CloseStatus.Value,
receiveResult.CloseStatusDescription,
CancellationToken.None);
}
}
Below is the exact code for my Azure Service Fabric Service Bus queue listener.
The problem I am having is that the _client.OnMessageAsync "takes a rest" when not used for a few minutes. It eventually wakes up and processes the queued messages, maybe within 5 minutes. messages are never dropped or deadletter'd, but this is unacceptable behavior. I tried tweaking message options, but nada..
Any ideas?
internal sealed class Core : StatelessService
{
readonly QueueClient _client;
readonly OnMessageOptions _msgOptions;
public Core(StatelessServiceContext context)
: base(context)
{
var connectionString = CloudConfigurationManager.GetSetting(Resources.ServiceBusConnectionString);
var inQueue = CloudConfigurationManager.GetSetting(Resources.InQueue);
var factory = MessagingFactory.CreateFromConnectionString(connectionString);
_client = factory.CreateQueueClient(inQueue, ReceiveMode.PeekLock);
_msgOptions = new OnMessageOptions
{
AutoComplete = false,
MaxConcurrentCalls = 5
};
}
protected override IEnumerable<ServiceInstanceListener> CreateServiceInstanceListeners()
{
return new ServiceInstanceListener[0];
}
protected override async Task RunAsync(CancellationToken cancellationToken)
{
await Task.Run(() =>
_client.OnMessageAsync(
async message =>
{
OnReceived(message);
await message.CompleteAsync();
}, _msgOptions),
cancellationToken);
}
private void OnReceived(BrokeredMessage brokeredMessage)
{
var message = brokeredMessage.GetBody<Message>();
ServiceEventSource.Current.ServiceMessage(Context, "Working-{0}", message.Transaction.Id);
var preocessor = new Processor();
preocessor.Process(message);
}
}
I just wrote a simple windows 8 form that post to web service api. It works fine. But my challenge is been able to determine when the post operation was a success and a failure. I dont know how to return a value cos aysnc Task is not allowing a return type.
//This class does the post to web service
public class B2cMobileuserService : IB2cMobileuserService
{
private string RegisterUserUrl = RestfulUrl.RegisterMobileUser;
private readonly HttpClient _client = new HttpClient();
public async Task RegisterMobileUser(B2cMobileuserView user)
{
var jsonString = Serialize(user);
var content = new StringContent(jsonString, Encoding.UTF8, "application/json");
var result = await _client.PostAsync(RegisterUserUrl, content);
}
}
//This class calls the one above
public class WebserviceProcessor
{
//declaring all the service objects that would be used
IB2cMobileuserService mobileuserService = null;
public WebserviceProcessor() {
mobileuserService = new B2cMobileuserService();
}
//This method is going to post values to the web serever
public async void RegisterUser(B2cMobileuserView mobileuser) {
mobileuserService.RegisterMobileUser(mobileuser);
}
}
//Then the code below is from my .xaml user interface that calls the class that sends to webservice
private void Button_Click(object sender, RoutedEventArgs e)
{
B2cMobileuserView user = new B2cMobileuserView();
user.Name = name.Text;
user.Email = email.Text;
user.PhoneType = "Windows Mobile";
user.BrowserType = "None";
user.CountryName = "Nigeria";
user.UserPhoneID = phone.Text;
Serviceprocessor.RegisterUser(user);
progressBar.Visibility = Visibility.Visible;
}
Please I dont know how to return a value cos when I try I get the error that says async method must be void.
I need to set a way to know when the post was a success based on the return value from the web service.
To ensure the POST was successful, call HttpResponseMessage.EnsureSuccessStatusCode:
public async Task RegisterMobileUser(B2cMobileuserView user)
{
var jsonString = Serialize(user);
var content = new StringContent(jsonString, Encoding.UTF8, "application/json");
var result = await _client.PostAsync(RegisterUserUrl, content);
result.EnsureSuccessStatusCode();
}
If you want to return a value, use a Task<T> return type instead of Task.
On a side note, avoid async void; use async Task instead of async void unless the compiler forces you to write async void:
//This method is going to post values to the web serever
public Task RegisterUser(B2cMobileuserView mobileuser) {
return mobileuserService.RegisterMobileUser(mobileuser);
}
Also, you should name your asynchronous methods ending in *Async:
//This method is going to post values to the web serever
public Task RegisterUserAsync(B2cMobileuserView mobileuser) {
return mobileuserService.RegisterMobileUserAsync(mobileuser);
}
You may find my async intro and MSDN article on async best practices helpful.
I have this first version of a class
public class GenerateAuthorisationWorkflows : IGenerateAuthorisationWorkflows
{
public IList<Guid> FromDtaObjects(IList<DtaObject> dtaObjects, Employee requestingEmployee)
{
foreach (var dtaObject in dtaObjects) { }
return new List<Guid>();
}
public IList<Guid> FromDtaObjects()
{
return new List<Guid>();
}
}
And the MSpec tests for it
public abstract class specification_for_generate_workflows : Specification<GenerateAuthorisationWorkflows>
{
protected static IWorkflowService workflowService;
Establish context = () => { workflowService = DependencyOf<IWorkflowService>(); };
}
[Subject(typeof(GenerateAuthorisationWorkflows))]
public class when_generate_workflows_is_called_with_a_dta_object_list_and_an_employee : specification_for_generate_workflows
{
static IList<Guid> result;
static IList<DtaObject> dtaObjects;
static Employee requestingEmployee;
Establish context = () =>
{
var mocks = new MockRepository();
var stubDtaObject1 = mocks.Stub<DtaObject>();
var stubDtaObject2 = mocks.Stub<DtaObject>();
var dtaObjectEnum = new List<DtaObject>{stubDtaObject1,stubDtaObject2}.GetEnumerator();
dtaObjects = mocks.Stub<IList<DtaObject>>();
dtaObjects.Stub(x => x.GetEnumerator()).Return(dtaObjectEnum).WhenCalled(x => x.ReturnValue = dtaObjectEnum);
requestingEmployee = mocks.Stub<Employee>();
mocks.ReplayAll();
};
Because of = () => result = subject.FromDtaObjects(dtaObjects, requestingEmployee);
It should_enumerate_the_dta_objects = () => dtaObjects.received(x=> x.GetEnumerator());
It should_call_workflow_host_helper = () => workflowService.AssertWasCalled(x => x.StartWorkflow());
}
With this configuration, my first test passes and my second test fails, as expected. I added a constructor to the class to inject the IWorkflowService.
public class GenerateAuthorisationWorkflows : IGenerateAuthorisationWorkflows
{
IWorkflowService _workflowService;
GenerateAuthorisationWorkflows(IWorkflowService workflowService)
{
_workflowService = workflowService;
}
public IList<Guid> FromDtaObjects(IList<DtaObject> dtaObjects, Employee requestingEmployee)
{
foreach (var dtaObject in dtaObjects)
{
Guid workflowKey = _workflowService.StartWorkflow();
}
return new List<Guid>();
}
public IList<Guid> FromDtaObjects()
{
return new List<Guid>();
}
}
Now, when I run the tests, they fail at the Because:
System.InvalidOperationException: Sequence contains no elements
at System.Linq.Enumerable.First(IEnumerable`1 source)
at MSpecTests.EmployeeRequestSystem.Tasks.Workflows.when_generate_workflows_is_called_with_a_dta_object_list_and_an_employee.<.ctor>b__4() in GenerateAuthorisationWorkflowsSpecs.cs: line 76
For clarity, line 76 above is:
Because of = () => result = subject.FromDtaObjects(dtaObjects, requestingEmployee);
I've tried tracing the problem but am having no luck. I have tried setting up a constructor that takes no arguments but it raises the same error. I have similar classes with IoC dependencies that work fine using MSpec/Rhino Mocks, where am I going wrong?
Castle Windsor requires a public constructor to instantiate a class. Adding public to the constructor allows correct operation.
public class GenerateAuthorisationWorkflows : IGenerateAuthorisationWorkflows
{
IWorkflowService _workflowService;
public GenerateAuthorisationWorkflows(IWorkflowService workflowService)
{
_workflowService = workflowService;
}
public IList<Guid> FromDtaObjects(IList<DtaObject> dtaObjects, Employee requestingEmployee)
{
foreach (var dtaObject in dtaObjects)
{
Guid workflowKey = _workflowService.StartWorkflow();
}
return new List<Guid>();
}
public IList<Guid> FromDtaObjects()
{
return new List<Guid>();
}
}
Rowan, looks like you answered your own question. It's good practice to explicitly state the access modifiers! By default, C# chooses private. These kinds of errors are easy to miss!
I can also see that your Establish block is too complicated. You're testing the implementation details and not the behavior. For example, you are
stubbing the GetEnumerator call that's implicitly made inside the foreach loop.
asserting that the workflow service was called only once
mixing MSpec automocking and your own local mocks
You're not actually testing that you got a GUID for every object in the input list. If I were you, I'd test the behavior like this...
public class GenerateAuthorisationWorkflows : IGenerateAuthorisationWorkflows
{
private readonly IWorkflowService _service;
public GenerateAuthorisationWorkflows(IWorkflowService service)
{
_service = service;
}
public List<Guid> FromDtaObjects(List<DtaObject> input, Employee requestor)
{
// I assume that the workflow service generates a new key
// per input object. So, let's pretend the call looks like
// this. Also using some LINQ to avoid the foreach or
// building up a local list.
input.Select(x => _service.StartWorkflow(requestor, x)).ToList();
}
}
[Subject(typeof(GenerateAuthorisationWorkflows))]
public class When_generating_authorisation_keys_for_this_input
: Specification<GenerateAuthorisationWorkflows>
{
private static IWorkflowService _service;
private static Employee _requestor = new Employee();
private static List<DtaObject> _input = new List<DtaObject>()
{
new DtaObject(),
new DtaObject(),
};
private static List<Guid> _expected = new List<Guid>()
{
Guid.NewGuid(),
Guid.NewGuid(),
};
private static List<Guid> _actual = new List<Guid>();
Establish context = () =>
{
// LINQ that takes each item from each list in a pair. So
// the service is stubbed to return a specific GUID per
// input DtaObject.
_input.Zip(_expected, (input, output) =>
{
DependencyOf<IWorkflowService>().Stub(x => x.StartWorkflow(_requestor, input)).Return(output);
});
};
Because of = () => _actual = Subject.FromDtaObjects(_input, _requestor);
// This should be an MSpec collection assertion that
// ensures that the contents of the collections are
// equivalent
It should_get_a_unique_key_per_input = _actual.ShouldEqual(_expected);
}
I am trying to implement the MVVM patter for my WP7 Silverlight app and I am running into a problem with the async JSON Rest call. I moved into my ViewModel class the following two methods that were on my WP7 app Page.
public void FetchGames()
{
ObservableCollection<Game> G = new ObservableCollection<Game>();
//REST call in here
var webClient = new WebClient();
Uri uri = new Uri("http://www.somewebsite.com/get/games/league/" + league);
webClient.OpenReadCompleted += new OpenReadCompletedEventHandler(OpenReadCompletedGames);
webClient.OpenReadAsync(uri);
}
private void OpenReadCompletedGames(object sender, OpenReadCompletedEventArgs e)
{
DataContractJsonSerializer ser = null;
ser = new DataContractJsonSerializer(typeof(ObservableCollection<Game>));
Games = ser.ReadObject(e.Result) as ObservableCollection<Game>;
this.IsDataLoaded = true;
}
Now the problem is that because it is an async call the following code does not work. The following code is on my app Page.
protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
{
base.OnNavigatedTo(e);
if (NavigationContext.QueryString.TryGetValue("league", out league))
{
try
{
App.gViewModel.league = league;
App.gViewModel.FetchGames();
if(App.gViewModel.IsDataLoaded)
{
lbTeams.ItemsSource = App.gViewModel.Games;
}
}
catch ()
{
//error logging in here
}
}
}
Stepping thru the code shows that FetchGames is called then hits the next line ( if(App.gViewModel.IsDataLoaded)
) before the async call is finished. So IsDataLoaded is always false and I cant bind the listbox on the page.
Doing a lot of googleing I have some possible solutions but I am unable convert them to my particular problem. One is like this and it has to do with continuation passing style'. I couldn't get it to work tho and would greatly appreciate some help.
Thanks!
void DoSomethingAsync( Action<string> callback ) {
HttpWebRequest req; // TODO: build your request
req.BeginGetResponse( result => {
// This anonymous function is a closure and has access
// to the containing (or enclosing) function.
var response = req.EndGetResponse( result );
// Get the result string and call the callback
string resultString = null; // TODO: read from the stream
callback(resultString);
}, null );
}
This can be resolved by moving
lbTeams.ItemsSource = App.gViewModel.Games;
to the end of the OpenReadCompletedGames method. You'll need to use the Dispatcher to update the UI from here.
Dispatcher.BeginInvoke( () => { lbTeams.ItemsSource = App.gViewModel.Games; } );