I have a requirement to connect to the server and collect data for processing. Below is my core class, which is responsible for looping through all the servers and try connecting them for processing.
public class CoreDataProcessingEngine : ICoreDataProcessingEngine
{
private readonly COMLib.ServerGateway _aServerGw;
private COMLib.ServerErrorInfo _aServerErrorInfo;
Public CoreDataProcessingEngine()
{
_aServerGw = new COMLib.ServerGateway();
_aServerErrorInfo = new COMLib.ServerErrorInfo();
}
//When service starts, I am collecting all the server details from config and trying to connect ONE BY ONE.
public async Task Start()
{
List<Server> servers = ConfigurationManager.GetSection("servers") as List<Server>;
foreach (var serverdetails in servers)
{
var data = Task.Run(() => ConnectToServer(serverdetails ));
}
}
}
Here is my ConnectToServer method
private async void ConnectToGateway(ServerDetails serverdetails )
{
await _aServerGw.connectToServerByName(serverdetails.serveraddress);
}
I have extended the connectToServerByName method as follow , which is in separate static class.
public static class ComLibraryExtensions
{
public static Task connectToServerByName(this ProxyGW #this, string serveraddress)
{
var tcs = new TaskCompletionSource<object>();
Action onSuccess = null;
Action<int> onFailed = null;
onSuccess = () =>
{
#this.onConnectSucceeded -= HandleManager_OnConnectSucceeded;
#this.onConnectFailed -= HandleManager_OnConnectFailed;
tcs.TrySetResult(null);
};
onFailed = hr =>
{
#this.onConnectSucceeded -= HandleManager_OnConnectSucceeded;
#this.onConnectFailed -= HandleManager_OnConnectFailed;
tcs.TrySetException(Marshal.GetExceptionForHR(hr));
};
#this.onConnectSucceeded += HandleManager_OnConnectSucceeded;
#this.onConnectFailed += HandleManager_OnConnectFailed;
#this.connectToGatewayByNameEx(serveraddress);
return tcs.Task;
}
private static void HandleManager_OnConnectFailed(int hr)
{
//How do I get access to dependent objects here?
//Like ILogger ??
_logger.Information(hr);
}
private static void HandleManager_OnConnectSucceeded()
{
//How do I get access #this??
#this.enableNotifications(true);//fails , it says #this does not exists
}
}
Question is:
How do I get access to _aServerGw in HandleManager_OnConnectSucceeded event, because I want to set some property based on the success event.
How do I get access to dependent objects here in extension classes like ILogger?
Related
I have an bunch of 'workers' which at some point need an unique key from my HTTP server to continue execution. This keys can sometimes arrive before the workers need them, in which case I just want to supply the worker with the key so it can continue execution. However if there are no keys left, I want the code execution in the task to be halted until a key is supplied. I also want the keys which where supplied first to be used first.
I have tried to implement a Singleton where the keys can be supplied to from the HTTP server and retrieved from by the workers, but execution seems to be deadlocked right now.
My code:
Keyhandler.cs
public sealed class KeyHandler
{
private static readonly KeyHandler instance = new KeyHandler();
private static Dictionary<Website, Queue<string>> retrievedKeys = new Dictionary<Website, Queue<string>>();
private static Dictionary<Website, Queue<TaskCompletionSource<string>>> waitingWorkers = new Dictionary<Website, Queue<TaskCompletionSource<string>>>();
// Explicit static constructor to tell C# compiler
// not to mark type as beforefieldinit
static KeyHandler()
{
}
private KeyHandler()
{
foreach (Website website in Enum.GetValues(typeof(Website)))
{
retrievedKeys.Add(website, new Queue<string>());
waitingWorkers.Add(website, new Queue<TaskCompletionSource<string>>());
}
}
public static KeyHandler Instance
{
get
{
return instance;
}
}
public static void AddKey(Website website, string response)
{
if (waitingWorkers[website].Count > 0)
{
waitingWorkers[website].Dequeue().SetResult(response);
}
else
{
retrievedKeys[website].Enqueue(response);
}
}
public static string RetrieveKey(Website website)
{
if (retrievedKeys[website].Count > 0)
{
return retrievedKeys[website].Dequeue();
}
else
{
TaskCompletionSource<string> tcs = new TaskCompletionSource<string>();
Task<string> t = tcs.Task;
waitingWorkers[website].Enqueue(tcs);
return t.Result;
}
}
}
Worker.cs
public async Task Run()
{
...
string key = KeyHandler.RetrieveKey(Website.MyWebsite);
// do something with key here
...
}
HTTPServer.cs
private void Process(HttpListenerContext context)
{
...
KeyHandler.AddKey(Website.MyWebsite, key);
...
}
I working on real-time search. At this moment on property setter which is bounded to edit text, I call a method which calls API and then fills the list with the result it looks like this:
private string searchPhrase;
public string SearchPhrase
{
get => searchPhrase;
set
{
SetProperty(ref searchPhrase, value);
RunOnMainThread(SearchResult.Clear);
isAllFriends = false;
currentPage = 0;
RunInAsync(LoadData);
}
}
private async Task LoadData()
{
var response = await connectionRepository.GetConnections(currentPage,
pageSize, searchPhrase);
foreach (UserConnection uc in response)
{
if (uc.Type != UserConnection.TypeEnum.Awaiting)
{
RunOnMainThread(() =>
SearchResult.Add(new ConnectionUser(uc)));
}
}
}
But this way is totally useless because of it totally mashup list of a result if a text is entering quickly. So to prevent this I want to run this method async in a property but if a property is changed again I want to kill the previous Task and star it again. How can I achieve this?
Some informations from this thread:
create a CancellationTokenSource
var ctc = new CancellationTokenSource();
create a method doing the async work
private static Task ExecuteLongCancellableMethod(CancellationToken token)
{
return Task.Run(() =>
{
token.ThrowIfCancellationRequested();
// more code here
// check again if this task is canceled
token.ThrowIfCancellationRequested();
// more code
}
}
It is important to have this checks for cancel in the code.
Execute the function:
var cancellable = ExecuteLongCancellableMethod(ctc.Token);
To stop the long running execution use
ctc.Cancel();
For further details please consult the linked thread.
This question can be answered in many different ways. However IMO I would look at creating a class that
Delays itself automatically for X (ms) before performing the seach
Has the ability to be cancelled at any time as the search request changes.
Realistically this will change your code design, and should encapsulate the logic for both 1 & 2 in a separate class.
My initial thoughts are (and none of this is tested and mostly pseudo code).
class ConnectionSearch
{
public ConnectionSearch(string phrase, Action<object> addAction)
{
_searchPhrase = phrase;
_addAction = addAction;
_cancelSource = new CancellationTokenSource();
}
readonly string _searchPhrase = null;
readonly Action<object> _addAction;
readonly CancellationTokenSource _cancelSource;
public void Cancel()
{
_cancelSource?.Cancel();
}
public async void PerformSearch()
{
await Task.Delay(300); //await 300ms between keystrokes
if (_cancelSource.IsCancellationRequested)
return;
//continue your code keep checking for
//loop your dataset
//call _addAction?.Invoke(uc);
}
}
This is basic, really just encapsulates the logic for both points 1 & 2, you will need to adapt the code to do the search.
Next you could change your property to cancel a previous running instance, and then start another instance immediatly after something like below.
ConnectionSearch connectionSearch;
string searchPhrase;
public string SearchPhrase
{
get => searchPhrase;
set
{
//do your setter work
if(connectionSearch != null)
{
connectionSearch.Cancel();
}
connectionSearch = new ConnectionSearch(value, addConnectionUser);
connectionSearch.PerformSearch();
}
}
void addConnectionUser(object uc)
{
//pperform your add logic..
}
The code is pretty straight forward, however you will see in the setter is simply cancelling an existing request and then creating a new request. You could put some disposal cleanup logic in place but this should get you started.
You can implement some sort of debouncer which will encapsulate the logics of task result debouncing, i.e. it will assure if you run many tasks, then only the latest task result will be used:
public class TaskDebouncer<TResult>
{
public delegate void TaskDebouncerHandler(TResult result, object sender);
public event TaskDebouncerHandler OnCompleted;
public event TaskDebouncerHandler OnDebounced;
private Task _lastTask;
private object _lock = new object();
public void Run(Task<TResult> task)
{
lock (_lock)
{
_lastTask = task;
}
task.ContinueWith(t =>
{
if (t.IsFaulted)
throw t.Exception;
lock (_lock)
{
if (_lastTask == task)
{
OnCompleted?.Invoke(t.Result, this);
}
else
{
OnDebounced?.Invoke(t.Result, this);
}
}
});
}
public async Task WaitLast()
{
await _lastTask;
}
}
Then, you can just do:
private readonly TaskDebouncer<Connections[]> _connectionsDebouncer = new TaskDebouncer<Connections[]>();
public ClassName()
{
_connectionsDebouncer.OnCompleted += OnConnectionUpdate;
}
public void OnConnectionUpdate(Connections[] connections, object sender)
{
RunOnMainThread(SearchResult.Clear);
isAllFriends = false;
currentPage = 0;
foreach (var conn in connections)
RunOnMainThread(() => SearchResult.Add(new ConnectionUser(conn)));
}
private string searchPhrase;
public string SearchPhrase
{
get => searchPhrase;
set
{
SetProperty(ref searchPhrase, value);
_connectionsDebouncer.Add(RunInAsync(LoadData));
}
}
private async Task<Connection[]> LoadData()
{
return await connectionRepository
.GetConnections(currentPage, pageSize, searchPhrase)
.Where(conn => conn.Type != UserConnection.TypeEnum.Awaiting)
.ToArray();
}
It is not pretty clear what RunInAsync and RunOnMainThread methods are.
I guess, you don't actually need them.
I'll try to create custom config provider that will take keys from database. As written in manuals, I created this provider and it's work fine. All keys are loaded on start and all works fine.
But now I'm trying to use IOptionsSnapshot and reload keys from db as they change. But nothing happens.
Can anyone tell me what's going wrong? Here is my code:
public class EFConfigProvider : ConfigurationProvider
{
private DateTime lastLoaded;
public EFConfigProvider(Action<DbContextOptionsBuilder> optionsAction)
{
OptionsAction = optionsAction;
lastLoaded = DateTime.Now;
ChangeToken.OnChange(
() => Watch(),
() => {
Thread.Sleep(250);
this.Load();
});
}
public new IChangeToken GetReloadToken()
{
return Watch();
}
Action<DbContextOptionsBuilder> OptionsAction { get; }
// Load config data from EF DB.
public override void Load()
{
this.Data.Clear();
var builder = new DbContextOptionsBuilder<ConfigContext>();
OptionsAction(builder);
using (var dbContext = new ConfigContext(builder.Options))
{
// Save Load Fact
dbContext.SaveLoadFact();
// Load Partners Settings
GetPartners(dbContext);
}
}
private IChangeToken Watch()
{
return new DatabaseChangeToken();
}
}
public class DatabaseChangeToken : IChangeToken
{
public bool HasChanged
{
get
{
return true;
}
}
public bool ActiveChangeCallbacks => false;
public IDisposable RegisterChangeCallback(Action<object> callback, object state) => EmptyDisposable.Instance;
internal class EmptyDisposable : IDisposable
{
public static EmptyDisposable Instance { get; } = new EmptyDisposable();
private EmptyDisposable() { }
public void Dispose() { }
}
}
What I did to start to worked it:
I add in class EFConfigProvider variable
private ConfigurationReloadToken _reloadToken = new ConfigurationReloadToken();
I add in constructor
// Start Periodic task to refresh the DB
PeriodicTask.Run(() =>
{
//Refresh();
OnReload();
}, TimeSpan.FromSeconds(reload));
I Add class for periodTask
public class PeriodicTask
{
public static async Task Run(Action action, TimeSpan period, CancellationToken cancellationToken)
{
while (!cancellationToken.IsCancellationRequested)
{
await Task.Delay(period, cancellationToken);
if (!cancellationToken.IsCancellationRequested)
action();
}
}
public static Task Run(Action action, TimeSpan period)
{
return Run(action, period, CancellationToken.None);
}
}
Add method on Reload
protected new void OnReload()
{
var previousToken = Interlocked.Exchange(ref _reloadToken, new ConfigurationReloadToken());
previousToken.OnReload();
}
Add change of using change token
ChangeToken.OnChange(
() => { return this._reloadToken; },
() => {
Thread.Sleep(250);
this.Load();
});
Updated: I found that overriding OnReload() broke the notification of changes (I was using OptionsMonitor, which didn't see the changes). So I changed my implementation to not do that. It's calling the default OnReload() which handles the notification of changes to OptionsMonitor.
Your updated implementation really helped me out, so thank you!
But rather than using your custom PeriodicTask, you could just use a Timer. The effect is just the same though.
Here is my implementation, which reloads the data every 5 minutes:
using System;
using System.Linq;
using System.Threading;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Primitives;
using Timer = System.Timers.Timer;
namespace MyProject.Classes.Configuration {
public class MyConfigProvider : ConfigurationProvider {
private readonly DbContextOptions<MyDbContext> _dbOptions;
private readonly Timer _reloadTimer = new Timer();
private ConfigurationReloadToken _reloadToken = new ConfigurationReloadToken();
public MyConfigProvider(Action<DbContextOptionsBuilder> dbOptionsAction) {
var builder = new DbContextOptionsBuilder<MyDbContext>();
dbOptionsAction(builder);
_dbOptions = builder.Options;
_reloadTimer.AutoReset = false;
_reloadTimer.Interval = TimeSpan.FromMinutes(5).TotalMilliseconds;
_reloadTimer.Elapsed += (s, e) => { Load(); };
}
public override void Load() {
try {
using (var db = new MyDbContext(_dbOptions)) {
var settings = db.Settings.AsNoTracking().ToList();
Data.Clear();
foreach (var s in settings) {
Data.Add(s.Name, s.Value);
}
}
OnReload();
} finally {
_reloadTimer.Start();
}
}
}
}
I am having same requirement and I reached to you code and I tried the same thing and not working.
But your code and some other investigation lead me to the hint.
public class CustomConfigurationProvider : ConfigurationProvider
{
private readonly string applicationName;
private readonly bool reloadOnChange;
private readonly IConfiguration configuration;
public CustomConfigurationProvider(string applicationName, bool reloadOnChange)
{
this.applicationName = applicationName;
this.reloadOnChange = reloadOnChange;
if(reloadOnChange)
{
ChangeToken.OnChange(
() => GetReloadToken(), // listener to token change
() =>
{
Thread.Sleep(250);
this.Load();
});
}
}
public override async void Load()
{
Data.Clear();
Data = read data from database;
if (Condition to check if data in database changed)
{
OnReload(); // This will create new token and trigger change so what is register in OnChange above will be called again which is this.Load()
}
}
}
I also refereed https://www.mikesdotnetting.com/article/301/loading-asp-net-core-mvc-views-from-a-database-or-other-location
and
https://learn.microsoft.com/en-us/aspnet/core/fundamentals/primitives/change-tokens
Hope this helps.
I am building api for one application. what i want is when I get a new order, i have to insert it in DB and I want to rise an event (without blocking my api call may be async). so that latter in other class i can handle that event and do the other work like send the new order notification to account and send order update to user using pubnub.
public class OrderService : IOrderService
{
public delegate void OrderEventHandler(Order order,bool isNewOrder);
public static event OrderEventHandler OrderEvents = delegate { };
public OrderService()
{
OrderEventListener listener = new OrderEventListener();
OrderEvents += new OrderEventHandler(listener.HandleOrderEvents);
}
#region Methods
public void Test()
{
INPRODataFacade facade = new NPRODataFacade();
var unitOfWork = facade.GetUnitOfWork();
var order = unitOfWork.OrderRepository.Find(o => o.OrderID == 1).FirstOrDefault();
unitOfWork.Commit();
facade.ReturnUnitOfWork();
RiseEvent(order,true);
}
private async System.Threading.Tasks.Task RiseEvent(Order order,bool isNewOrder)
{
if (order == null)
{
return;
}
OrderEvents(order,isNewOrder);
}
}
public class OrderEventListener
{
PubNub.PubNub pubNub = null;
const string ChanelPrifix = "fasttract";
public OrderEventListener()
{
pubNub = new PubNub.PubNub(ConfigurationSettings.PubNub_SubscribeKey, ConfigurationSettings.PubNub_PublishKey, ConfigurationSettings.PubNub_SecretKey, ConfigurationSettings.PubNub_SSlOn);
}
public void HandleOrderEvents(Order order, bool isNewOrder)
{
string chanelName = isNewOrder ? string.Format("{0}_{1}", ChanelPrifix, order.AccountID)
: string.Format("{0}_{1}_{2}", ChanelPrifix, order.AccountID, order.OrderID);
var channel = pubNub.Channel(chanelName);
channel.Publish(new PubNubMessageModel { Message = GetMessage( order, isNewOrder) });
}
public string GetMessage(Order order,bool isNewOrder)
{
string message = string.Empty;
if(isNewOrder)
{
message = "You have recieved a new order #" + order.OrderID;
return message;
}
message = order.Status.ToString();
return message;
}
}
Here have noticed that it is blocking my api call. means I am not getting api response back until event handler finished its execution. is this the right way to handle events ? I think there should be a way that i just add events while getting new order and then in listener i process that events anytime without blocking api call.
So I have a server and I'm making calls to it through a wrapped up WebSocket (WebSocket4Net) and one of the requirements of the library I'm building is the ability to await on the return of the request. So I have a class MessageEventHandler that contains events that are triggered by the class MessageHandler as messages come in.
MessageEventHandler ex.
public class MessageEventHandler : IMessageEventHandler
{
public delegate void NodeNameReceived(string name);
public event Interfaces.NodeNameReceived OnNodeNameReceived;
public void NodeNameReceive(string name)
{
if (this.OnNodeNameReceived != null)
{
this.OnNodeNameReceived(name);
}
}
}
MessageHandler ex.
public class MessageHandler : IMessageHandler
{
private IMessageEventHandler eventHandler;
public MessageHandler(IMessageEventHandler eventHandler)
{
this.eventHandler = eventHandler;
}
public void ProcessDataCollectorMessage(string message)
{
var serviceMessage = JsonConvert.DeserializeObject<ServiceMessage>(message);
switch (message.MessageType)
{
case MessageType.GetNodeName:
{
var nodeName = serviceMessage.Data as string;
if (nodeName != null)
{
this.eventHandler.NodeNameReceive(nodeName);
}
break;
}
default:
{
throw new NotImplementedException();
}
}
}
Now building upon those classes I have the class containing my asynchronous function that handles the call to get the node name.
public class ClientServiceInterface : IClientServiceInterface
{
public delegate void RequestReady(ServiceMessage serviceMessage);
public event Interfaces.RequestReady OnRequestReady;
public int ResponseTimeout { get; private set; }
private IMessageEventHandler messageEventHandler;
public ClientServiceInterface(IMessageEventHandler messageEventHandler, int responseTimeout = 5000)
{
this.messageEventHandler = messageEventHandler;
this.ResponseTimeout = responseTimeout;
}
public Task<string> GetNodeNameAsync()
{
var taskCompletionSource = new TaskCompletionSource<string>();
var setHandler = default(NodeNameReceived);
setHandler = name =>
{
taskCompletionSource.SetResult(name);
this.messageEventHandler.OnNodeNameReceived -= setHandler;
};
this.messageEventHandler.OnNodeNameReceived += setHandler;
var ct = new CancellationTokenSource(this.ResponseTimeout);
var registration = new CancellationTokenRegistration();
registration = ct.Token.Register(
() =>
{
taskCompletionSource.TrySetCanceled();
this.messageEventHandler.OnNodeNameReceived -= setHandler;
registration.Dispose();
},
false);
var serviceMessage = new ServiceMessage() { Type = MessageType.GetNodeName };
this.ReadyMessage(serviceMessage);
return taskCompletionSource.Task;
}
}
As you can see I wouldn't call it pretty and I apologize if anyone threw up a little reading it. But this is my first attempt at wrapping a Task with Asynchronous Event. So with that on the table I could use some help.
Is there a better way to accomplish what I'm trying to achieve here? Remembering that I want a user of the library to either subscribe to the event and listen for all callbacks OR they can simply await the return depending on
their needs.
var nodeName = await GetNodeNameAsync();
Console.WriteLine(nodeName);
or
messageEventHandler.OnNodeNameReceived += (name) => Console.WriteLine(name);
GetNodeNameAsync();
Alternatively if my approach is actually 'good' can anyone provide any advice as to how I can write a helper function to abstract out setting up each function in this way? Any help would be greatly appreciated.
So I've written a couple classes to solve the problem I was having. The first of which is my CallbackHandle class which contains the task inside the TaskCompletionSource so each time that a request is made in my example a new callback handle is created.
public class CallbackHandle<T>
{
public CallbackHandle(int timeout)
{
this.TaskCompletionSource = new TaskCompletionSource<T>();
var cts = new CancellationTokenSource(timeout);
cts.Token.Register(
() =>
{
if (this.Cancelled != null)
{
this.Cancelled();
}
});
this.CancellationToken = cts;
}
public event Action Cancelled;
public CancellationTokenSource CancellationToken { get; private set; }
public TaskCompletionSource<T> TaskCompletionSource { get; private set; }
}
Then I have a 'handler' that manages the handles and their creation.
public class CallbackHandler<T>
{
private readonly IList<CallbackHandle<T>> callbackHandles;
private readonly object locker = new object();
public CallbackHandler()
{
this.callbackHandles = new List<CallbackHandle<T>>();
}
public CallbackHandle<T> AddCallback(int timeout)
{
var callback = new CallbackHandle<T>(timeout);
callback.Cancelled += () =>
{
this.callbackHandles.Remove(callback);
callback.TaskCompletionSource.TrySetResult("Error");
};
lock (this.locker)
{
this.callbackHandles.Add(callback);
}
return callback;
}
public void EventTriggered(T eventArgs)
{
lock (this.locker)
{
if (this.callbackHandles.Count > 0)
{
CallbackHandle<T> callback =
this.callbackHandles.First();
if (callback != null)
{
this.callbackHandles.Remove(callback);
callback.TaskCompletionSource.SetResult(eventArgs);
}
}
}
}
}
This is a simplified version of my actual implementation but it should get someone started if they need something similar. So to use this on my ClientServiceInterface class in my example I would start by creating a class level handler and using it like this:
public class ClientServiceInterface : IClientServiceInterface
{
private readonly CallbackHandler<string> getNodeNameHandler;
public ClientServiceInterface(IMessageEventHandler messageEventHandler, int responseTimeout = 5000)
{
this.messageEventHandler = messageEventHandler;
this.ResponseTimeout = responseTimeout;
this.getNodeNameHandler = new
CallbackHandler<string>();
this.messageEventHandler.OnNodeNameReceived += args => this.getNodeNameHandler.EventTriggered(args);
}
public Task<string> GetNodeNameAsync()
{
CallbackHandle<string> callbackHandle = this.getNodeNameHandler.AddCallback(this.ResponseTimeout);
var serviceMessage = new ServiceMessage
{
Type = MessageType.GetNodeName.ToString()
};
this.ReadyMessage(serviceMessage);
return callbackHandle.TaskCompletionSource.Task;
}
// Rest of class declaration removed for brevity
}
Which is much better looking than what I had before (at least in my opinion) and it's easy to extend.
For starters follow a thread-safe pattern:
public void NodeNameReceive(string name)
{
var evt = this.OnNodeNameReceived;
if (evt != null)
{
evt (name);
}
}
If you do not take a reference to the event object it can be set to null between the time you check null and call the method.