After long searching I decided to ask here.
How can i control DC motors with Pololu DRV8835 Motor Driver?
I tried to use Microsoft.IoT.Lightning library, but frequency seems to be too low. In Python library for this driver frequency is 20 kHz.
When I tried to modify this library, build threw exception because of non-existing targets (but they didn't even appeared after library building).
This is my code:
public async Task Initialize()
{
try
{
if (!LightningProvider.IsLightningEnabled)
{
throw new Exception("No lightning provider detected!");
}
LowLevelDevicesController.DefaultProvider = LightningProvider.GetAggregateProvider();
Gpio = await GpioController.GetDefaultAsync();
APhase = Gpio.OpenPin(19);
BPhase = Gpio.OpenPin(6);
APhase.SetDriveMode(GpioPinDriveMode.Output);
BPhase.SetDriveMode(GpioPinDriveMode.Output);
Pwm = await PwmController.GetDefaultAsync();
Pwm.SetDesiredFrequency(1000);
AEnable = Pwm.OpenPin(26);
BEnable = Pwm.OpenPin(13);
}
catch (Exception e)
{
throw e;
}
}
private bool stopMove = true;
private double speed = 1;
public void StopMove()
{
stopMove = true;
}
public async Task MoveForward()
{
await Task.Delay(1);
stopMove = false;
APhase.Write(GpioPinValue.Low);
BPhase.Write(GpioPinValue.Low);
AEnable.SetActiveDutyCyclePercentage(speed);
BEnable.SetActiveDutyCyclePercentage(speed);
Debug.WriteLine("Starting...");
AEnable.Start();
BEnable.Start();
while (!stopMove) { }
AEnable.Stop();
BEnable.Stop();
AEnable.SetActiveDutyCyclePercentage(0);
BEnable.SetActiveDutyCyclePercentage(0);
}
public async Task MoveBackward()
{
await Task.Delay(1);
stopMove = false;
APhase.Write(GpioPinValue.High);
BPhase.Write(GpioPinValue.High);
AEnable.SetActiveDutyCyclePercentage(speed);
BEnable.SetActiveDutyCyclePercentage(speed);
AEnable.Start();
BEnable.Start();
while (!stopMove) { }
AEnable.Stop();
BEnable.Stop();
AEnable.SetActiveDutyCyclePercentage(0);
BEnable.SetActiveDutyCyclePercentage(0);
}
Does anyone know any working solution?
There are two ways you can have a try:
First, using Microsoft.IoT.Lightning library. There are two PWM control providers:
Software simulate PWM. But you can see the max frequency is 1kHz. In order to use higher frequency you can modify the constant and reference the lightning source code for you project, instead of the nugget one. However, Since it’s using software timing, you might have slewed waveforms in you make aggressive attempt.
PCA9685. For doing this you need a external PCA9685 module and some electronic circuits. Likewise, you need reference source code the modify the max frequency that defined.
Second, using the out of box driver BSP. This driver support on-chip PWM hardware. Try to use higher frequency to see if the signal generated meets your requirement.
Related
When using Apache Thrift [https://github.com/apache/thrift] to create a non blocking server in C#, the following Classes/Types cannot be recognized:
TNonblockingServerTransport
TNonblockingServer
I want to send command from my win10 laptop to control a time-consuming calculation performed on a high performance server (ubuntu). That's why I came to Apache Thrift. I have found the official C# version tutorial [https://github.com/apache/thrift/tree/master/tutorial/csharp] and it works well. This tutorial uses the so-called Blocking Mode (TSimpleServer). But in my situation, the time-consuming calculation procedure should be interrupt-able. Consequently, I must use a non-blocking server.
The logic is simple. For the server, I used a private flag forceStop. If the Client call Stop(), forceStop will set to true and the calculation loop will break.
// #Server#
// Server Set-Up
private void SetUp()
{
try
{
CalculatorHandler handler = new CalculatorHandler();
Calculator.Processor processor = new
Calculator.Processor(handler);
var serverTransport = new TServerSocket(9090);
TServer server = new TSimpleServer(processor, serverTransport);
// Use this for a multithreaded server
// server = new TThreadPoolServer(processor, serverTransport);
Console.WriteLine("Starting the server...");
server.Serve();
}
catch (Exception x)
{
Console.WriteLine(x.StackTrace);
}
}
private bool forceStop;
public int TimeConsumingOperation(int n1, int n2)
{
Console.WriteLine("add({0},{1})", n1, n2);
for (int i = 0; i < 10; i++)
{
//calculating
Thread.Sleep(500);
if (forceStop)
{
Quit();
}
}
return n1 + n2;
}
public void Stop()
{
forceStop = true;
}
// Client
// Button#1 Click callback
private void Button_Start_Click()
{
client.TimeConsumingOperation(0,0);
}
// Button#2 Click callback
private void Button_Stop_Click()
{
client.Stop();
}
//
I've found some useful examples in java [https://chamibuddhika.wordpress.com/2011/10/02/apache-thrift-quickstart-tutorial/]. I've try my best to convert the java code of non-block server to the corresponding C# code but I found that there seems to be no TNonblockingServerTransport in C#. Anyone could help me with this probelm?
// Java Code
public class NonblockingServer {
private void start() {
try {
TNonblockingServerTransport serverTransport = new TNonblockingServerSocket(7911);
ArithmeticService.Processor processor = new ArithmeticService.Processor(new ArithmeticServiceImpl());
TServer server = new TNonblockingServer(new TNonblockingServer.Args(serverTransport).
processor(processor));
System.out.println("Starting server on port 7911 ...");
server.serve();
} catch (TTransportException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
NonblockingServer srv = new NonblockingServer();
srv.start();
}
}
There's actually two answers to that question.
Possible implementation
Your setup is flawed, to begin with.
// #Server#
// Server Set-Up
private bool forceStop;
public void Stop()
{
forceStop = true;
}
Let's assume, we have two clients, both starting a new calculation. Now one client wants to abort. What happens?
The solution would be to structure it in a way where the calculation is a separate business logic object that is instantiated at TimeConsumingOperation() and made available to the client by some means, typically by returning some sort of ID.
When the client now wants to abort, it calls Stop(calcualtionID). The server side logic now routes that call to the implementation and triggers whatever the abort mechanism might be, with C# is is probably a CancellationToken.
A third call would be needed to query the final results from the server end once the calculation has finished. Note that we still work with a TSimpleServer and the reason it works is because we avoid blocking calls by means of the API design.
Nonblocking server
Yes, there is no implementation yet for C#. Since Thrift is Open Source, that probably simply means that there were nobody running into that use case so far and wanted to spend time on an implementation. That is not to say that such a use case may not exist.
What exists are
Threaded and Threadpool servers
Task.Run(() => { your code })
which may help to solve your use case. Also, when used with ASP.NET there is no need for a nonblocking server since the runtime offers enough support already for multiple connections.
Bottom line
There are certain ways to work around that limitation you experierenced. One additional alternative could be to become a contributor by porting one of the existing (e.g. Java) nonblocking implementation to NetStd (preferred, since C# and NetCore will mature into "deprecated" state with the next release and both be replaced by NetStd eventually)
I'm trying to create an app. Here I'm trying to get the user's current location by clicking the button. But it produces an exception like Not Implemented exception - This functionality is not implemented in the portable version of this assembly. You should reference the NuGet package from your main application project in order to reference the platform-specific implementation
Already I did clean and rebuild the solution.
And I've enabled the permission for access_fine_location,
access_coarse_location.
I've added plugin.current and add an activity inside the mainactivity.cs
file.
string answer ="";
try
{
await CrossGeolocator.Current.StartListeningAsync(new
TimeSpan(20000),10);
if (CrossGeolocator.Current.IsListening)
{
var locator = CrossGeolocator.Current;
locator.DesiredAccuracy = 50;
var position = await
locator.GetPositionAsync(TimeSpan.FromSeconds(20000));
string lat = position.Latitude.ToString();
string lon = position.Longitude.ToString();
answer = lat + lon;
}
else
{
answer= "Not listening";
}
}
catch (Exception ex)
{
answer= ex.Message;
}
I need the result which contains the longitude and latitude values.
What I've to do in my project?
Edited :
public async Task<String> GetLastKnownLocationAsync()
{
Position ObjPosition = null;
try
{
await DependencyService.Get<IGeolocator>().StartListeningAsync(new TimeSpan(20000), 10);
if (CrossGeolocator.Current.IsListening)
{
var locator = CrossGeolocator.Current;
locator.DesiredAccuracy = 50;
ObjPosition = await DependencyService.Get<IGeolocator>().GetPositionAsync(TimeSpan.FromSeconds(20));
string lat = ObjPosition.Latitude.ToString();
string lon = ObjPosition.Longitude.ToString();
Info = lat + lon;
}
else
{
Info = "Not listening";
}
return Info;
}
catch (Exception ex)
{
Info = ex.Message;
return Info;
}
}
I'm getting error on the sixth line.
I have implemented GPS tracking functionality in my Xamarin.Forms application too. And as per my personal experience, Geolocator plugin doesn't work as expected with Xamarin.Forms and also have some issues and limitations too. This plugin is developed by taking reference of Xamarin Essentials Geolocation. I will suggest that you should use Xamarin Essentials instead of Geolocator plugin as it is well documented and easy to implement without any major issues. You can find step by step guid to implement Xamarin Essentials Geolocation from following link:
https://learn.microsoft.com/en-us/xamarin/essentials/geolocation?tabs=android
Did you install the NuGet package on both your shared project as well as your platform projects? The error message, in this case, is quite accurate. What basically happens here is that this plugin installs a form of dependency service that has a platform-specific implementation and just an interface on your shared project.
For some reason, your calls end up in the shared code, which only implements this exception to let you know you're in the wrong place. This is usually due to not having the package installed on your platform project, or, the package being "optimized away" by the linker. This tends to happen because the compiler notices there is no reference from your project to this library, so it strips it to let it take up less space.
To make sure this last thing doesn't happen, you can go into your platform project, in this case, Android and go into the MainActivity.cs and add a dummy reference to an object in this plugin. For instance, add this:
protected override void OnCreate(Bundle savedInstanceState)
{
TabLayoutResource = Resource.Layout.Tabbar;
ToolbarResource = Resource.Layout.Toolbar;
base.OnCreate(savedInstanceState);
global::Xamarin.Forms.Forms.Init(this, savedInstanceState);
// THIS WAS ADDED
var foo = new Plugin.Geolocator.Abstractions.Address();
LoadApplication(new App());
}
This is actually the reason a lot of these libraries used to have an Init method.
Having said all this, I actually have to agree with Akshay in the other answer and if at all possible, you probably want to look at upgrading your project to use .NET Standard and move to the Xamarin.Essentials library which takes all of this pain away.
Here is a little advice for you, that I usually use: if you have an issue and can't resolve it, try to create a separate little project with step-by-step tutorial of how that works. Usually it works, and you can find out what exactly wrong with your main solution.
EDIT:
Link to DependencyService
Shortly, you have to create Interface that would be used by you, i.e. IMyInterface. After that, write platform-specific classes for Android/iOS with interface implementation. When you write it, you can use the methods like so:
DependencyService.Get<IMyInterface>().YourMethod().
EDIT 2:
You have to add this for your platform-specific classes:
[assembly: Dependency(typeof(MyCustomClass))] // Check this assembly
namespace ISSO_I.Droid.PlatformSpecific
{
public class MyCustomClass: IMyInterface
{
/// your code here
}
}
EDIT 3:
Call location Updates:
protected override void OnAppearing()
{
base.OnAppearing();
ToIssoView = false;
RequestLocationUpdates(); // Call this method
}
Method to request location updates:
public async void RequestLocationUpdates()
{
/// check permission for location updates
var hasPermission = await CommonStaffUtils.CheckPermissions(Permission.Location);
if (!hasPermission)
return;
if (CrossGeolocator.Current.IsListening) return;
MyMap.MyLocationEnabled = true;
CrossGeolocator.Current.PositionChanged += Current_PositionChanged;
CrossGeolocator.Current.PositionError += Current_PositionError;
await CrossGeolocator.Current.StartListeningAsync(TimeSpan.FromSeconds(1), 5);
}
public static async Task<bool> CheckPermissions(Permission permission)
{
var permissionStatus = await CrossPermissions.Current.CheckPermissionStatusAsync(permission);
var request = false;
if (permissionStatus == PermissionStatus.Denied)
{
if (Device.RuntimePlatform == Device.iOS)
{
var title = $"{permission} Permission";
var question = $"To use this plugin the {permission} permission is required. Please go into Settings and turn on {permission} for the app.";
const string positive = "Settings";
const string negative = "Maybe Later";
var task = Application.Current?.MainPage?.DisplayAlert(title, question, positive, negative);
if (task == null)
return false;
var result = await task;
if (result)
{
CrossPermissions.Current.OpenAppSettings();
}
return false;
}
request = true;
}
if (!request && permissionStatus == PermissionStatus.Granted) return true;
{
var newStatus = await CrossPermissions.Current.RequestPermissionsAsync(permission);
if (!newStatus.ContainsKey(permission) || newStatus[permission] == PermissionStatus.Granted) return true;
var title = $"{permission} Permission";
var question = $"To use the plugin the {permission} permission is required.";
const string positive = "Settings";
const string negative = "Maybe Later";
var task = Application.Current?.MainPage?.DisplayAlert(title, question, positive, negative);
if (task == null)
return false;
var result = await task;
if (result)
{
CrossPermissions.Current.OpenAppSettings();
}
return false;
}
}
I need to test if there's any memory leak in our application and monitor to see if memory usage increases too much while processing the requests.
I'm trying to develop some code to make multiple simultaneous calls to our api/webservice method. This api method is not asynchronous and takes some time to complete its operation.
I've made a lot of research about Tasks, Threads and Parallelism, but so far I had no luck. The problem is, even after trying all the below solutions, the result is always the same, it appears to be processing only two requests at the time.
Tried:
-> Creating tasks inside a simple for loop and starting them with and without setting them with TaskCreationOptions.LongRunning
-> Creating threads inside a simple for loop and starting them with and without high priority
-> Creating a list of actions on a simple for loop and starting them using
Parallel.Foreach(list, options, item => item.Invoke)
-> Running directly inside a Parallel.For loop (below)
-> Running TPL methods with and without Options and TaskScheduler
-> Tried with different values for MaxParallelism and maximum threads
-> Checked this post too, but it didn't help either. (Could I be missing something?)
-> Checked some other posts here in Stackoverflow, but with F# solutions that I don't know how to properly translate them to C#. (I never used F#...)
(Task Scheduler class taken from msdn)
Here's the basic structure that I have:
public class Test
{
Data _data;
String _url;
public Test(Data data, string url)
{
_data = data;
_url = url;
}
public ReturnData Execute()
{
ReturnData returnData;
using(var ws = new WebService())
{
ws.Url = _url;
ws.Timeout = 600000;
var wsReturn = ws.LongRunningMethod(data);
// Basically convert wsReturn to my method return, with some logic if/else etc
}
return returnData;
}
}
sealed class ThreadTaskScheduler : TaskScheduler, IDisposable
{
// The runtime decides how many tasks to create for the given set of iterations, loop options, and scheduler's max concurrency level.
// Tasks will be queued in this collection
private BlockingCollection<Task> _tasks = new BlockingCollection<Task>();
// Maintain an array of threads. (Feel free to bump up _n.)
private readonly int _n = 100;
private Thread[] _threads;
public TwoThreadTaskScheduler()
{
_threads = new Thread[_n];
// Create unstarted threads based on the same inline delegate
for (int i = 0; i < _n; i++)
{
_threads[i] = new Thread(() =>
{
// The following loop blocks until items become available in the blocking collection.
// Then one thread is unblocked to consume that item.
foreach (var task in _tasks.GetConsumingEnumerable())
{
TryExecuteTask(task);
}
});
// Start each thread
_threads[i].IsBackground = true;
_threads[i].Start();
}
}
// This method is invoked by the runtime to schedule a task
protected override void QueueTask(Task task)
{
_tasks.Add(task);
}
// The runtime will probe if a task can be executed in the current thread.
// By returning false, we direct all tasks to be queued up.
protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued)
{
return false;
}
public override int MaximumConcurrencyLevel { get { return _n; } }
protected override IEnumerable<Task> GetScheduledTasks()
{
return _tasks.ToArray();
}
// Dispose is not thread-safe with other members.
// It may only be used when no more tasks will be queued
// to the scheduler. This implementation will block
// until all previously queued tasks have completed.
public void Dispose()
{
if (_threads != null)
{
_tasks.CompleteAdding();
for (int i = 0; i < _n; i++)
{
_threads[i].Join();
_threads[i] = null;
}
_threads = null;
_tasks.Dispose();
_tasks = null;
}
}
}
And the test code itself:
private void button2_Click(object sender, EventArgs e)
{
var maximum = 100;
var options = new ParallelOptions
{
MaxDegreeOfParallelism = 100,
TaskScheduler = new ThreadTaskScheduler()
};
// To prevent UI blocking
Task.Factory.StartNew(() =>
{
Parallel.For(0, maximum, options, i =>
{
var data = new Data();
// Fill data
var test = new Test(data, _url); //_url is pre-defined
var ret = test.Execute();
// Check return and display on screen
var now = DateTime.Now.ToString("HH:mm:ss");
var newText = $"{Environment.NewLine}[{now}] - {ret.ReturnId}) {ret.ReturnDescription}";
AppendTextBox(newText, ref resultTextBox);
}
}
public void AppendTextBox(string value, ref TextBox textBox)
{
if (InvokeRequired)
{
this.Invoke(new ActionRef<string, TextBox>(AppendTextBox), value, textBox);
return;
}
textBox.Text += value;
}
And the result that I get is basically this:
[10:08:56] - (0) OK
[10:08:56] - (0) OK
[10:09:23] - (0) OK
[10:09:23] - (0) OK
[10:09:49] - (0) OK
[10:09:50] - (0) OK
[10:10:15] - (0) OK
[10:10:16] - (0) OK
etc
As far as I know there's no limitation on the server side. I'm relatively new to the Parallel/Multitasking world. Is there any other way to do this? Am I missing something?
(I simplified all the code for clearness and I believe that the provided code is enough to picture the mentioned scenarios. I also didn't post the application code, but it's a simple WinForms screen just to call and show results. If any code is somehow relevant, please let me know, I can edit and post it too.)
Thanks in advance!
EDIT1: I checked on the server logs that it's receiving the requests two by two, so it's indeed something related to sending them, not receiving.
Could it be a network problem/limitation related to how the framework manages the requests/connections? Or something with the network at all (unrelated to .net)?
EDIT2: Forgot to mention, it's a SOAP webservice.
EDIT3: One of the properties that I send (inside data) needs to change for each request.
EDIT4: I noticed that there's always an interval of ~25 secs between each pair of request, if it's relevant.
I would recommend not to reinvent the wheel and just use one of the existing solutions:
Most obvious choice: if your Visual Studio license allows you can use MS Load Testing Framework, most likely you won't even have to write a single line of code: How to: Create a Web Service Test
SoapUI is a free and open source web services testing tool, it has some limited load testing capabilities
If for some reasons SoapUI is not suitable (i.e. you need to run load tests in clustered mode from several hosts or you need more enhanced reporting) you can use Apache JMeter - free and open source multiprotocol load testing tool which supports web services load testing as well.
A good solution to create load tests without write a own project is use this service https://loader.io/targets
It is free for small tests, you can POST Parameters, Header,... and you have a nice reporting.
Isnt the "two requests at a time" the result of the default maxconnection=2 limit on connectionManagement?
<configuration>
<system.net>
<connectionManagement>
<add address = "http://www.contoso.com" maxconnection = "4" />
<add address = "*" maxconnection = "2" />
</connectionManagement>
</system.net>
</configuration>
My favorite load testing library is NBomber. It has an easy and powerful API, realistic user simulations, and provides you with nice HTML reports about latency and requests per second.
I used it to test my API and wrote an article about how I did it.
I will try to tell my problem in as simple words as possible.
In my UWP app, I am loading the data async wise on my Mainpage.xaml.cs`
public MainPage()
{
this.InitializeComponent();
LoadVideoLibrary();
}
private async void LoadVideoLibrary()
{
FoldersData = new List<FolderData>();
var folders = (await Windows.Storage.StorageLibrary.GetLibraryAsync
(Windows.Storage.KnownLibraryId.Videos)).Folders;
foreach (var folder in folders)
{
var files = (await folder.GetFilesAsync(Windows.Storage.Search.CommonFileQuery.OrderByDate)).ToList();
FoldersData.Add(new FolderData { files = files, foldername = folder.DisplayName, folderid = folder.FolderRelativeId });
}
}
so this is the code where I am loading up a List of FolderData objects.
There in my other page Library.xaml.cs I am using that data to load up my gridview with binding data.
protected override void OnNavigatedTo(NavigationEventArgs e)
{
try
{
LoadLibraryMenuGrid();
}
catch { }
}
private async void LoadLibraryMenuGrid()
{
MenuGridItems = new ObservableCollection<MenuItemModel>();
var data = MainPage.FoldersData;
foreach (var folder in data)
{
var image = new BitmapImage();
if (folder.files.Count == 0)
{
image.UriSource = new Uri("ms-appx:///Assets/StoreLogo.png");
}
else
{
for (int i = 0; i < folder.files.Count; i++)
{
var thumb = (await folder.files[i].GetThumbnailAsync(Windows.Storage.FileProperties.ThumbnailMode.VideosView));
if (thumb != null) { await image.SetSourceAsync(thumb); break; }
}
}
MenuGridItems.Add(new MenuItemModel
{
numberofvideos = folder.files.Count.ToString(),
folder = folder.foldername,
folderid = folder.folderid,
image = image
});
}
GridHeader = "Library";
}
the problem I am facing is that when i launch my application, wait for a few seconds and then i navigate to my library page, all data loads up properly.
but when i try to navigate to library page instantly after launching the app, it gives an exception that
"collection was modified so it cannot be iterated"
I used the breakpoint and i came to know that if i give it a few seconds the List Folder Data is already loaded properly asyncornously, but when i dnt give it a few seconds, that async method is on half way of loading the data so it causes exception, how can i handle this async situation? thanks
What you need is a way to wait for data to arrive. How you fit that in with the rest of the application (e.g. MVVM or not) is a different story, and not important right now. Don't overcomplicate things. For example, you only need an ObservableCollection if you expect the data to change while the user it looking at it.
Anyway, you need to wait. So how do you wait for that data to arrive?
Use a static class that can be reached from everywhere. In there put a method to get your data. Make sure it returns a task that you cache for future calls. For example:
internal class Data { /* whatever */ }
internal static class DataLoader
{
private static Task<Data> loaderTask;
public static Task<Data> LoadDataAsync(bool refresh = false)
{
if (refresh || loaderTask == null)
{
loaderTask = LoadDataCoreAsync();
}
return loaderTask;
}
private static async Task<Data> LoadDataCoreAsync()
{
// your actual logic goes here
}
}
With this, you can start the download as soon as you start the application.
await DataLoader.LoadDataAsync();
When you need the data in that other screen, just call that method again. It will not download the data again (unless you set refresh is true), but will simply wait for the work that you started earlier to finish, if it is not finished yet.
I get that you don't have enough experience.There are multiple issues and no solution the way you are loading the data.
What you need is a Service that can give you ObservableCollection of FolderData. I think MVVM might be out of bounds at this instance unless you are willing to spend a few hours on it. Though MVVM will make things lot easier in this instance.
The main issue at hand is this
You are using foreach to iterate the folders and the FolderData list. Foreach cannot continue if the underlying collection changes.
Firstly you need to start using a for loop as opposed to foreach. 2ndly add a state which denotes whether loading has finished or not. Finally use observable data source. In my early days I used to create static properties in App.xaml.cs and I used to use them to share / observe other data.
I have a fairly simple WPF application that uses Entity Framework. The main page of the application has a list of records that I am getting from a database on startup.
Each record has a picture, so the operation can be a little slow when the wireless signal is poor. I'd like this (and many of my SQL operations) to perform in the background if possible. I have async/await setup and at first it seemed to be working exactly as I wanted but now I'm seeing that my application is becoming unresponsive when accessing the DB.
Eventually I'm thinking I'm going to load up the text in one query and the images in another background operation and load them as they come in. This way I get the important stuff right away and the pictures can come in in the background, but the way things are going it's still looking like it will lock up if I do this.
On top of that, I'm trying to implement something to handle connectivity issues (in case the wifi cuts out momentarily) so that the application notifies the user of a connection issue, automatically retries a few times, etc. I put a try catch for SQL exception which seems to be working for me, but the whole application locks up for about a minute while it is trying to connect to the DB.
I tried testing my async/await using await Task.Delay() and everything is very responsive as expected while awaiting the delay, but everything locks up when awaiting the .ToListAsync(). Is this normal and expected? My understanding of async/await is pretty limited.
My code is kind of messy (I'm new) but it does what I need it to do for the most part. I understand there's probably plenty of improvements I can make and better ways to do things, but one step at a time here. My main goal right now is to keep the application from crashing during database accessing exceptions and to keep the user notified of what the application is doing (searching, trying to access db, unable to reach DB and retrying, etc) as opposed to being frozen, which is what they're going to think when they see it being unresponsive for over a minute.
Some of my code:
In my main view model
DataHelper data = new DataHelper();
private async void GetQualityRegisterQueueAsync()
{
try
{
var task = data.GetQualityRegisterAsync();
IsSearching = true;
await task;
IsSearching = false;
QualityRegisterItems = new ObservableCollection<QualityRegisterQueue>(task.Result);
OrderQualityRegisterItems();
}
catch (M1Exception ex)
{
Debug.WriteLine(ex.Message);
Debug.WriteLine("QualityRegisterLogViewModel.GetQualityRegisterQueue() Operation Failed");
}
}
My Data Helper Class
public class DataHelper
{
private bool debugging = false;
private const int MAX_RETRY = 2;
private const double LONG_WAIT_SECONDS = 5;
private const double SHORT_WAIT_SECONDS = 0.5;
private static readonly TimeSpan longWait = TimeSpan.FromSeconds(LONG_WAIT_SECONDS);
private static readonly TimeSpan shortWait = TimeSpan.FromSeconds(SHORT_WAIT_SECONDS);
private enum RetryableSqlErrors
{
ServerNotFound = -1,
Timeout = -2,
NoLock = 1204,
Deadlock = 1205,
}
public async Task<List<QualityRegisterQueue>> GetQualityRegisterAsync()
{
if(debugging) await Task.Delay(5000);
var retryCount = 0;
using (M1Context m1 = new M1Context())
{
for (; ; )
{
try
{
return await (from a in m1.QualityRegisters
where (a.qanClosed == 0)
//orderby a.qanAssignedDate descending, a.qanOpenedDate
orderby a.qanAssignedDate.HasValue descending, a.qanAssignedDate, a.qanOpenedDate
select new QualityRegisterQueue
{
QualityRegisterID = a.qanQualityRegisterID,
JobID = a.qanJobID.Trim(),
JobAssemblyID = a.qanJobAssemblyID,
JobOperationID = a.qanJobOperationID,
PartID = a.qanPartID.Trim(),
PartRevisionID = a.qanPartRevisionID.Trim(),
PartShortDescription = a.qanPartShortDescription.Trim(),
OpenedByEmployeeID = a.qanOpenedByEmployeeID.Trim(),
OpenedByEmployeeName = a.OpenedEmployee.lmeEmployeeName.Trim(),
OpenedDate = a.qanOpenedDate,
PartImage = a.JobAssembly.ujmaPartImage,
AssignedDate = a.qanAssignedDate,
AssignedToEmployeeID = a.qanAssignedToEmployeeID.Trim(),
AssignedToEmployeeName = a.AssignedEmployee.lmeEmployeeName.Trim()
}).ToListAsync();
}
catch (SqlException ex)
{
Debug.WriteLine("SQL Exception number = " + ex.Number);
if (!Enum.IsDefined(typeof(RetryableSqlErrors), ex.Number))
throw new M1Exception(ex.Message, ex);
retryCount++;
if (retryCount > MAX_RETRY) throw new M1Exception(ex.Message, ex); ;
Debug.WriteLine("Retrying. Count = " + retryCount);
Thread.Sleep(ex.Number == (int)RetryableSqlErrors.Timeout ?
longWait : shortWait);
}
}
}
}
}
Edit: Mostly looking for general guidance here, though a specific example of what to do would be great. For these types of operations where I am downloading data, is it just a given that if I need the application to be responsive I need to be making multiple threads? Is that a common solution to this type of problem? Is this not something I should be expecting async/await to solve?
If you call this method from your UI thread, you will overload the capture of UI thread context and back on itself. Also, your service will not be necessarily "Performant" because it must wait until the UI thread is free before it can continue.
The solution is simple: just call the method passing the ConfigureAwait "false" parameter when you made the call.
.ToListAsync().ConfigureAwaiter(false);
I hope it helps