I have the following task i would love to initialize once the app is running, and just populate a value in my App.xaml.cs
public partial class App : Application
{
public bool ProcessedTask = false;
public List<Item> Items = new List<Item>();
public App()
{
...
// Standard XAML initialization
InitializeComponent();
// NOW HERE I WOULD LIKE TO INVOKE MY TASK
}
public async Task<string> RequestDataTask()
{
HttpClient client = new HttpClient();
Task<string> getStringRequests = client.GetStringAsync("http://test.com/get/");
ItemsRootObject responseObject = Newtonsoft.Json.JsonConvert.DeserializeObject<ItemsRootObject>(getStringRequests);
foreach (Item item in responseObject.rows)
{
Items.Add(item);
}
ProcessedTask = true;
string Message = "Processed Task";
return Message;
}
}
public class ItemsRootObject
{
public List<Item> rows { get; set; }
public bool success { get; set; }
public int code { get; set; }
public string msg { get; set; }
}
public class Item
{
public string value { get; set; }
}
I already know the JSON return and objects are there correctly (works in a regular call), but i don't know (understand) how to invoke my task while keeping the phone app going (load MainPage.xaml).
Anyone can tell how i get my task started asynchronously?
You can use async and await where you need to call the task.
private async void yourFunction(object sender, RoutedEventArgs e)
{
string result = await RequestDataTask();
}
http://msdn.microsoft.com/it-it/library/hh191443.aspx
But if you want to call an async method inside a constructor use something like this:
public partial class App
{
private readonly Task _initializingTask;
public App()
{
_initializingTask = Init();
}
private async Task Init()
{
/*
Initialization that you need with await/async stuff allowed
*/
string result = await RequestDataTask();
}
}
Related
I am implementing a scheduler. As part of its core logic it processes a custom object Schedule. Basically it iterates over an array of schedules and try to process it. The problem is who ever creates a Schedule needs to register it with the container using an ISchedule interface. My Scheduler then pulls all the ISchedule references from the container. SO far this is working but it does not have the flexibility of loading the schedules runtime. What design and implementation I can adapt to implement a Scheduler that can load those Schedules run time. I am giving some sample code.
Something that is coming to my mind is having the developers writing a json representation of the Schedules and put that inside a config or implementing an endpoint that returns that config to the Scheduler. But can I avoid this? I want the Scheduler to be completely agonistic of developer code.
You can use the factory to register information about schedule classes. And dynamically change the call interval by finding the schedule by Id.
You register in the container: <IScheduleFactory,ScheduleFactory> and <IScheduleManager,ScheduleManager>
public interface ISchedule
{
public string Id { get; set; }
public TimeSpan Interval { get; set; }
public DateTime? LastExecution { get; set; }
public bool CanStart { get; }
void Start();
void Stop();
}
public sealed class Schedule : ISchedule
{
public string Id { get; set; }
public TimeSpan Interval { get; set; }
public DateTime? LastExecution { get; set; }
public bool CanStart {
get
{
lock (_sync)
{
return !LastExecution.HasValue || LastExecution.Value.Add(Interval) >= DateTime.UtcNow;
}
}
}
private readonly object _sync = new object();
public void Start()
{
lock (_sync)
{
if (!LastExecution.HasValue || LastExecution.Value.Add(Interval) >= DateTime.UtcNow)
{
// DO WORK
LastExecution = DateTime.UtcNow;
}
}
}
public void Stop()
{
throw new NotImplementedException();
}
}
public interface IScheduleFactory
{
ISchedule Create();
}
public sealed class ScheduleFactory: IScheduleFactory
{
private readonly IScheduleManager _manager;
public ScheduleFactory(IScheduleManager manager)
{
_manager = manager;
}
public ISchedule Create()
{
var schedule = new Schedule();
_manager.Register(schedule);
return schedule;
}
}
public interface IScheduleManager
{
void Register(ISchedule schedule);
ISchedule Get(string id);
void Start();
void Stop();
}
public sealed class ScheduleManager : IScheduleManager
{
private readonly Dictionary<string,ISchedule> _items = new Dictionary<string, ISchedule>();
private readonly object _sync = new object();
public void Register(ISchedule schedule)
{
lock (_sync)
{
if (_items.ContainsKey(schedule.Id))
_items.Add(schedule.Id, schedule);
}
}
public ISchedule Get(string id)
{
lock (_sync)
{
if (_items.ContainsKey(id))
return _items[id];
}
return null;
}
private bool _isStart;
public void Start()
{
_isStart = true;
while (_isStart)
{
ISchedule[] array = null;
lock (_sync)
{
array = _items.Values.ToArray();
}
foreach (var schedule in array)
{
if (schedule.CanStart)
Task.Factory.StartNew(()=>schedule.Start());
}
}
}
public void Stop()
{
_isStart = false;
}
}
I am creating a simple logging system by using Nest and C#. I have a log producer for collecting logs inside of blockingcollection. Also I have a consumer. But I stumpled upon with an issue. How can I use my comsumer in startup or is there any way to create background servise which was listening queue of blockingcollection? What is best practice of it? I am confusing how to call AsyncConsumer or consumer when application startup.
public class SimpleLog
{
public string Header { get; set; }
public string LogDate { get; set; }
public string Sessionid { get; set; }
public string Userid { get; set; }
public string Correlationid { get; set; }
public int Status { get; set; }
public string UrlQueryString { get; set; }
public string UrlPath { get; set; }
public string UrlMethod { get; set; }
public string Environment { get; set; }
public string IndexName { get; set; }
public string IndexType { get; set; }
}
public class QuickLog
{
private static BlockingCollection<SimpleLog> data = new BlockingCollection<SimpleLog>();
public static void Producer(SimpleLog pageviewLog)
{
data.TryAdd(pageviewLog, TimeSpan.FromSeconds(10));
}
public static void Consumer()
{
var _client = ElasticConfig.GetClient();
var logs = new List<SimpleLog>();
foreach (var item in data.GetConsumingEnumerable())
{
logs.Add(item);
}
if (logs == null && logs.Count <= 0)
return;
var log = logs.FirstOrDefault();
var response = _client.IndexMany(logs, log.IndexName, log.IndexType);
if (!response.IsValid)
throw response.OriginalException;
}
public async Task AsyncConsumer()
{
var _client = ElasticConfig.GetClient();
var logs = new List<SimpleLog>();
foreach (var item in data.GetConsumingEnumerable())
{
logs.Add(item);
}
if (logs == null && logs.Count <= 0)
return;
var log = logs.FirstOrDefault();
var response = await _client.IndexManyAsync(logs, log.IndexName, log.IndexType).ConfigureAwait(false);
if (!response.IsValid)
throw response.OriginalException;
await Task.Delay(TimeSpan.FromSeconds(10)).ConfigureAwait(false);
}
}
public static class ElasticConfig
{
private static IElasticClient _client;
static ElasticConfig()
{
var esurl = LogSettings.Url;
string[] urls = esurl.Split(',');
var nodes = new Uri[2];
for (int i = 0; i < urls.Length; i++)
{
nodes.SetValue(new Uri(urls[i]), i);
}
var connectionPool = new SniffingConnectionPool(nodes);
var connectionSettings = new ConnectionSettings(connectionPool).RequestTimeout(
TimeSpan.FromSeconds(60))
.PingTimeout(TimeSpan.FromSeconds(60))
.MaxRetryTimeout(TimeSpan.FromSeconds(60))
.MaxDeadTimeout(TimeSpan.FromSeconds(60))
.DeadTimeout(TimeSpan.FromSeconds(60)).DisablePing()
.SniffOnConnectionFault(false)
.SniffOnStartup(false)
.SniffLifeSpan(TimeSpan.FromMinutes(1));
_client = new ElasticClient(connectionSettings);
}
public static IElasticClient GetClient()
{
return _client;
}
}
Not sure how many times and which method exactly you want to call. If you want to run some asynchronous background jobs you can use IHostedService. You will need to install Microsoft.Extensions.Hosting NuGet package or Microsoft.AspNetCore.App metapackage.
Usage:
Add this line to your Startup.cs
services.AddHostedService<LogBackgroundService>(); //service is instance of IServiceCollection
And this is the implementation of your background service:
public class LogBackgroundService : IHostedService
{
public async Task StartAsync(CancellationToken cancellationToken)
{
await QuickLog.AsyncConsumer(); // or whatever you want to call
}
public Task StopAsync(CancellationToken cancellationToken)
{
return Task.CompletedTask;
}
}
Behavior:
It will run once(but IHostedService still will be running. If you want to reduce resource consumption just call StopAsync() when it's done). If you want to run something in a loop, you can implement this:
while (!cancellationToken.IsCancellationRequested)
{
await QuickLog.AsyncConsumer();
await Task.Delay(250, cancellationToken); // you can add this if you want to throttle
}
PS. If you need to run multiple IHostedServices in your application without blocking each other you will need to wrap your methods into Tasks:
public Task StartAsync(CancellationToken cancellationToken)
{
Task.Run(() => QuickLog.AsyncConsumer(), cancellationToken);
}
IHostedService is solution for you.
You can crate new class and inherit from this class https://gist.github.com/davidfowl/a7dd5064d9dcf35b6eae1a7953d615e3
Then your new class will be something like this
public class LogService : HostedService
{
private readonly IServiceScopeFactory _scopeFactory;
public LogBackgroundService (IServiceScopeFactory scopeFactory)
{
_scopeFactory = scopeFactory;
}
protected override async Task ExecuteAsync(CancellationToken cancellationToken)
{
while (!cancellationToken.IsCancellationRequested)
{
await new QuickLog().AsyncConsumer(cancellationToken);
await Task.Delay(TimeSpan.FromSeconds(10), cancellationToken);
}
}
}
Finally update your Startup.cs:
services.AddSingleton<IHostedService, LogService>();
In my program I have a set of Links, let's say a million. The requirements are:
1) Process the links in parallel
2) Limit how many of these links will run in parallel.
In the example, we don't care about the execution order of the links but to ensure the process is finished with errors or without.
How can I properly ensure the above conditions?
class Program
{
private static void Main()
{
start_url_list();
}
static void start_url_list()
{
HashSet<string> urls = get_url_list();
Parallel.ForEach(urls, new ParallelOptions { MaxDegreeOfParallelism = 10 },
url =>
{
Console.WriteLine("Trying..:{0}", url);
var my_url = new Url_Class(url);
my_url.Start();
Console.WriteLine("Processing url is over");
//Do stuff with my_url properties
if (my_url.isError)
{
Console.WriteLine("ERROR:{0} msg:{1}",url,my_url.errorMsg);
}
else
{
Console.WriteLine("OK:{0} Size{1}", url, my_url.size);
}
});
}
}
public class Url_Class
{
public string url { get; private set; }
public string errorMsg { get; internal set; }
public bool isError { get; private set; }
public int size { get; private set; }
public Host(string url_str)
{
url = url_str;
isError = false;
}
public void Start()
{
//Long Process
}
}
I try to execute a method in an external library:
This is the interface of the external library:
public interface IExternal
{
string Name { get; }
Task DoAsync();
}
This is the class of the external library:
public class ExternalClass : IExternal
{
public string Name
{
get
{
return "Test external";
}
}
public async Task DoAsync()
{
Console.WriteLine("Do async");
await Task.Delay(3000);
Console.WriteLine("Done async");
}
}
This is the local class my ViewModel uses:
public class External
{
public string Name { get; set; }
public string Description { get; set; }
public RelayCommand Run { get; set; }
}
In my application i load the external library as an reference.
An try to bind DoAsync as RelayCommand:
foreach (var item in externals)
{
var t = new External();
t.Name = item.Name;
t.Description = "test";
t.Run = new RelayCommand(async () => await item.DoAsync());
}
The XAML binding of the button is as follows: Command="{Binding Run}"
Nothing happens, no command is executed.
I have a background process that i want to regularly maintain the state of gps location. I am not clear on how to invoke a delegate on the main thread in the ui layer when the threaded method is in another class. Here is sample code. My form launches the thread on load:
public partial class MainScreen : Form
{
.
. // form stuff
.
private void MainScreen_Load(object sender, EventArgs e)
{
var gpsStatusManager = new GpsStatusManager();
Thread t = new Thread(gpsStatusManager.UpdateLocation);
t.IsBackground = true;
t.Start();
}
delegate void GpsDataParameterDelegate(GpsStatus value);
public void UpdateGpsStatus(GpsStatus value)
{
if (InvokeRequired)
{
// We're not in the UI thread, so we need to call BeginInvoke
BeginInvoke(new GpsDataParameterDelegate(UpdateGpsStatus), new object[] { value });
return;
}
// Must be on the UI thread if we've got this far
gpsStatus.SetGpsStatus(value);
}
}
I have a domain object class for the gps information:
public class GpsStatus
{
public void SetGpsStatus(GpsStatus gpsStatus)
{
Latitude = gpsStatus.Latitude;
Longitude = gpsStatus.Longitude;
CurrentDateTime = gpsStatus.CurrentDateTime;
NumberOfSatellites = gpsStatus.NumberOfSatellites;
TotalNumberSatellites = gpsStatus.TotalNumberSatellites;
}
public float Latitude { get; private set; }
public float Longitude { get; private set; }
public DateTime CurrentDateTime { get; private set; }
public int NumberOfSatellites { get; private set; }
public int TotalNumberSatellites { get; private set; }
}
Then, my manager class where i update status in the secondary thread:
public class GpsStatusManager
{
private GpsStatus _gpsStatus;
public void UpdateLocationx()
{
while (UpdateGpsData())
{
Thread.Sleep(2000);
}
}
private bool UpdateGpsData()
{
SError error;
SGpsPosition gpsPosition;
try
{
if (CApplicationAPI.GetActualGpsPosition(out error, out gpsPosition, true, 0) != 1)
return false;
}
catch (Exception)
{
return false;
}
var numberOfSatellites = gpsPosition.Satellites;
var totalSatellites = gpsPosition.satellitesInfo;
var datetime = gpsPosition.Time;
var lat = gpsPosition.Latitude;
var lon = gpsPosition.Longitude;
_gpsStatus.SetGpsStatus(lat, lon, datetime, numberOfSatellites, totalSatellites);
//How do I invoke the delegate to send the _gpsStatus data to my main thread?
return true;
}
}
Thanks for any assistance.
Here's one way to do it, just off the top of my head:
public class GpsStatusEventArgs : EventArgs
{
public GpsStatus Status { get; private set; }
public GpsStatusEventArgs(GpsStatus status)
{
Status = status;
}
}
public class GpsStatusManager
{
...
public event EventHandler<GpsStatusEventArgs> GpsStatusUpdated;
private void OnGpsStatusUpdated(GpsStatus gpsStatus)
{
EventHandler<GpsStatusEventArgs> temp = GpsStatusUpdated;
if (temp != null)
temp.Invoke(this, new GpsStatusEventArgs(gpsStatus));
}
}
public partial class MainScreen : Form
{
...
private void MainScreen_Load(object sender, EventArgs e)
{
var gpsStatusManager = new GpsStatusManager();
gpsStatusManager.GpsStatusUpdated += new EventHandler<GpsStatusEventArgs>(GpsStatusManager_GpsStatusUpdated);
...
}
private void GpsStatusManager_GpsStatusUpdated(object sender, GpsStatusEventArgs e)
{
UpdateGpsStatus(e.Status);
}
...
}
Then add this to the bottom of UpdateGpsData:
OnGpsStatusUpdated(_gpsStatus);
You should use the SynchronizationContext class.
In the UI thread (in any class), set a field (perhaps static) to SynchronizationContext.Current.
You can then call Send or Post on the saved instance to execute code on the UI thread.
Here is another approach using the ISynchronizeInvoke interface. This is the same pattern the System.Timers.Timer class uses to raise the Elapsed event.
public class GpsStatusManager
{
public ISynchronizeInvoke SynchronizingObject { get; set; }
public event EventHandler Update;
public void UpdateGpsData()
{
// Code omitted for brevity.
OnUpdate(_gpsStatus);
return true;
}
private OnUpdate(GpsStatus status)
{
if (SynchronizingObject != null && SynchronizingObject.IsInvokeRequired)
{
ThreadStart ts = () => { OnUpdate(status); };
SynchronizingObject.Invoke(ts, null);
}
else
{
if (Update != null)
{
Update(this, status);
}
}
}
public class UpdateEventArgs : EventArgs
{
public GpsStatus Status { get; set; }
}
}