How to re-register refit handlers in MAUI - maui

In my MAUI, I register custom auth handlers using the following code snippet.
public static class MauiProgram
{
public static MauiApp CreateMauiApp()
{
var builder = MauiApp.CreateBuilder();
builder
.UseMauiApp<App>()
.UseMauiCompatibility()
.UseMauiCommunityToolkit()
.RegisterRefitClients()
return builder.Build();
}
}
And my RegisterRefitClients extension method looks like this.
public static MauiAppBuilder RegisterRefitClients(this MauiAppBuilder mauiAppBuilder)
{
mauiAppBuilder.Services.AddRefitClient<IMyApiService>()
.ConfigureHttpClient(c => c.BaseAddress = new Uri(Configuration.BASE_URL))
.AddHttpMessageHandler<AuthHeaderHandler>();
return mauiAppBuilder;
}
Here, the thing is, Configuration.BASE_URL can change at run time. By default, it always points to our production server. But the testers can change it to staging/dev server at run time. I want to invoke this method at run time, in order to set custom auth handler for the newly changed staging server.
But I cannot get the MauiAppBuilder instance anywhere in the app once it is initiated.
How do I call the RegisterRefitClients at run time?

RegisterRefitClients can not be called when the program start running. .NET MAUI enables apps to be initialized from a single location. The MauiProgram class is the entry point to the application, setting up configuration and wiring up services the application will use. Once the program starts, you can not change the MauiProgram.

Related

Spring testing onProprtiesSet with embedded mongodb

We have some logic that runs on application start using InitializingBean.onPropertiesSet that depends on the contents of the mongo database and we want to test it.
Specifically, we trigger some code the saves a state in the db and then the causes the app to restart, and after that the state in the db is asserted in onPropertiesSet.
I tried to do the following:
#SpringBootTest(...some properties)
#DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_EACH_TEST_METHOD)
public class TestClass {
#Test
public void dummyTest() {
// trigger app restart which also saves the state in the db
}
#Test(dependsOnMethods = { "dummyTest" })
public void realTest() {
// test that the code executed in onPropertiesSet() is correct
}
}
DirtiesContext is used to trigger the onProprtiesSet.
This logic runs fine except that DirtiesContext clears the database so then there is nothing to assert on restart.
I'd like to hear how this can be solved. Other methods of testing such scenarios are also welcome (I've seen people use the #sql annotation to inject data before the test starts, but I haven't been able to find something similar for mongodb), although testing the entire scenario is preferred.

How do I make data calls from different Blazor components simultaneously?

I'm new to Blazor and trying to make a page with several separate components to handle a massive form. Each individual component covers a part of the form.
The problem I'm facing is that each of my components needs access to data from the back-end, and not every component uses the same data. When the page loads, each components makes an attempt to fetch data from the server, which causes a problem with Entity Framework.
A second operation started on this context before a previous operation
completed. This is usually caused by different threads using the same
instance of DbContext.
This is obviously caused by the fact that my components are initialized at the same time, and all make their attempt to load the data simultaneously. I was under the impression that the way DI is set up in Blazor, this wouldn't be a problem, but it is.
Here are the components in my template:
<CascadingValue Value="this">
<!-- BASE DATA -->
<CharacterBaseDataView />
<!-- SPECIAL RULES -->
<CharacterSpecialRulesView />
</CascadingValue>
Here is how my components are initialized:
protected async override Task OnInitializedAsync()
{
CharacterDetailsContext = new EditContext(PlayerCharacter);
await LoadCharacterAsync();
}
private async Task LoadCharacterAsync()
{
PlayerCharacter = await PlayerCharacterService.GetPlayerCharacterAsync(ViewBase.CharacterId.Value);
CharacterDetailsContext = new EditContext(PlayerCharacter);
}
When two components with the above code are in the same view, the mentioned error occurs. I thread using the synchronous version "OnInitialized()" and simply discarding the task, but that didn't fix the error.
Is there some other way to call the data so that this issue doesn't occur? Or am I going about this the wrong way?
You've hit a common problem in using async operations in EF - two or more operations trying to use the same context at once.
Take a look at the MS Docs article about EF DBContexts - there's a section further down specific to Blazor. It explains the use of a DbContextFactory and CreateDbContext to create contexts for units-of-work i.e. one context per operation so two async operations each have a separate context.
Initially to solve the threading issues, I used DbContextFactory to create contexts for each operation - however this resulted in database in-consistency issues across components, and I realised I need change tracking across components.
Therefore instead, I keep my DbContext as scoped, and I don't create a new context before each operation.
I then adapted my OnInitializedAsync() methods to check if the calls to the database have completed, before making these calls through my injected services. This works really well for my app:
#code {
static Semaphore semaphore;
//code ommitted for brevity
protected override async Task OnInitializedAsync()
{
try
{
//First open global semaphore
semaphore = Semaphore.OpenExisting("GlobalSemaphore");
while (!semaphore.WaitOne(TimeSpan.FromTicks(1)))
{
await Task.Delay(TimeSpan.FromSeconds(1));
}
//If while loop is exited or skipped, previous service calls are completed.
ApplicationUsers = await ApplicationUserService.Get();
}
finally
{
try
{
semaphore.Release();
}
catch (Exception ex)
{
Console.WriteLine("ex.Message");
}
}
}

HTML version of LibGDX game doesn't show any logging

I am having trouble getting a html version of my programme to appear.
I am using LibGDX 1.3.1 and running in Java it works fine.
I uploaded the game here:
http://www.darkflame.co.uk/MeshExplorer/index.html
The libgdx loading bar appears and finishes - and in Chromes network tag I can see assets loading.
However, nothing appears other then the rectangle of the expected game size.
Most confusingly for me though, I dont see any crashes or logs from my code.
That is, there is nothing after "SoundManager 2 loaded (OK) "
Given that the first lines of my main core class are:
game=this;
font = new BitmapFont();
batch = new SpriteBatch();
Gdx.app.log(logstag, "loading..");
I expected at least to see "loading.."
I even added some gwt logs to html launcher
public class HtmlLauncher extends GwtApplication {
static Logger Log = Logger.getLogger("HtmlLauncher");
#Override
public GwtApplicationConfiguration getConfig () {
Log.info("GwtApplicationConfiguration");
System.out.print("GwtApplicationConfiguration");
return new GwtApplicationConfiguration(640, 480);
}
#Override
public ApplicationListener getApplicationListener () {
Log.info("test, returning class ME() ");
System.out.print("test, returning class ME() ");
return new ME();
}
}
again, nothing.
I am at a lose how to disorganize this problem further.
It just seems like libgdx isn't even attempting to run my code.
The default logging level in the html target is LOG_ERROR. You would not see any Gdx.app.log messages unless you set the logging level to LOG_INFO.
Calling Gdx.app.setLogLevel(LOG_INFO) in your getConfig or getApplicationListener methods should do the trick.

Show view from non-view/viewmodel in mvvmcross

What is the correct way to open a View in mvvmcross from a non-view? From within a viewmodel we would use ShowViewModel<>(..).
Specifically we are responding to a push notification opening the app (with a custom payload) which dictates a view that should be loaded.
We have a hackety workaround just for proof of concept, just wanted to get an idea of the correct MVX approach
I don't think there is a 'correct way' - I think it depends on your app and what you need it to do.
For some specific cases - e.g. ViewModel->ViewModel and AppStart - MvvmCross provides some convenient methods:
you can call ShowViewModel in MvxViewModel
the app start can be overridden to use a hint object - see https://speakerdeck.com/cirrious/appstart-in-mvvmcross
But overall, any class can request a ShowViewModel by calling:
var viewDispatcher = Mvx.Resolve<IMvxViewDispatcher>();
viewDispatcher.ShowViewModel(new MvxViewModelRequest(
viewModelType,
parameterBundle,
presentationBundle,
requestedBy));
Further, there is a base class - MvxNavigatingObject.cs - which can help with this (it's a base class of MvxViewModel and MvxAppStart) - so you can easily provide one or more services like INavigateMyselfService who's implementations inherit from MvxNavigatingObject.
public interface INavigateMyselfService
{
void GoWild(string side);
}
public class NavigateMyselfService
: MvxNavigatingObject
, INavigateMyselfService
{
public void GoWild(string side)
{
ShowViewModel<WildViewModel>(new { side = side });
}
}
http://forums.xamarin.com/discussion/4694/conditionally-call-registerappstart-with-mvvmcross
Check the above link and you will get idea
In my case,I want to launch the app from secondary tile.For this,I have to launch specific page for Secondary tile.
My Initial App Start view model is LoginViewModel and my custom app start view model is HomeViewModel.
I controlled this from App.cs (Core) to MyCustomAppStart class.
Refer the above link

pause viewmodel process for user input

I've been looking at a view examples of the typical "raise dialog from viewmodel" problem, noting 3 main solutions:
use attached behaviors
use a mediator pattern
use a service
I'm getting a bit bogged down though and struggling to find a solution that easily fits into my problem space - which is a very simple file copy problem:
My viewmodel is processing a loop (copying a list of files)
When a file already exists at the destination I need to raise a modal dialog to get confirmation to replace
The vm needs to wait for and receive confirmation before continuing
The "modal dialog" is actually not a new window but a hidden overlay in my MainWindow, as per http://www.codeproject.com/KB/WPF/wpfmodaldialog.aspx (thanks Ronald!)
I'm mostly there but the biggest struggles I have are:
- how to pause the loop in the viewmodel while it waits for input
- how to get input back to the viewmodel within the loop so it can carry on
So far I'm leaning towards the service solution because it seems a direct method call with a return that the vm must wait for. However, it does mean the service needs to tie directly to the view in order to make an element visible?
If anyone can post some simple code that deals directly with this problem I (and the net) would be very happy! Thanks!
For example, you have a service called IDialogService with the following interface:
public interface IDialogService
{
bool ConfirmAction(string title, string confirmationText);
}
As you mentioned, in order for the service to be able to show the actual dialog it needs to have a reference to the view that will show the actual overlay element. But instead of directly referencing the view I prefer to reference it via an interface. Lets call it ICanShowDialog and it will have the following members:
public interface ICanShowDialog
{
void ShowDialog(object dialogContent);
void HideDialog();
}
This interface will be implemented by your view that owns the dialog overlay (e.g. your main window).
Now the interesting part: suspending the code execution while the dialog is shown. First of all, I would recommend you not to use overlay elements but use usual windows if possible. Then you will not have that problem. You can style the dialog window so it will look just like the overlay element.
Anyway, if you still want to use overlay elements then you can do the following trick to suspend the code execution:
Here is pseudo code of the ConfirmAction method of the IDialogService inteface:
public bool ConfirmAction(string title, string confirmationText)
{
ConfirmationDialogView dialogView = new ConfirmationDialogView(title, confirmationText);
DialogShower.ShowDialog(dialogView); // DialogShower is of type ICanShowDialog
while (!dialogView.ResultAvailable)
{
DispatcherUtils.DoEvents();
}
DialogShower.HideDialog();
return dialogView.Result;
}
Here is the code of DispatcherUtils.DoEvents() (that was taken from here: http://dedjo.blogspot.com/2007/08/how-to-doevents-in-wpf.html):
public static class DispatcherUtils
{
public static void DoEvents()
{
DispatcherFrame f = new DispatcherFrame();
Dispatcher.CurrentDispatcher.BeginInvoke(
DispatcherPriority.Background,
(SendOrPostCallback)delegate(object arg) {
DispatcherFrame fr = arg as DispatcherFrame;
fr.Continue=True;
}, f);
Dispatcher.PushFrame(frame);
}
}
But I must warn you. Using DoEvents can result in some subtle bugs caused by inner dispatcher loops.
As an alternative to suspending the code execution while a dialog is shown you can use callbacks:
public interface IDialogService
{
void ConfirmAction(string title, string confirmationText, Action<bool> dialogResultCallback);
}
But it will not be so convenient to use.