Await Action() is not awaited - c#

I'm using ZXing.Net.Mobile library to scan QR codes. I would like to put the code responsible for scanning to separate class so I could just call a method from it and it would return the result. Something like that:
var scanner = new ZXing.Mobile.MobileBarcodeScanner();
var result = await scanner.Scan();
if (result != null)
Console.WriteLine("Scanned Barcode: " + result.Text);
It's an example from official repository, but it doesn't work (at least for me).
So I created a class and put the code that DID work form me elsewhere. Here's the class code:
public class QrHandler
{
public ZXingScannerPage scanPage { get; set; }
public string Result { get; set; }
public async Task<string> Scan()
{
var options = new ZXing.Mobile.MobileBarcodeScanningOptions
{
PossibleFormats = new List<ZXing.BarcodeFormat>
{
ZXing.BarcodeFormat.QR_CODE
},
TryHarder = false,
AutoRotate = false,
TryInverted = false,
};
scanPage = new ZXingScannerPage();
scanPage.AutoFocus();
scanPage.OnScanResult += (result) =>
{
scanPage.IsScanning = false;
Device.BeginInvokeOnMainThread(async () =>
{
Application.Current.MainPage.Navigation.PopAsync();
try
{
Result = result.Text;
}
catch (Exception ex)
{
Result = ex.Message;
}
});
};
await Application.Current.MainPage.Navigation.PushAsync(scanPage);
return Result;
}
}
And I'm calling it from a method like below:
public async Task<string> Validate()
{
string _Result = "OK";
QrHandler q = new QrHandler();
_Result = await q.Scan();
return _Result;
}
The problem is that Validate() returns _Result before q.Scan() returns any value. Shouldn't it be fired when after q.Scan() completes?

You can't return from Scan() until the OnScanResult event handler has been executed.
It's unclear when this event is being raised but provided that it's atually raised when you'd expect, you may block asynchronously in the Scan() method using a SemaphoreSlim:
public async Task<string> Scan()
{
var options = new ZXing.Mobile.MobileBarcodeScanningOptions
{
PossibleFormats = new List<ZXing.BarcodeFormat>
{
ZXing.BarcodeFormat.QR_CODE
},
TryHarder = false,
AutoRotate = false,
TryInverted = false,
};
scanPage = new ZXingScannerPage();
scanPage.AutoFocus();
using (SemaphoreSlim semaphoreSlim = new SemaphoreSlim(0, 1))
{
scanPage.OnScanResult += (result) =>
{
scanPage.IsScanning = false;
Device.InvokeOnMainThread(async () =>
{
await Application.Current.MainPage.Navigation.PopAsync();
try
{
Result = result.Text;
}
catch (Exception ex)
{
Result = ex.Message;
}
semaphore.Release();
});
};
await Application.Current.MainPage.Navigation.PushAsync(scanPage);
await semaphoreSlim.WaitAsync();
}
return Result;
}

Related

How to write unit test case for SaveChangesAsync()

I am writing unit test case for my Entity Framework Core SaveAsync method.
try
{
// Here add business logic to insert record in to new database
var dbObj = new TableName()
{
Code = 1,
Description = "test"
};
_ = _dbContext.TableName.AddAsync(dbObj);
_ = _dbContext.SaveChangesAsync(context.CancellationToken);
}
catch (Exception ex)
{
_logger.LogCritical($"{GetType().Name}:{nameof(Consume)} {ex}");
}
await Task.CompletedTask;
I have written unit test case by mocking:
Mock<MyDbContext> dbContext = new();
var data = return new List<TableName>()
{
new TableName
{
Id = 1,
Description = "test"
}
};
dbContext.Setup(r => r.TableName).ReturnsDbSet(data);
dbContext.Setup(r => r.SaveChangesAsync(It.IsAny<CancellationToken>())).ReturnsAsync(1);
// Check that each method was only called once.
//dbContext.Verify(x => x.TableName.Add(It.IsAny<TableName>()), Times.Once());
dbContext.Verify(x => x.SaveChangesAsync(It.IsAny<CancellationToken>()), Times.Once());
Below is what I get as a result
Moq.MockException : Expected invocation on the mock once, but was 0 times
While debugging the test case, I am getting the below.
Firstly,add await into try catch block.otherwise it would not catch excption
try
{
// Here add business logic to insert record in to new database
var dbObj = new TableName()
{
Code = 1,
Description = "test"
};
_ = await _dbContext.TableName.AddAsync(dbObj);
_ = await _dbContext.SaveChangesAsync(context.CancellationToken);
}
//remove this line
await Task.CompletedTask;
The document related with Asynchronous programming
And you could check the document related with testing non query scenarios
I tried as below:
Assuming a controller:
public async Task<IActionResult> Create([Bind("Id,Name")] SomeEntity someEntity)
{
if (ModelState.IsValid)
{
try
{
await _context.AddAsync(someEntity);
await _context.SaveChangesAsync();
}
catch (Exception e)
{
}
return RedirectToAction(nameof(Index));
}
return View(someEntity);
}
Test:
public class UnitTest1
{
[Fact]
public async Task Test1()
{
var mockset = new Mock<DbSet<SomeEntity>>();
var mockdbcontext = new Mock<WebApplication8Context>(new DbContextOptions<WebApplication8Context>() );
mockdbcontext.Setup(x=>x.SomeEntity).Returns(mockset.Object);
var a = mockdbcontext.Object;
var controller = new SomeEntitiesController(mockdbcontext.Object);
await controller.Create(new SomeEntity() { Name = "someName" });
mockdbcontext.Verify(x => x.SaveChangesAsync(default(CancellationToken)), Times.Once);
}
}
Result:

Use transaction with ASP.NET identity

I am using ASP.NET identites userManager.CreateAsync so it automatically populates NormalizedEmail from email field and stuff like that. However this time I want to insert user together with some metadata (in some other tables) which means I need transaction.
I successfully made it work without throwing exception but it seems transaction is not rollbacked (UserManager uses some different dbContext?)
Here is code:
try
{
await ResilientTransaction.New(_databaseContext).ExecuteAsync(async () =>
{
var currentUser = await _userManager.FindByIdAsync(_identityContext.UserId.ToString());
user.ParentUserId = _identityContext.UserId;
var existingUserWithEmail = await FindByEmailAsync(user.Email);
if (existingUserWithEmail != null)
{
throw new ValidationException($"User with email {user.Email} already exists");
}
var currentUserRoles = await _roleRepository.GetWithPermissionsByUserId(_identityContext.UserId);
var result = await _userManager.CreateAsync(user);
var rolesResult = await _userManager.AddToRolesAsync(user, currentUserRoles.Select(x => x.Name));
if (!result.Succeeded)
{
throw new ValidationException(result.Errors.FirstOrDefault()!.Description);
}
if (currentUser.OverrideVehicleAccessSettingsByEmailDomainName)
{
user.OverrideVehicleAccessSettingsByEmailDomainName = true;
var vehicleAccessSettings = await _vehicleAccessSettingRepository.GetAll(x => x.UserId == _identityContext.UserId);
foreach (var vehicleAccessSetting in vehicleAccessSettings)
{
vehicleAccessSetting.UserId = user.Id;
// vehicleAccessSetting.Id = 0;
_ = await _vehicleAccessSettingRepository.Insert(vehicleAccessSetting);
}
}
user.Roles = currentUserRoles;
});
}
catch (Exception ex)
{
throw;
}
And this ResillianteTransaction is just this:
public class ResilientTransaction
{
private readonly DbContext _context;
private ResilientTransaction(DbContext context) =>
_context = context ?? throw new ArgumentNullException(nameof(context));
public static ResilientTransaction New(DbContext context) =>
new ResilientTransaction(context);
public async Task ExecuteAsync(Func<Task> action)
{
// Use of an EF Core resiliency strategy when using multiple DbContexts
// within an explicit BeginTransaction():
// https://learn.microsoft.com/ef/core/miscellaneous/connection-resiliency
var strategy = _context.Database.CreateExecutionStrategy();
await strategy.ExecuteAsync(async () =>
{
await using var transaction = await _context.Database.BeginTransactionAsync();
try
{
await action();
await transaction.CommitAsync();
}
catch (Exception)
{
await transaction.RollbackAsync();
throw;
}
});
}
}

ASP.NET Core - call one controller method from another

I need to call from create method delete for cleanup. I want to call DeleteDevice like lazy cleanup and do not care if it will succeed or not.
People suggested to use this way:
DeleteDevice(param).ConfigureAwait(false);
Is it safe?
public async Task<ActionResult<Device>> CreateDevice([FromBody] CreateDeviceRequest request)
{
if (!ModelState.IsValid)
return BadRequest(ModelState);
try
{
var registeredDevice = await RegisterDevice(request.DisplayName);
bool isCreatePrinterSucceed = false;
try
{
var updatedDevice = await UpdateDevice(registeredDevice.Id);
isCreatePrinterSucceed = true;
return Ok(new DeviceReference
{
DisplayName = request.DisplayName,
Id = updatedDevice.Id
});
}
finally
{
if (!isCreatePrinterSucceed)
{
var param = new DeleteDeviceRequest()
{
Id = registeredDevice.Id,
AzureUserBearerToken = request.AzureUserBearerToken
};
DeleteDevice(param).ConfigureAwait(false); ;
}
}
}
catch (Exception ex)
{
return StatusCode((int)HttpStatusCode.InternalServerError, ex.Message);
}
}

ListBox.Invoke() in .Net Core 3.1 in WindowsFormApp with thread

I want to add some items to listbox with a thread or backgroundworker in C# .Net core 3.1. But when I call PridaniDoLB(ListBox lb, Messages messages) function in a loop my program is not responding. And when I am in Debug mod, I get an error message. How can I fix that?
error message
1)
private async Task Client_Log(LogMessage args)
{
listStandard.Add(new Messages("Standard", args.Source.ToString(), args.Message.ToString(), DateTime.Now));
PridaniDoLB(lb_standard, listStandard[listStandard.Count() - 1]);
}
public delegate void Update();
public void PridaniDoLB(ListBox lb, object messages)
{
if (lb.InvokeRequired)
{
//lb.Invoke(new MethodInvoker(delegate
//{
// lb.Items.Add(messages);
//}));
lb.Invoke(new Update(() => lb.Items.Add(messages)));
}
else
{
lb.Items.Add(messages);
}
}
2)MainAsync
private async Task MainAsync()
{
client = new DiscordSocketClient(new DiscordSocketConfig
{
LogLevel = LogSeverity.Debug
});
commands = new CommandService(new CommandServiceConfig
{
CaseSensitiveCommands = true,
DefaultRunMode = RunMode.Async,
LogLevel = LogSeverity.Debug
});
client.MessageReceived += Client_MessageReceived;
await commands.AddModulesAsync(Assembly.GetEntryAssembly(), serviceProvider);
//joinAudioManager.JoinTask();
client.Ready += Client_Ready;
client.Log += Client_Log;
string token = "";
using (var stream = new FileStream((Path.GetDirectoryName(Assembly.GetEntryAssembly().Location)).Replace(#"bin\Debug\netcoreapp3.1"
, #"Core\Data\Token.txt"), FileMode.Open, FileAccess.Read))
using (var readToken = new StreamReader(stream))
{
token = readToken.ReadToEnd();
}
await client.LoginAsync(TokenType.Bot, token);
await client.StartAsync();
await Task.Delay(-1);
}
3) Class Messages
class Messages
{
public string nazev, text, result;
public DateTime cas;
public override string ToString()
{
//Console.WriteLine($"[{DateTime.Now} at {args.Source}] {args.Message}");
return $"[{cas} at {text}] {result}";
}
public Messages(string nazev, string text, string result, DateTime cas)
{
this.nazev = nazev;
this.text = text;
this.result = result;
this.cas = cas;
}
}

Async await call does not return

I have a problem, where i call an async method, and the call does not return back. I assume its a race of threads. How do I write this correctly?
This is where it starts. I first call an async method called "GetCachedValuesAsync"
public void OnNavigatingTo(NavigationParameters parameters)
{
Task.Run(async () =>
{
await GetCachedValuesAsync();
ClipRefernce = GenerateRefernce(clips);
});
}
Here is the method signature for GetCachedValueAsync
public async Task GetCachedValuesAsync()
{
try
{
clips = await BlobCache.LocalMachine.GetObject<List<Clip>>("clips");
}
catch (KeyNotFoundException ex)
{
clips = new List<Clip>();
}
}
I do not get the call returned from BlobCache, BlobCahce method is part of a library called akavache.
The code also does not hit: ClipRefernce = GenerateRefernce(clips);
I appreciate your help
Edit 1
This is GenerateRefernce method.
public string GenerateRefernce(List<Clip> clips)
{
string refernce = "";
if(clips.Count > 0)
{
var clip = clips.LastOrDefault();
refernce = String.Format("Ref {0:yyyy}/{1}",DateTime.Now , clip.ClipId + 1);
}
else{
refernce = String.Format("Ref {0:yyyy}/{1}", DateTime.Now, 1);
}
return refernce;
}
You need to remove the sync method from the Task.Run like this:
public void OnNavigatingTo(NavigationParameters parameters)
{
Task.Run(async () =>
{
await GetCachedValuesAsync();
});
ClipRefernce = GenerateRefernce(clips);
}
public async Task GetCachedValuesAsync()
{
try
{
clips = await BlobCache.LocalMachine.GetObject<List<Clip>>("clips");
}
catch (KeyNotFoundException ex)
{
clips = new List<Clip>();
}
}

Categories