Skip to content

Instantly share code, notes, and snippets.

@dmorosinotto
Last active September 10, 2023 15:27
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dmorosinotto/091fd87ffded98b773b1bbf22cf59b7f to your computer and use it in GitHub Desktop.
Save dmorosinotto/091fd87ffded98b773b1bbf22cf59b7f to your computer and use it in GitHub Desktop.
C# AsyncUtils utility to call an async method from a sincronous context IT WORKS!
using System.Threading;
using System.Threading.Tasks;
/// <summary>
/// Helper class to run async methods within a sync process.
/// ORIGINAL CODE: https://www.ryadel.com/en/asyncutil-c-helper-class-async-method-sync-result-wait/
/// </summary>
public static class AsyncUtil {
private static readonly TaskFactory _taskFactory = new TaskFactory(CancellationToken.None,
TaskCreationOptions.None,
TaskContinuationOptions.None,
TaskScheduler.Default);
/// <summary>
/// Executes an async Task method which has a void return value synchronously
/// USAGE: AsyncUtil.RunSync(() => yourMethodAsync());
/// </summary>
/// <param name="task">Task method to execute</param>
public static void RunSync(Func<Task> task) => _taskFactory
.StartNew(task)
.Unwrap().GetAwaiter().GetResult();
/// <summary>
/// Executes an async Task<T> method which has a T return type synchronously
/// USAGE: T result = AsyncUtil.RunSync(() => yourMethodAsync<T>());
/// </summary>
/// <typeparam name="TResult">Return Type</typeparam>
/// <param name="task">Task<T> method to execute</param>
/// <returns></returns>
public static TResult RunSync<TResult>(Func<Task<TResult>> task) => _taskFactory
.StartNew(task)
.Unwrap().GetAwaiter().GetResult();
/// <summary>
/// Force the current Thread/Task to temporary "loose processor" to give opportunity to other Thread/Task to execute.
/// ORIGINAL CODE BY @MircoVanini THE MASTER OF C# THREADING
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void ThreadContextSwitching() { Task.Factory.StartNew(() => Thread.Sleep(0)).GetAwaiter().GetResult(); }
}
public class GenericApiResult<TValue>
{
public bool IsSuccessful { get; private set; }
public string ErrorMessage { get; private set; }
public TValue Value { get; private set; }
private GenericApiResult()
{
IsSuccessful = false;
ErrorMessage = "";
}
public static GenericApiResult<T> WithValue<T>(T value) => new GenericApiResult<T>()
{
IsSuccessful = true,
Value = value,
ErrorMessage = ""
};
public static GenericApiResult<T> WithErrorMessage<T>(string error) => new GenericApiResult<T>()
{
IsSuccessful = false,
ErrorMessage = error,
Value = default,
};
public static implicit operator bool(GenericApiResult<TValue> res)
{ //USATO PER POTER SCRIVERE CONDIZIONI DEL TIPO if (!res) ...
return res.IsSuccessful;
}
}
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Net.Http;
public class MyHttpService {
private readonly ILogger _logger;
public NeosService(ILogger logger)
{
_logger = logger;
}
public Task<GenericApiResult<IEnumerable<INeosCollection>>> GetCollectionsAsync()
{
return this.callApi<IEnumerable<INeosCollection>>("collections");
}
public Task<GenericApiResult<IEnumerable<INeosTag>>> GetTagsAsync()
{
List<string> parameterList = new List<string>();
if (!string.IsNullOrEmpty(collectionIdentifier))
{
parameterList.Add($"collection={Uri.EscapeUriString(collectionIdentifier)}");
}
string parameters = null;
if (parameterList.Count() > 0)
{
parameters = "?" + string.Join("&", parameterList);
}
return this.callApi<IEnumerable<INeosTag>>("tags", parameters);
}
#region "Helper chiamata API Http"
private static string BaseUrl
{
get
{
return ConfigurationManager.AppSettings["ExternalAPI.BaseUrl"];
}
}
private static string Token
{
get
{
return ConfigurationManager.AppSettings["ExternalAPI.Token"];
}
}
private async Task<GenericApiResult<T>> callApi<T>(string method, string escapedParameters = null) where T : class
{
const string defaultErrorMessage = "Error calling exteral API";
try
{
using (var http = new HttpClient())
{
//http.DefaultRequestHeaders.Add("ContentType", "application/json");
http.DefaultRequestHeaders.Add("Authentication", $"Bearer {Token}");
var url = $"{BaseUrl}/dam/api/{method}";
if (!string.IsNullOrEmpty(escapedParameters))
{
url += escapedParameters;
}
var response = await http.GetAsync(url);
if (response.IsSuccessStatusCode)
{
var content = await response.Content.ReadAsStringAsync();
var val = JsonConvert.DeserializeObject<T>(content);
return GenericApiResult<T>.WithValue<T>(val);
}
else
{
string errorDetail = defaultErrorMessage;
if (response.StatusCode == System.Net.HttpStatusCode.BadRequest)
{
var content = response.Content.ReadAsStringAsync().Result;
var res = JsonConvert.DeserializeObject<INeosErrorResponse>(content);
errorDetail = res.detail;
}
return GenericApiResult<T>.WithErrorMessage<T>(errorDetail);
}
}
}
catch (Exception e)
{
_logger.Error<NeosService>("callNeosApi Error calling " + method, e);
return GenericApiResult<T>.WithErrorMessage<T>(defaultErrorMessage + method);
}
}
#endregion
}
using System;
public class SampleUsage {
private readonly _myHttpService: MyHttpService;
void ThisIs_SYNCRONOUS_Context()
{
var cols = Utility.RunSync(() => _myHttpService.GetCollectionsAsync());
if (!cols) throw new Exception("error getting collections!");
foreach(c in cols.Value) {
var tags = Utility.RunSync(() => _myHttpService.GetTagsAsync(c));
if (tags) Console.WriteLine(string.Join(tags.Value, ", "));
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment