How do I properly use DI with IHttpClientFactory in .NET MAUI - httpclient

I haven't found anything about HttpClient in .NET MAUI.
Does anyone know if the service:
builder.Services.AddHttpClient<IMyService, MyService>();
is possible in MAUI's startup MauiProgram.cs? And then inject HttpClient to where it's going to be used. I have tried everything and it does not seem to work. Only AddSingleton of HttpClient works for me, but it doesn't seem optimal.
PS.: I had to install nuget package Microsoft.Extensions.Http in order to use the AddHttpClient service.
UPDATES:
WORKING CODE:
MauiProgram.cs
builder.Services.AddTransient<Service<Display>, DisplayService>();
builder.Services.AddTransient<Service<Video>, VideoService>();
builder.Services.AddTransient<Service<Image>, ImageService>();
builder.Services.AddTransient<Service<Log>, LogService>();
builder.Services.AddSingleton(sp => new HttpClient() { BaseAddress = new Uri("https://api.myapi.com") });
Example of VideosViewModel.cs using a service
[INotifyPropertyChanged]
public partial class VideosViewModel
{
readonly Service<Video> videoService;
[ObservableProperty]
ObservableCollection<Video> videos;
[ObservableProperty]
bool isEmpty;
[ObservableProperty]
bool isRefreshing;
public VideosViewModel(Service<Video> videoService)
{
this.videoService = videoService;
}
[ICommand]
internal async Task LoadVideosAsync()
{
#if ANDROID || IOS || tvOS || Tizen
UserDialogs.Instance.ShowLoading("Henter videoer fra databasen...");
#endif
await Task.Delay(2000);
Videos = new();
try
{
await foreach (Video video in videoService.GetAllAsync().OrderBy(x => x.Id))
{
Videos.Add(video);
}
}
catch (Exception ex)
{
throw new Exception(ex.Message);
}
finally
{
IsRefreshing = false;
#if ANDROID || IOS || tvOS
UserDialogs.Instance.HideLoading();
#endif
if (Videos.Count is 0)
{
IsEmpty = true;
}
else
{
IsEmpty = false;
}
}
}
[ICommand]
async Task UploadVideoAsync()
{
await Shell.Current.DisplayAlert("Upload en video", "Under opbygning - kommer senere!", "OK");
}
}
NOT WORKING CODE:
MauiProgram.cs
builder.Services.AddHttpClient<Service<Display>, DisplayService>(sp => sp.BaseAddress = new Uri("https://api.myapi.com"));
builder.Services.AddHttpClient<Service<Video>, VideoService>(sp => sp.BaseAddress = new Uri("https://api.myapi.com"));
builder.Services.AddHttpClient<Service<Image>, ImageService>(sp => sp.BaseAddress = new Uri("https://api.myapi.com"));
builder.Services.AddHttpClient<Service<Log>, LogService>(sp => sp.BaseAddress = new Uri("https://api.myapi.com"));
VideosViewModel.cs
Same as above working code.
What specifically doesn't work is that I get object reference exception on OrderBy(x => x.Id), specifically highlighted x.Id in ViewModel. Removing OrderBy method gives no longer exceptions, but the view shows no data except one random empty Frame.

Do not use builder.Services.AddHttpClient in MAUI.
Use one instance.

Related

How to customize collectionview in dotnetmaui

I am porting my Xamarin Forms application to dotnet maui and I want to customize the collection view for android so that the items will stack from the end. I have done it in Xamarin Forms as follows,
public class ChatCollectionViewRenderer : CollectionViewRenderer
{
public ChatCollectionViewRenderer(Context context) : base(context)
{
this.SetItemViewCacheSize(20);
this.HasFixedSize = true;
}
protected override LayoutManager SelectLayoutManager(IItemsLayout layoutSpecification)
{
var manager = new LinearLayoutManager(Context, LinearLayoutManager.Vertical, false);
manager.StackFromEnd = true;
return manager;
}
}
How to do it in maui using handlers?
Found it, it can be done like this,
Microsoft.Maui.Controls.Handlers.Items.CollectionViewHandler.Mapper.AppendToMapping("ChatStackFromEnd", (h, v) =>
{
var recycleView = h.PlatformView;
var manager = new AndroidX.RecyclerView.Widget.LinearLayoutManager(recycleView.Context, AndroidX.RecyclerView.Widget.LinearLayoutManager.Vertical, false);
manager.StackFromEnd = true;
recycleView.SetLayoutManager(manager);
});
Trying it out...

Unity - Google cloud speech-to-text voice recognition, Unity freezes after successful result

A friend of mine and I are working on a VR project in Unity at the moment and we are trying to implement voice recognition as a feature. We are using Unity version 2018.3.3f1. The idea is that a user can say a word and the voice recognition will see if they pronounced it correctly. We have chosen to use the Google cloud speech-to-text service for this as it supports the target language (Norwegian). In addition, the application is also multiplayer and so we are trying to use the streaming version of Google cloud speech. Here is a link to their documentation: https://cloud.google.com/speech-to-text/docs/streaming-recognize
What we have done is to have a plugin that essentially runs the speech recognition for us. It is a modification of the example code given in the link above:
public Task<bool> StartSpeechRecognition()
{
return StreamingMicRecognizeAsync(20, "fantastisk");
}
static async Task<bool> StreamingMicRecognizeAsync(int inputTime, string inputWord)
{
bool speechSuccess = false;
Stopwatch timer = new Stopwatch();
Task delay = Task.Delay(TimeSpan.FromSeconds(1));
if (NAudio.Wave.WaveIn.DeviceCount < 1)
{
//Console.WriteLine("No microphone!");
return false;
}
var speech = SpeechClient.Create();
var streamingCall = speech.StreamingRecognize();
// Write the initial request with the config.
await streamingCall.WriteAsync(
new StreamingRecognizeRequest()
{
StreamingConfig = new StreamingRecognitionConfig()
{
Config = new RecognitionConfig()
{
Encoding =
RecognitionConfig.Types.AudioEncoding.Linear16,
SampleRateHertz = 16000,
LanguageCode = "nb",
},
InterimResults = true,
}
});
// Compare speech with the input word, finish if they are the same and speechSuccess becomes true.
Task compareSpeech = Task.Run(async () =>
{
while (await streamingCall.ResponseStream.MoveNext(
default(CancellationToken)))
{
foreach (var result in streamingCall.ResponseStream
.Current.Results)
{
foreach (var alternative in result.Alternatives)
{
if (alternative.Transcript.Replace(" ", String.Empty).Equals(inputWord, StringComparison.InvariantCultureIgnoreCase))
{
speechSuccess = true;
return;
}
}
}
}
});
// Read from the microphone and stream to API.
object writeLock = new object();
bool writeMore = true;
var waveIn = new NAudio.Wave.WaveInEvent();
waveIn.DeviceNumber = 0;
waveIn.WaveFormat = new NAudio.Wave.WaveFormat(16000, 1);
waveIn.DataAvailable +=
(object sender, NAudio.Wave.WaveInEventArgs args) =>
{
lock (writeLock)
{
if (!writeMore) return;
streamingCall.WriteAsync(
new StreamingRecognizeRequest()
{
AudioContent = Google.Protobuf.ByteString
.CopyFrom(args.Buffer, 0, args.BytesRecorded)
}).Wait();
}
};
waveIn.StartRecording();
timer.Start();
//Console.WriteLine("Speak now.");
//Delay continues as long as a match has not been found between speech and inputword or time that has passed since recording is lower than inputTime.
while (!speechSuccess && timer.Elapsed.TotalSeconds <= inputTime)
{
await delay;
}
// Stop recording and shut down.
waveIn.StopRecording();
timer.Stop();
lock (writeLock) writeMore = false;
await streamingCall.WriteCompleteAsync();
await compareSpeech;
//Console.WriteLine("Finished.");
return speechSuccess;
}
We made a small project in Unity to test if this was working with a cube GameObject that had this script:
private CancellationTokenSource tokenSource;
VR_VoiceRecognition.VoiceRecognition voice = new VR_VoiceRecognition.VoiceRecognition();
IDisposable speech;
// Use this for initialization
void Start() {
speech = Observable.FromCoroutine(WaitForSpeech).Subscribe();
}
// Update is called once per frame
void Update() {
}
IEnumerator WaitForSpeech()
{
tokenSource = new CancellationTokenSource();
CancellationToken token = tokenSource.Token;
Debug.Log("Starting up");
Task<bool> t = Task.Run(() => voice.StartSpeechRecognition());
while (!(t.IsCompleted || t.IsCanceled))
{
yield return null;
}
if (t.Status != TaskStatus.RanToCompletion)
{
yield break;
}
else
{
bool result = t.Result;
UnityEngine.Debug.Log(t.Result);
yield return result;
}
}
void OnApplicationQuit()
{
print("Closing application.");
speech.Dispose();
}
We are also using a plugin that was recommended to us by Unity support that they thought might have a workaround called UniRx (https://assetstore.unity.com/packages/tools/integration/unirx-reactive-extensions-for-unity-17276).
At the moment it is working fine when you play it in the editor for the first time. When the voice recognition returns false then everything is fine (two cases when this happens is if it cannot find a microphone or if the user does not say the specific word). However, if it is a success then it still returns true, but if you exit play mode in the editor and try to play again then Unity will freeze. Unity support suspects that it might have something to do with the Google .dll files or Google API. We are not quite sure what to do from here and we hope that someone could point us to the right direction.

UWP trying to run background service throwing exception

I am trying to run background service in UWP application. I am first checking if application has background permission. If yes then I am registering the service for running.
This code was working fine until I updated Visual Studio along with Windows 10 SDK to Creators Update version. Now I can't figure out if this update changes things for registering background service.
using System;
using Windows.ApplicationModel.Background;
using BackgroundService;
using SampleApp.Config;
namespace SampleApp.Background
{
class BackgroundClass
{
LocalConfig LC = new LocalConfig();
public async void RequestBackgroundAccess()
{
var result = await BackgroundExecutionManager.RequestAccessAsync();
switch (result)
{
case BackgroundAccessStatus.AllowedMayUseActiveRealTimeConnectivity:
break;
case BackgroundAccessStatus.AllowedWithAlwaysOnRealTimeConnectivity:
break;
case BackgroundAccessStatus.Denied:
break;
case BackgroundAccessStatus.Unspecified:
break;
}
}
public async void RegisterBackgroundSync()
{
var trigger = new ApplicationTrigger();
var condition = new SystemCondition(SystemConditionType.InternetAvailable);
if (!LC.BackgroundSyncStatusGET())
{
var task = new BackgroundTaskBuilder
{
Name = nameof(BackgroundSync),
CancelOnConditionLoss = true,
TaskEntryPoint = typeof(BackgroundSync).ToString(),
};
task.SetTrigger(trigger);
task.AddCondition(condition);
task.Register();
LC.BackgroundSyncStatusSET(true);
}
await trigger.RequestAsync(); //EXCEPTION HAPPENS AT THIS LINE
}
public void RegisterBackgroundService(uint time)
{
var taskName = "BackgroundService";
foreach (var unregisterTask in BackgroundTaskRegistration.AllTasks)
{
if (unregisterTask.Value.Name == taskName)
{
unregisterTask.Value.Unregister(true);
}
}
if(time != 0)
{
var trigger = new TimeTrigger(time, false);
var condition = new SystemCondition(SystemConditionType.InternetAvailable);
var task = new BackgroundTaskBuilder
{
Name = nameof(BackgroundService),
CancelOnConditionLoss = true,
TaskEntryPoint = typeof(BackgroundService).ToString(),
};
task.SetTrigger(trigger);
task.AddCondition(condition);
task.Register();
}
}
}
}
Now while requesting I am checking if background service is registered keeping issues for re-registration. I am getting following exception
System.Runtime.InteropServices.COMException occurred
HResult=0x80004005
Message=Error HRESULT E_FAIL has been returned from a call to a COM component.
Source=Windows
 
StackTrace:
  
at Windows.ApplicationModel.Background.ApplicationTrigger.RequestAsync()
  
at SampleApp.Background.BackgroundClass.d__2.MoveNext()
Please Help
Had this same problem, was in my Windows 10 Privacy Settings.
System Settings => Privacy Settings
In the left-hand menu choose Background apps.
Check to make sure your app hasn't been blocked from running background tasks.

checking paragraph property loses the selection

in my vsto addin i have some simple code on a timer
private void MainTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
if (!dialogopen & Application.Documents.Count > 0)
{
var doc = Application.ActiveDocument;
Word.InlineShapes shps;
Word.Paragraphs pars;
try
{
pars = doc.Paragraphs;
}
catch (Exception)
{
return;
}
var pars2 = pars.Cast<Word.Paragraph>().ToList();
foreach (var obj in pars2)
{
if (obj.OutlineLevel == Word.WdOutlineLevel.wdOutlineLevelBodyText )//PROBLEM HERE
{
};
}
}
}
as soon as it reaches the line that checks the outlinelevel, even if i dont do a thing, the selection in the activedocument gets lost
of course the user cant use a plugin which keeps on canceling his selection...
googling didnt give me a thing
thanks
EDIT1
I tried making a static function for checking the styles, but it did not help. Here's the code
static public class Helpers
{
static public Word.Paragraph checkPars(List<Word.Paragraph> pars)
{
return pars.FirstOrDefault();//(x) => x.OutlineLevel == Word.WdOutlineLevel.wdOutlineLevelBodyText);
}
}
As you can see, I had to remove the actual check, since it was causing the cursor to blink and lose selection
please advise
We use the Application.ScreenUpdating = true; and this keep the selection for all properties in Paragraph except for the Range property.
Then, we tried to access the range via Reflection and this was the solution.
Range rng = (Range)typeof(Paragraph).GetProperty("Range").GetValue(comObj);
We tried to eliminate querying ActiveDocument thinking that that might have had side-effects that were causing the problem but that was not the case.
Then, we confirmed that the selection was not "lost" and screen-updating is the only problem, so we tried restoring the UI with Application.ScreenRefresh and while it did work, it causes the screen to flicker every time the timer fires and this is not good enough.
Finally, knowing that it's only a UI problem, I tried simply switching off Application.ScreenUpdating...
in ThisAddin
private void ThisAddIn_Startup(object sender, System.EventArgs e)
{
Timer timer = new Timer(2000);
timer.Elapsed += (s, t) =>
{
var scrnUpdating = Application.ScreenUpdating;
Application.ScreenUpdating = false;
MainTimer.onElapsed(Application, t);
if (scrnUpdating)
Application.ScreenUpdating = true;
};
timer.Start();
}
In another class library (note that it's static, I still think this is the best way)...
public static class MainTimer
{
public static void onElapsed (Word.Application state, System.Timers.ElapsedEventArgs e)
{
if (state.Documents.Count > 0)
{
var doc = state.ActiveDocument;
Word.InlineShapes shps;
Word.Paragraphs pars;
try
{
pars = doc.Paragraphs;
}
catch (Exception)
{
return;
}
var pars2 = pars.Cast<Word.Paragraph>()
.Where(p => p.OutlineLevel == Word.WdOutlineLevel.wdOutlineLevelBodyText)
.Select(p => p) // do stuff with the selected parragraphs...
.ToList();
}
}
}
And this works for me.
The selection remains and is displayed in the UI without flicker.
I also eliminated some premature enumeration from your code: you don't meed the .ToList() before the foreach.

Creating testcases using nunit and moq on WEB API controllers

I am trying to create a unit test cases using nunit on controllers in an existing Web API project.
I am using Moq as mocking framework. In here I would like to mock repository to return some expected
result.I am not sure what I can do at the point of setting up of repository mockup and validating result.
Can somebody suggest if whatever I am doing it right ? I just took a random controller for this question.
Any advise or guidance to some example url would be greatly appreciated.
[TestCase]
public void TestMethod1()
{
AccountController ac = new AccountController();
var mockAuthRepository = new Mock<AuthRepository>();
//mockAuthRepository.Setup(m=>m.RegisterUser(It.IsAny<UserModel>))
}
[RoutePrefix("api/Account")]
public class AccountController : ApiController
{
private readonly AuthRepository _repo;
public AccountController()
{
_repo = new AuthRepository();
}
}
[HttpPost]
[Authorize(Users = "admin")]
[Route("Register")]
public async Task<IHttpActionResult> Register(UserModel userModel)
{
IdentityResult result = await _repo.RegisterUser(userModel);
IHttpActionResult errorResult = WrapError(result);
if (errorResult != null)
{
return errorResult;
}
return Ok();
}
private IHttpActionResult WrapError(IdentityResult result)
{
if (result == null)
{
return InternalServerError();
}
if (!result.Succeeded)
{
if (result.Errors != null)
{
foreach (string err in result.Errors)
{
ModelState.AddModelError("", err);
}
}
if (ModelState.IsValid)
{
return BadRequest();
}
return BadRequest(ModelState);
}
return null;
}
In your any specific test use setups like these:
mockAuthRepository
.Setup(m => m.RegisterUser(It.IsAny<UserModel>))
.Returns(new List<UserModel> {
new UserModel { // specific properties },
new UserModel { // specific properties },
new UserModel { // specific properties }
}.AsQueryable());
And then just make assertion for selected users.
For example, you can test Register method to prevent registering existing user by placing in mockAuthRepository the same user that was in controller input and assert that specific error code is generated.
You can also use mocks to check if some code is called by using Verify method with param Times.Once, Times.Never etc..
Good luck!