Blazor Interop causes LOS Overflow - interop

I am using Blazor and i am using interop to call a .NET method from javascript at regular intervals using window.SetInterval.
Calling the .NET method causes laggy-ness and this message in the browser -debug:
Message
WASM: GC_MAJOR_SWEEP: major size: 1232K in use: 836K
blazor.webassembly.js:1 WASM: GC_MAJOR: (LOS overflow) time 76.10ms, stw 76.14ms los size: 24464K in use: 23455K
blazor.webassembly.js:1 WASM: GC_MINOR: (LOS overflow) time 19.33ms, stw 19.37ms promoted 0K major size: 1232K in use: 339K los size: 24464K in use: 23455K
The js part is pretty straight forward.I have a subscription, a set and a clear interval method that will be called from .net.
Js
window.methods={
subscription: null,
setInterval: function (interval) {
if (this.subscription != null || this.subscription != undefined) {
window.clearInterval(this.subscription);
}
subscription = window.setInterval(async function () {
console.log("calling .net from js");
await DotNet.invokeMethodAsync("Sms.Studio.Web.Client", "RefreshCallbackAsync");
}, interval);
},
clearInterval: function () {
if (subscription == null || subscription == undefined) {
return;
}
console.log("Clearing subscription");
window.clearInterval(subscription);
}
}
In .Net i have a method that starts the SetInterval and a method that is JSInvokeable.This invokeable method launches an event to which i subscribe in my component.
Component
private bool shouldRefresh;
[Parameter] protected bool ShouldRefresh {
get {
return this.shouldRefresh;
}
set {
ManageSubscription(value);
}
}
public delegate Task OnRefresh();
public static event OnRefresh JSCalledRefresh;
[JSInvokable("RefreshCallbackAsync")]
public static async Task RefreshAsync() {
Console.WriteLine("Invoked event");
JSCalledRefresh?.Invoke();
}
private void ManageSubscription(bool value) {
if (this.shouldRefresh == value) {
return;
}
if(this.shouldRefresh==false && value == true) {
JSMethods.SetInterval(INTERVAL);
JSCalledRefresh += SubscribeMethod;
}
else if(this.shouldRefresh==true && value == false) {
JSMethods.ClearInterval();
JSCalledRefresh -=SubscribeMethod;
}
this.shouldRefresh = value;
}
Basically what i am doing is i have a [Parameter] bool ShouldRefresh that when it changes value i either clear or set the subscription via js interop.When the subscription is set the JSInvokeable method will also publish an event to the parent component.
P.S If i stop invoking the JSInvokeable method from js i stop getting lag and the overflow message.

#Bercovici Adrian, your code is not complete. Suppose that when your child component is rendered for the first time, and the value assigned to ShouldRefresh equals true: window.methods.setInterval method is called, which calls back the C# RefreshCallbackAsync method that triggers the event JSCalledRefresh... I don't see any code that assign false to JSCalledRefresh, and thus initiate a call to window.methods.clearInterval... As a result, window.methods.setInterval is executed endlessly, calling again and again to the RefreshCallbackAsync method. Perhaps, this is the cause of the exception.
Hope this helps...

Related

Blazor client side refresh component

I'm trying to figure out how to refresh the client-side component after button click.
Repo Link with example: https://github.com/ovie91/RefreshComponent
Site /test or from nav menu test
So I have OnInitializedAsync method that is retrieving data from API
protected override async Task OnInitializedAsync()
{
result = await (some API Call);
}
Then I have a method connected to the button
private async void ButtonClick()
{
await (some API Call);
result = null;
this.StateHasChanged(); <--- Doesnt work :<
}
I have tried to use this.StateHasChanged(); but there is no reaction.
As a workaround, I can force you to navigate again to the same website but this refresh "Whole" website but not a component.
Any ideas on how to deal with it?
whole code (stripped to minimum):
#page "/test"
#inject HttpClient Http
#if (result == null)
{
<p>Loading...<p>
}
else
{
#result
<button #onclick="(() => ButtonClick())">Click</button>
}
#code {
private APIObject result;
protected override async Task OnInitializedAsync()
{
result = await (some API Call);
}
private async void ButtonClick()
{
await (some API Call);
result = null;
this.StateHasChanged(); <--- Doesnt work :<
}
}
Update
I want to refresh component so OnInitializedAsync would be triggered again and that would mean I don't have to run the same code again after button click. Hope you understand what I mean.
To get the desired output you just have to shuffle the lines a little, from:
private async void ButtonClick()
{
await (some API Call); // UI checks if an update is needed (No)
result = null; // now an update is needed
this.StateHasChanged(); <--- Doesnt work :< // actually: not needed
}
to:
private async Task ButtonClick()
{
result = null; // change the state
//this.StateHasChanged(); // not needed, a request is pending
await (some API Call); // should show '<h3>Loading</h3>' now
}
Note that the UI is updated when an await releases the Thread.
however, from your answer we get
var APICall = await Http.GetAsync("SomeAPI");
Thread.Sleep(2000);
This should work when Http.GetAsync("SomeAPI"); really is an async call and not just some stand-in pseudo code. Because Thread.Sleep(2000); will really freeze things.
If you want to make sure:
private async Task GetData()
{
await Task.Delay(1); // release the thread for rendering
var APICall = await Http.GetAsync("SomeAPI");
Random rnd = new Random();
Thread.Sleep(2000); // Task.Delay() is much preferred
result = "Random Number: " + rnd.Next();
}
Thread.Sleep() is appropriate to simulate some CPU (not I/O) intensive code. So I'm not saying it's wrong but be aware of the difference.
And it is much better to make eventhandlers async Task instead of async void but that is not the direct problem here.
From here:
Blazor uses a synchronization context (SynchronizationContext) to enforce a single logical thread of execution. A component's lifecycle methods and any event callbacks that are raised by Blazor are executed on the synchronization context.
Blazor Server's synchronization context attempts to emulate a single-threaded environment so that it closely matches the WebAssembly model in the browser, which is single threaded. At any given point in time, work is performed on exactly one thread, giving the impression of a single logical thread. No two operations execute concurrently.
So as enet asnwered, you should use async Task signature instead of async void.
I have moved API call to another Method and inside of OnInitializedAsync I called it.
Then when I reset the result variable to see Loading state I'm able to "refresh" component to achieve that you need to add. this.StateHasChanged()
Now I have a responsive component to updates that are happening :)
#page "/test"
#using System.Threading;
#inject HttpClient Http
#if (result == null)
{
<h3>Loading</h3>
}
else
{
#result
<button #onclick="(() => ButtonClick())">Click</button>
}
#code {
private string result;
protected override async Task OnInitializedAsync()
{
await GetData();
}
private async Task GetData()
{
var APICall = await Http.GetAsync("SomeAPI");
Random rnd = new Random();
Thread.Sleep(2000);
result = "Random Number: " + rnd.Next();
}
private async Task ButtonClick()
{
await Http.GetAsync("SomeAPIcall");
result = null; // required to see loading state.
this.StateHasChanged(); // when added model is refreshed and Loading state is visible.
await GetData();
}
}

In-Memory Caching with auto-regeneration on ASP.Net Core

I guess there is not built-in way to achieve that:
I have some cached data, that need to be always up to date (interval of few 10s of minutes). Its generation takes around 1-2 minutes, therefore it leads sometimes to timeout requests.
For performances optimisation, I put it into memory cache, using Cache.GetOrCreateAsync, so I am sure to have fast access to the data during 40 minutes. However it still takes time when the cache expires.
I would like to have a mechanism that auto-refreshes the data before its expiration, so the users are not impacted from this refresh and can still access the "old data" during the refresh.
It would actually be adding a "pre-expiration" process, that would avoid data expiration to arrive at its term.
I feel that is not the functioning of the default IMemoryCache cache, but I might be wrong?
Does it exist? If not, how would you develop this feature?
I am thinking of using PostEvictionCallbacks, with an entry set to be removed after 35 minutes and that would trigger the update method (it involves a DbContext).
This is how I solve it:
The part called by the web request (the "Create" method should be called only the first time).
var allPlaces = await Cache.GetOrCreateAsync(CACHE_KEY_PLACES
, (k) =>
{
k.AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(40);
UpdateReset();
return GetAllPlacesFromDb();
});
And then the magic (This could have been implemented through a timer, but didn't want to handle timers there)
// This method adds a trigger to refresh the data from background
private void UpdateReset()
{
var mo = new MemoryCacheEntryOptions();
mo.RegisterPostEvictionCallback(RefreshAllPlacessCache_PostEvictionCallback);
mo.AddExpirationToken(new CancellationChangeToken(new CancellationTokenSource(TimeSpan.FromMinutes(35)).Token));
Cache.Set(CACHE_KEY_PLACES_RESET, DateTime.Now, mo);
}
// Method triggered by the cancellation token that triggers the PostEvictionCallBack
private async void RefreshAllPlacesCache_PostEvictionCallback(object key, object value, EvictionReason reason, object state)
{
// Regenerate a set of updated data
var places = await GetLongGeneratingData();
Cache.Set(CACHE_KEY_PLACES, places, TimeSpan.FromMinutes(40));
// Re-set the cache to be reloaded in 35min
UpdateReset();
}
So the cache gets two entries, the first one with the data, expiring after 40 minutes, the second one expiring after 35min via a cancellation token that triggers the post eviction method.
This callback refreshes the data before it expires.
Keep in mind that this will keep the website awake and using memory even if not used.
** * UPDATE USING TIMERS * **
The following class is registered as a singleton. DbContextOptions is passed instead of DbContext to create a DbContext with the right scope.
public class SearchService
{
const string CACHE_KEY_ALLPLACES = "ALL_PLACES";
protected readonly IMemoryCache Cache;
private readonly DbContextOptions<AppDbContext> AppDbOptions;
public SearchService(
DbContextOptions<AppDbContext> appDbOptions,
IMemoryCache cache)
{
this.AppDbOptions = appDbOptions;
this.Cache = cache;
InitTimer();
}
private void InitTimer()
{
Cache.Set<AllEventsResult>(CACHE_KEY_ALLPLACESS, new AllPlacesResult() { Result = new List<SearchPlacesResultItem>(), IsBusy = true });
Timer = new Timer(TimerTickAsync, null, 1000, RefreshIntervalMinutes * 60 * 1000);
}
public Task LoadingTask = Task.CompletedTask;
public Timer Timer { get; set; }
public long RefreshIntervalMinutes = 10;
public bool LoadingBusy = false;
private async void TimerTickAsync(object state)
{
if (LoadingBusy) return;
try
{
LoadingBusy = true;
LoadingTask = LoadCaches();
await LoadingTask;
}
catch
{
// do not crash the app
}
finally
{
LoadingBusy = false;
}
}
private async Task LoadCaches()
{
try
{
var places = await GetAllPlacesFromDb();
Cache.Set<AllPlacesResult>(CACHE_KEY_ALLPLACES, new AllPlacesResult() { Result = places, IsBusy = false });
}
catch{}
}
private async Task<List<SearchPlacesResultItem>> GetAllPlacesFromDb()
{
// blablabla
}
}
Note:
DbContext options require to be registered as singleton, default options are now Scoped (I believe to allow simpler multi-tenancy configurations)
services.AddDbContext<AppDbContext>(o =>
{
o.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking);
o.UseSqlServer(connectionString);
},
contextLifetime: ServiceLifetime.Scoped,
optionsLifetime: ServiceLifetime.Singleton);

Akavache always calling the fetchFunc

I have the following code using Akavache in a Xamarin app and it's not behaving the way I would think it should. Probably my misunderstanding of how it should be but it's driving me crazy.
So in my viewmodel I'm making the call to FetchNewsCategories and specifying a cache of 5 minutes for the item. What I'd expect to happen is that if the cache item is not there, it would make a call to the fetchFunc (ie. FetchNewsCategoriesAsync) but if I call the service any number of times inside the cache timeout of 5 minutes, it should just give me the cached item and not do the server call. In all cases that I've tried, it keeps doing the rest call and never gives me the cached item. I've also tried this with GetAndFetchLatest and if there is a cached item, it doesn't make the rest call but it also doesn't make the call in the subscribe event in the viewmodel.
Any ideas what I'm doing wrong here?
EDIT: I tested this same code on Android (Nexus 5 KitKat API19) and it's working flawlessly. I'm going to reset my IOS emulator and see if something was just out of whack.
NewsService.cs
public static async Task<ServiceResponse<List<ArticleCategoryInfo>>> FetchNewsCategoriesAsync(BlogSourceType blogSource)
{
return await ServiceClient.POST<List<ArticleCategoryInfo>>(Config.ApiUrl + "news/categories", new
{
ModuleId = Int32.Parse(Config.Values[blogSource == BlogSourceType.News ? ConfigKeys.KEY_NEWS_MODULE_ID : ConfigKeys.KEY_BLOG_MODULE_ID])
});
}
public static IObservable<ServiceResponse<List<ArticleCategoryInfo>>> FetchNewsCategories(BlogSourceType blogSource)
{
var cache = BlobCache.LocalMachine;
var cachedCategories = cache.GetOrFetchObject("categories" + blogSource,
async () => await FetchNewsCategoriesAsync(blogSource),
DateTime.Now.AddMinutes(5));
return cachedCategories;
}
NewsViewModel.cs
public async Task LoadCategories()
{
var cachedCategories = NewsService.FetchNewsCategories(blogSource);
cachedCategories.Subscribe((obj) => { Device.BeginInvokeOnMainThread(() => DisplayCategories(obj.Result,"Subscribe"));});
return;
}
private void DisplayCategories(IList<ArticleCategoryInfo> categories, string source)
{
Categories.Clear();
System.Diagnostics.Debug.WriteLine("Redisplaying categories from " + source);
foreach (var item in categories)
{
Categories.Add(item);
}
}
Just wanted to add my resolution to the issue I experienced above for reference to others with this problem.
The ServiceResponse object that I was trying to cache had an HttpResponseMessage in it which I suspect was causing a serialization error, probably a cyclical reference, so it never did get cached and ended up calling the endpoint every time. I ended up putting an [IgnoreDataMemberAttribute] on that property so it wasn't serialized and the problems went away.
I ended up handling the subscribe in the following manner to handle errors and to make sure the activity indicator bound to the IsBusy property was updated properly.
public async Task LoadActivities(bool refresh)
{
IsBusy = true;
if (refresh) OlderThanJournalId = int.MaxValue;
var cached = ActivityService.FetchJournalItems(GroupId, OlderThanJournalId, refresh);
cached.Subscribe((result) => { Device.BeginInvokeOnMainThread(() => {
DisplayActivities(result);
}); }, (err) => HandleError(err), () => IsBusy = false);
}
public void HandleError(Exception ex) {
IsBusy = false;
DialogService.ShowErrorToast(AppResources.ErrorMessage, "Unable to load activity stream.");
System.Diagnostics.Debug.WriteLine(ex.Message);
}
private void DisplayActivities(ServiceResponse<List<JournalItem>> response)
{
if (!response.IsConnected) {
DialogService.ShowInfoToast(AppResources.ErrorMessage, AppResources.NotConnected);
return;
}
if (!response.Authorized) {
App.LoginManager.Logout();
}
Activities.Clear();
foreach (var item in response.Result)
{
Activities.Add(item);
}
}
BeginInvokeOnMainThread is used to make sure that the updates to the ObservableCollection in DisplayActivities are seen in the UI.

Proper pattern to return results of a recursive AJAX call (Microsoft AJAX)

I'm using the following function (code heavily removed for sake of pattern examination) to get all lists of a certain SP.ListTemplateType from a site collection. Since MS AJAX does not include a $promise function (to my knowledge), I'm creating a queue that increments as calls are made, and decrements as calls are returned, either successfully or in error.
This seems (possibly) prone to error, if the first call is returned before the second is made. So far, even in the case of 20+ recursions, the first call doesn't return until the last call is made, so the queue SEEMS to be safe.
Is this wrong, or am I doing this correctly?
function allListsOfType(type, callback) {
//setup context, etc...
var returnListArray = [];
var queue = 0;
getListsFromWeb(web);
function getListsFromWeb(targetWeb) {
//get lists from root web, and get all subwebs
context.load(objects);
queue++;
context.executeQueryAsync(
Function.createDelegate(this, function () { successHandler(); }),
Function.createDelegate(this, errorHandler)
);
}
function successHandler() {
//add list results to array
//loop through subwebs and call getListsFromWeb()
queue--;
if (queue == 0) {
callback(returnListArray);
}
}
function errorHandler(sender, args) {
queue--;
}
};
allListsOfType(SP.ListTemplateType.announcements, function(arr){
alert(arr.length)
});
This seems correct, except that your callback will never be called if the first Ajax request returns an error.
Do the same check into errorHandler() than the one done into successHandler():
function errorHandler(sender, args) {
queue--;
if (queue == 0) {
callback(returnListArray);
}
}

How to Check internet is available or not in windows phone 7?

For internet checking i wrote the below lines of code.
bool isAvailable = NetworkInterface.GetIsNetworkAvailable();
if (isAvailable == true)
{
}
the above line always returning true if net is not available also.please tell me how to check the internet connection?
Check NetworkInterfaceType. If Internet is available then It
should be other than None
return (Microsoft.Phone.Net.NetworkInformation.NetworkInterface.NetworkInterfaceType
!= Microsoft.Phone.Net.NetworkInformation.NetworkInterfaceType.None);
Reference Answer By vjsrinath: https://stackoverflow.com/questions/8341169/how-to-check-internetconnection-in-wp7
Hope this helps.
You can also try,
bool isAvailable = Microsoft.Phone.Net.NetworkInformation.DeviceNetworkInformation.IsNetworkAvailable;
Always check this in a thread, it takes some time
internal static bool IsNetworkAvailable()
{
bool _bhasNetworkConnection = false;
try
{
_bhasNetworkConnection = (NetworkInterface.NetworkInterfaceType != NetworkInterfaceType.None);
}
catch
{
_bhasNetworkConnection = false;
}
return _bhasNetworkConnection;
}
Checking NetworkInterface.NetworkInterfaceType in the UI thread is "dangerous" as it is a blocking call! It is possible that this call takes up to 20 seconds...
Use the following method to have a non-blocking call:
public static void IsConnected(Action<bool> completed)
{
ThreadPool.QueueUserWorkItem(o =>
{
var type = NetworkInterface.NetworkInterfaceType;
completed(type != NetworkInterfaceType.None);
});
}
See https://xp-dev.com/svn/mytoolkit/MyToolkit.Wp8/Networking/NetworkState.cs (From my project page)
If you would like to change the UI in the completed action you have to run the logic in the UI thread. E.g.:
NetworkState.IsConnected(connected => {
if (connected)
{
Deployment.Current.Dispatcher.BeginInvoke(delegate {
// TODO add your UI logic
});
}
});
If you need a lot of connection checks, check out the NetworkStateTracker, which is my project:
https://xp-dev.com/svn/mytoolkit/MyToolkit.Wp8/Networking/NetworkStateTracker.cs

Resources