using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.ServiceModel; using System.Threading; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Threading; using BeWo.Multitenancy; using BeWo.Security; using BS.Shared; using BS.Shared.DataContracts; using BS.Shared.DataContracts.Compact; using DevExpress.CodeParser; using DevExpress.Export.Xl; namespace BeWo.ServiceProxy { // Ich verlass mich jetzt mal hierdrauf: // http://dotnetslackers.com/WSE/re-51155_WCF_Proxy_Performance_vs_WSE_V3.aspx // Man muss also nicht schließen wenn nur eine Instanz vom Proxy gehalten wird // und Services PerCall konfiguriert sind... public static class ServiceFacade { public static object _Lock = string.Empty; private static string _ServerName; private static Dictionary _EndpointAddresses; private static readonly Dictionary _ProxyCache; private static int _RunningAsyncs; // Nur um die EndpointAddresses wegen neuer SiteOfOrigin zu aktualisieren public static Dictionary EndpointAddresses { get { return _EndpointAddresses; } } public static void UpdateServerAddresses(String siteOfOrigin) { _ServerName = siteOfOrigin; var test = _ServerName.Substring(_ServerName.Length - 1, 1); if (_ServerName.Substring(_ServerName.Length - 1, 1) != "/") _ServerName += "/"; _EndpointAddresses = new Dictionary { { typeof(IQueryService), new EndpointAddress(_ServerName + "QueryService.svc") }, { typeof(IStreamingService), new EndpointAddress(_ServerName + "StreamingService.svc") }, { typeof(IEmployeeService), new EndpointAddress(_ServerName + "EmployeeService.svc") }, { typeof(ICustomerService), new EndpointAddress(_ServerName + "CustomerService.svc") }, { typeof(IUserService), new EndpointAddress(_ServerName + "UserService.svc") }, { typeof(IValueListService), new EndpointAddress(_ServerName + "ValueListService.svc") }, { typeof(IDownloadService), new EndpointAddress(_ServerName + "DownloadService.svc") }, { typeof(IResourceService), new EndpointAddress(_ServerName + "ResourceService.svc") }, { typeof(IOperationsService), new EndpointAddress(_ServerName + "OperationsService.svc") }, { typeof(IOperationsEnhancedService), new EndpointAddress(_ServerName + "OperationsEnhancedService.svc") }, { typeof(IAnalysisService), new EndpointAddress(_ServerName + "AnalysisService.svc") }, { typeof(IMailService), new EndpointAddress(_ServerName + "MailService.svc") }, { typeof(IAccountingService), new EndpointAddress(_ServerName + "AccountingService.svc") }, { typeof(IGkvAccountingService), new EndpointAddress(_ServerName + "GkvAccountingService.svc") }, { typeof(IReportService), new EndpointAddress(_ServerName + "ReportService.svc") }, { typeof(IAiEnhancedService), new EndpointAddress(_ServerName + "AiEnhancedService.svc") } }; } static ServiceFacade() { _ProxyCache = new Dictionary(); } #region AiEnhancedService public static void DoAiEnhancedServiceSnyc(Action pAction) => DoSync(pAction); public static T DoAiEnhancedServiceSnyc(Func pFunc) => DoSync(pFunc); public static void DoAiEnhancedServiceAsnyc(Action pAction, Action pCallback = null, bool showWaitDialog = true, UserControl caller = null) => DoAsync(pAction, pCallback, showWaitDialog, caller?.Dispatcher); public static void DoAiEnhancedServiceAsnyc(Func pFunc, Action pCallback = null, bool showWaitDialog = true, UserControl caller = null) => DoAsync(pFunc, pCallback, showWaitDialog, caller?.Dispatcher); #endregion #region OperationsEnhancedService public static void DoOperationsEnhancedServiceSnyc(Action pAction) => DoSync(pAction); public static T DoOperationsEnhancedServiceSnyc(Func pFunc) => DoSync(pFunc); public static void DoOperationsEnhancedServiceAsnyc(Action pAction, Action pCallback = null, bool showWaitDialog = true, UserControl caller = null) => DoAsync(pAction, pCallback, showWaitDialog, caller?.Dispatcher); public static void DoOperationsEnhancedServiceAsnyc(Func pFunc, Action pCallback = null, bool showWaitDialog = true, UserControl caller = null) => DoAsync(pFunc, pCallback, showWaitDialog, caller?.Dispatcher); #endregion public static void DoAccountingServiceAsync(Func pFunc, Action pCallBack, bool showWaitDialog) { DoAsync(pFunc, pCallBack, showWaitDialog); } public static void DoAccountingServiceAsync(Func pFunc, Action pCallBack) { DoAsync(pFunc, pCallBack, true); } public static void DoAccountingServiceAsync(Action pAction) { DoAsync(pAction, true); } public static void DoAccountingServiceAsync(Action pAction, Action callback) { DoAsync(pAction, callback, true); } public static T DoAccountingServiceSync(Func pFunc) { try { return pFunc.Invoke(GetInstance()); } catch (Exception e) { ShowErrorMessage(e); } return default(T); } public static void DoAnalysisServiceAsync(Func pFunc, Action pCallBack) { DoAsync(pFunc, pCallBack, true); } public static void DoAnalysisServiceAsync(Func pFunc, Action pCallBack, bool showWaitDialog) { DoAsync(pFunc, pCallBack, showWaitDialog); } public static void DoAccountingServiceSync(Action pAction) { pAction.Invoke(GetInstance()); } // public static void DoAnalysisServiceAsync(Action pAction) { DoAsync(pAction, true); } public static void DoCustomerServiceAsync(Func pFunc, Action pCallBack, bool showWaitDialog) { DoAsync(pFunc, pCallBack, showWaitDialog); } public static void DoCustomerServiceAsync(Func pFunc, Action pCallBack) { DoAsync(pFunc, pCallBack, true); } public static void DoCustomerServiceAsync(Action pAction) { DoAsync(pAction, true); } public static void DoCustomerServiceSync(Action pAction) { pAction.Invoke(GetInstance()); } public static T DoCustomerServiceSync(Func pFunc) { try { return pFunc.Invoke(GetInstance()); } catch (Exception e) { ShowErrorMessage(e); } return default(T); } public static void DoDownloadServiceAsync(Func pFunc, Action pCallBack) { DoAsync(pFunc, pCallBack, true); } public static void DoDownloadServiceAsync(Action pAction) { DoAsync(pAction, true); } public static void DoDownloadServiceSync(Action pAction) { try { pAction.Invoke(GetInstance()); } catch (Exception e) { // if (HandleServiceProxyException(e)) // throw e; ShowErrorMessage(e); } } public static T DoDownloadServiceSync(Func pFunc) { try { return pFunc.Invoke(GetInstance()); } catch (Exception e) { ShowErrorMessage(e); } return default(T); } public static void DoEmployeeServiceAsync(Func pFunc, Action pCallBack, bool showWaitDialog) { DoAsync(pFunc, pCallBack, showWaitDialog); } public static void DoEmployeeServiceAsync(Func pFunc, Action pCallBack) { DoAsync(pFunc, pCallBack, true); } public static void DoEmployeeServiceAsync(Action pAction) { DoAsync(pAction, true); } public static void DoEmployeeServiceSync(Action pAction) { try { pAction.Invoke(GetInstance()); } catch (Exception e) { ShowErrorMessage(e); } } public static T DoEmployeeServiceSync(Func pFunc) { try { return pFunc.Invoke(GetInstance()); } catch (Exception e) { ShowErrorMessage(e); } return default(T); } #region Wohnheim public static void DoWohnheimServiceAsync(Func pFunc, Action pCallBack, bool showWaitDialog) { DoAsync(pFunc, pCallBack, showWaitDialog); } public static void DoWohnheimServiceAsync(Func pFunc, Action pCallBack) { DoAsync(pFunc, pCallBack, true); } public static void DoWohnheimServiceAsync(Action pAction) { DoAsync(pAction, true); } public static void DoWohnheimServiceSync(Action pAction) { try { pAction.Invoke(GetInstance()); } catch (Exception e) { ShowErrorMessage(e); } } public static T DoWohnheimServiceSync(Func pFunc) { try { return pFunc.Invoke(GetInstance()); } catch (Exception e) { ShowErrorMessage(e); } return default(T); } #endregion #region GkvAccounting public static void DoGkvAccountingServiceAsync(Func pFunc, Action pCallBack, bool showWaitDialog) { DoAsync(pFunc, pCallBack, showWaitDialog); } public static void DoGkvAccountingServiceAsync(Func pFunc, Action pCallBack) { DoAsync(pFunc, pCallBack, true); } public static void DoGkvAccountingServiceAsyncGUI(Func pFunc, Action pCallBack, Dispatcher dispatcher) { DoAsync(pFunc, pCallBack, true, dispatcher); } public static void DoGkvAccountingServiceAsync(Action pAction) { DoAsync(pAction, true); } public static void DoGkvAccountingServiceSync(Action pAction) { try { pAction.Invoke(GetInstance()); } catch (Exception e) { ShowErrorMessage(e); } } public static T DoGkvAccountingServiceSync(Func pFunc) { try { return pFunc.Invoke(GetInstance()); } catch (Exception e) { ShowErrorMessage(e); } return default(T); } #endregion public static void DoMailServiceAsync(Func pFunc, Action pCallBack) { DoAsync(pFunc, pCallBack, true); } public static void DoMailServiceAsync(Action pAction) { DoAsync(pAction, true); } public static T DoMailServiceSync(Func pFunc) { try { return pFunc.Invoke(GetInstance()); } catch (Exception e) { ShowErrorMessage(e); } return default(T); } public static void DoMultipleAccountingServicesAsync(List> pActions, Action pAllDoneCallBack) { DoMulitpleAsync(pActions, pAllDoneCallBack, true); } public static void DoMultipleCustomerServicesAsync(List> pActions, Action pAllDoneCallBack) { DoMulitpleAsync(pActions, pAllDoneCallBack, true); } public static void DoMultipleEmployeeServicesAsync(List> pActions, Action pAllDoneCallBack) { DoMulitpleAsync(pActions, pAllDoneCallBack, true); } public static void DoMultipleEmployeeServicesAsync(List> pActions, Action pAllDoneCallBack, bool showWaitDialog) { DoMulitpleAsync(pActions, pAllDoneCallBack, showWaitDialog); } public static void DoMultipleMailServicesAsync(List> pActions, Action pAllDoneCallBack) { DoMulitpleAsync(pActions, pAllDoneCallBack, true); } public static void DoMultipleOperationsServicesAsync(List> pActions, Action pAllDoneCallBack) { DoMulitpleAsync(pActions, pAllDoneCallBack, true); } public static void DoMultipleOperationsServicesSync(List> pActions) { DoMulitpleSync(pActions, true); } public static void DoMultipleResourceServicesAsync(List> pActions, Action pAllDoneCallBack) { DoMulitpleAsync(pActions, pAllDoneCallBack, true); } public static void DoMultipleValueListServicesAsync(List> pActions, Action pAllDoneCallBack) { DoMulitpleAsync(pActions, pAllDoneCallBack, true); } public static void DoOperationsServiceAsync(Func pFunc, Action pCallBack, bool showWaitDialog) { DoAsync(pFunc, pCallBack, showWaitDialog); } public static void DoOperationsServiceAsync(Func pFunc, Action pCallBack) { DoAsync(pFunc, pCallBack, true); } public static void DoOperationsServiceAsync(Action pAction, Action pCallBack) { DoAsync(pAction, pCallBack, true); } public static void DoOperationsServiceAsync(Action pAction) { DoAsync(pAction, true); } public static void DoOperationsServiceSync(Action pAction) { try { pAction.Invoke(GetInstance()); } catch (Exception e) { ShowErrorMessage(e); } } public static void DoOperationsServiceAsyncNoWait(Func pFunc, Action pCallBack, object state) { DoAsync(pFunc, cb => pCallBack(cb, state), false); } public static T DoOperationsServiceSync(Func pFunc) { try { return pFunc.Invoke(GetInstance()); } catch (Exception e) { ShowErrorMessage(e); } return default(T); } public static void DoQueryServiceAsync(Func pFunc, Action pCallBack) { DoAsync(pFunc, pCallBack, true); } public static void DoQueryServiceAsync(Action pAction) { DoAsync(pAction, true); } public static T DoQueryServiceSync(Func pFunc) { try { return pFunc.Invoke(GetInstance()); } catch (Exception e) { ShowErrorMessage(e); } return default(T); } public static void DoResourceServiceSync(Func pFunc) { try { pFunc.Invoke(GetInstance()); } catch (Exception e) { ShowErrorMessage(e); } } public static void DoResourceServiceAsync(Func pFunc, Action pCallBack, bool showWaitDialog) { DoAsync(pFunc, pCallBack, showWaitDialog); } public static void DoResourceServiceAsync(Func pFunc, Action pCallBack) { DoAsync(pFunc, pCallBack, true); } public static void DoResourceServiceAsync(Action pAction) { DoAsync(pAction, true); } public static void DoResourceServiceAsync(Action pAction, Action pCallback) { DoAsync(pAction, pCallback, true); } public static void DoStreamingServiceAsync(Func pFunc, Action pCallBack, Action pErrorCallBack) { DoAsync(pFunc, pCallBack, pErrorCallBack, true); } public static void DoStreamingServiceAsync(Action pAction) { DoAsync(pAction, true); } public static void DoUserServiceAsync(Func pFunc, Action pCallBack, bool showWaitDialog) { DoAsync(pFunc, pCallBack, showWaitDialog); } public static T DoUserServiceSync(Func pFunc) { return pFunc.Invoke(GetInstance()); } public static void DoUserServiceAsync(Action pAction, bool showWaitDialog) { DoAsync(pAction, showWaitDialog); } public static void DoUserServiceAsync(Action pAction, Action pCallBack, bool showWaitDialog) { DoAsync(pAction, pCallBack, showWaitDialog); } public static void DoValueListServiceAsync(Func pFunc, Action pCallBack) { DoAsync(pFunc, pCallBack, true); } public static void DoValueListServiceSync(Func pFunc, Action pCallBack) { DoAsync(pFunc, pCallBack, true); } public static T DoValueListServiceSync(Func pFunc) { try { return pFunc.Invoke(GetInstance()); } catch (Exception e) { ShowErrorMessage(e); } return default(T); } public static void DoValueListServiceAsync(Action pAction) { DoAsync(pAction, true); } public static void DoReportServiceAsync(Func pFunc, Action pCallBack) { DoAsync(pFunc, pCallBack, true); } public static void DoReportServiceAsync(Func pFunc, Action pCallBack, bool showWaitDialog) { DoAsync(pFunc, pCallBack, showWaitDialog); } public static void DoReportServiceSync(Action pAction) { try { pAction.Invoke(GetInstance()); } catch (Exception e) { ShowErrorMessage(e); } } public static void DoReportServiceAsync(Action pAction) { DoAsync(pAction, true); } public static T DoReportServiceSync(Func pFunc) { try { return pFunc.Invoke(GetInstance()); } catch (Exception e) { ShowErrorMessage(e); } return default(T); } public static void GetAll(Action> pCallBack) { if (typeof(T) == typeof(CompactCustomerDC)) { long? oid = null; if (!BeWoApp.LoggedOnUser.HasRight(UserRightType.ViewAll) && !BeWoApp.LoggedOnUser.HasRight(UserRightType.CustomerView_View)) { oid = BeWoApp.LoggedOnUser.Employee.EmployeeOid; } DoCustomerServiceAsync( s => s.GetAllActiveCustomersCompact(false, oid), r => { List list = r; if (oid != null) { list = list.Where(c => c.IsRelatedToEmployee).ToList(); } list.Sort((x, y) => { return (x.LastName + ", " + x.FirstName).CompareTo(y.LastName + ", " + y.FirstName); }); // (Comparison)new CustomerSorter().GetComparison("LastName", BeWo.Core.AbstractSorter.SortDirection.Ascending)); pCallBack(list.Cast().ToList()); }); } else if (typeof(T) == typeof(CompactEmployeeDC)) { DoEmployeeServiceAsync(s => s.GetAllActiveEmployeesCompact().Cast().ToList(), r => pCallBack(r)); } else if (typeof(T) == typeof(CompactTeamDC)) { DoEmployeeServiceAsync(s => s.GetAllTeamsCompact().Cast().ToList(), r => pCallBack(r)); } else if (typeof(T) == typeof(CompactOrganisationDC)) { DoCustomerServiceAsync(s => s.GetAllActiveOrganisationsCompact().Cast().ToList(), r => pCallBack(r)); } else if (typeof(T) == typeof(CompactPersonDC)) { DoCustomerServiceAsync(s => s.GetAllActivePersonsCompact().Cast().ToList(), r => pCallBack(r)); } else if (typeof(T) == typeof(CompactSupportConceptDC)) { DoCustomerServiceAsync(s => s.GetAllActiveSupportConceptsCompact().Cast().ToList(), r => pCallBack(r)); } else if (typeof(T) == typeof(CompactUserDC)) { DoUserServiceAsync(s => s.GetAllUsersCompact().Cast().ToList(), r => pCallBack(r), true); } else if (typeof(T) == typeof(ResourceDC)) { DoResourceServiceAsync(s => s.GetAllResources().Cast().ToList(), pCallBack, true); } else if (typeof(T) == typeof(TextModuleDC)) { DoOperationsServiceAsync(s => s.GetAllTextModules().Cast().ToList(), pCallBack); } else { throw new NotImplementedException(); } } // returnvalue with callback private static void DoAsync(Func pFunc, Action pCallback, bool showWaitDialog) where TClient : ClientBase, TInterface, new() where TInterface : class { DoAsync(pFunc, pCallback, null, showWaitDialog); } private static void DoAsync(Func pFunc, Action pCallback, bool showWaitDialog, Dispatcher dispatcher) where TClient : ClientBase, TInterface, new() where TInterface : class { if(dispatcher is null) { dispatcher = BeWoApp.Current.Dispatcher; } Action action = (t) => dispatcher.BeginInvoke( DispatcherPriority.Normal, (Action)delegate { pCallback(t); }); DoAsync(pFunc, action, showWaitDialog); } private static void DoAsync(Func pFunc, Action pCallback, Action pErrorCallback, bool showWaitDialog) where TClient : ClientBase, TInterface, new() where TInterface : class { try { if (showWaitDialog) { StartWaiting(); } pFunc.BeginInvoke( GetInstance(), cb => { try { T lResult = pFunc.EndInvoke(cb); if (pCallback != null) { pCallback(lResult); } if (showWaitDialog) { EndWaiting(); } } catch (Exception e) { // bool lThrow = ShowErrorMessage(e); // if (showWaitDialog) EndWaiting(); // if (lThrow) // throw e; if (showWaitDialog) { EndWaiting(); } ShowErrorMessage(e); if (pErrorCallback != null) { pErrorCallback(); } } }, null); } catch (Exception e) { // bool lThrow = ShowErrorMessage(e); // if (showWaitDialog) EndWaiting(); // if (lThrow) // throw e; if (showWaitDialog) { EndWaiting(); } ShowErrorMessage(e); } } // void without callback private static void DoAsync(Action pAction, bool showWaitDialog) where TClient : ClientBase, TInterface, new() where TInterface : class { DoAsync(pAction, null, showWaitDialog); } private static void DoAsync(Action pAction, Action pCallback, bool showWaitDialog, Dispatcher dispatcher) where TClient : ClientBase, TInterface, new() where TInterface : class { Action action = pCallback; if (dispatcher != null) { action = () => dispatcher.BeginInvoke( DispatcherPriority.Normal, (Action)delegate { pCallback(); }); } DoAsync(pAction, action, showWaitDialog); } // private static void DoAsync(Action pAction, Action pCallback, bool ) // void with callback private static void DoAsync(Action pAction, Action pCallback, bool showWaitDialog) where TClient : ClientBase, TInterface, new() where TInterface : class { try { if (showWaitDialog) { StartWaiting(); } pAction.BeginInvoke( GetInstance(), cb => { try { pAction.EndInvoke(cb); if (pCallback != null) { pCallback(); } if (showWaitDialog) { EndWaiting(); } } catch (Exception e) { // bool lThrow = ShowErrorMessage(e); // if (showWaitDialog) EndWaiting(); // if (lThrow) // throw e; if (showWaitDialog) { EndWaiting(); } ShowErrorMessage(e); } }, null); } catch (Exception e) { // bool lThrow = ShowErrorMessage(e); // if (showWaitDialog) EndWaiting(); // if (lThrow) // throw e; if (showWaitDialog) { EndWaiting(); } ShowErrorMessage(e); } } private static void DoMulitpleAsync(List> pActions, Action pAllDoneCallBack, bool showWaitDialog) where TClient : ClientBase, TInterface, new() where TInterface : class { int lCount = pActions.Count; if (lCount == 0) { pAllDoneCallBack(); } foreach (var iAction in pActions) { DoAsync( iAction, delegate { Monitor.Enter(_Lock); lCount--; Monitor.Exit(_Lock); if (lCount == 0) { pAllDoneCallBack(); } }, showWaitDialog); } } private static void DoMulitpleSync(List> pActions, bool showWaitDialog) where TClient : ClientBase, TInterface, new() where TInterface : class { int lCount = pActions.Count; if (showWaitDialog) { StartWaiting(); } foreach (var iAction in pActions) { iAction.Invoke(GetInstance()); } if (showWaitDialog) { EndWaiting(); } } private static void DoSync(Action pAction) where TClient : ClientBase, TInterface, new() where TInterface : class { try { pAction.Invoke(GetInstance()); } catch (Exception e) { ShowErrorMessage(e); } } private static T DoSync(Func pFunc) where TClient : ClientBase, TInterface, new() where TInterface : class { try { return pFunc.Invoke(GetInstance()); } catch (Exception e) { ShowErrorMessage(e); } return default(T); } private static TInterface GetInstance() where TClient : ClientBase, TInterface, new() where TInterface : class { TClient lClient = null; if (_ProxyCache.ContainsKey(typeof(TClient))) { lClient = (TClient)_ProxyCache[typeof(TClient)]; if (lClient.State == CommunicationState.Closing || lClient.State == CommunicationState.Closed || lClient.State == CommunicationState.Faulted) { if (lClient.State == CommunicationState.Faulted) { lClient.Abort(); } lClient = null; _ProxyCache.Remove(typeof(TClient)); } } if (lClient != null) {// EndpointAddresses aktualisieren if (!lClient.Endpoint.Address.Equals(EndpointAddresses[typeof(TInterface)])) { if (lClient.State == CommunicationState.Opened) lClient.Close(); if (_ProxyCache.ContainsKey(typeof(TClient))) _ProxyCache.Remove(typeof(TClient)); lClient = null; } } if (lClient == null) { lClient = new TClient(); if (!_ProxyCache.ContainsKey(lClient.GetType())) { _ProxyCache.Add(typeof(TClient), lClient); } #if DEBUG if (DebugConfig.CurrentConfig is DebugConfig current && current.EnableAutoRemoteDebugging) { if (lClient.Endpoint.Binding is BasicHttpBinding binding) { if (_ServerName?.StartsWith("https") ?? true) { binding.Security.Mode = BasicHttpSecurityMode.Transport; } else { binding.Security.Mode = BasicHttpSecurityMode.None; } } } #endif lClient.Endpoint.Behaviors.Add(new SecurityHeaderEndpointBehavior()); lClient.Endpoint.Behaviors.Add(new MultitenancyEndpointBehavior()); lClient.Endpoint.Address = EndpointAddresses[typeof(TInterface)]; } return lClient; } private static void ShowErrorMessage(Exception e) { BeWoApp.ShowErrorMessage(e); } private static void StartWaiting() { Monitor.Enter(_Lock); if ((++_RunningAsyncs) == 1 && BeWoApp.MainControl != null) { if (BeWoApp.MainControl.CurrentAnimatedBeWoWindow is null) BeWoApp.MainControl.StartWaiting(); else BeWoApp.MainControl.CurrentAnimatedBeWoWindow.StartWaiting(); } Monitor.Exit(_Lock); } private static void EndWaiting() { Monitor.Enter(_Lock); if ((--_RunningAsyncs) == 0 && BeWoApp.MainControl != null) { BeWoApp.MainControl.CurrentAnimatedBeWoWindow?.EndWaiting(); BeWoApp.MainControl.EndWaiting(); } Monitor.Exit(_Lock); } public static bool IsWaiting() { return _RunningAsyncs != 0; } //public static void WaitUntilAsyncIsFinished(Action callback = null) //{ // if (!IsWaiting()) // { // callback(true); // return; // } // try // { // Task.Run(() => WaitUntil(() => !IsWaiting(), callback)); // } // catch(Exception) // { // callback(false); // } //} //private static async Task WaitUntil(Func condition, Action callback, int frequency = 25, int timeout = -1) //{ // var waitTask = Task.Run(async () => // { // while (!condition()) await Task.Delay(frequency); // callback(true); // }); // if (waitTask != await Task.WhenAny(waitTask, // Task.Delay(timeout))) // throw new TimeoutException(); //} } }