Home

Strange task issue with multiple #load'ed scripts

We have a quick testing environment for testing out REST calls, we have built up through multiple scripts which we then #load into a specific test case script.

  1. rest_extensions.linq - This defines various extension methods for generating a fluent api for wiring up a REST client (HttpClient). This is a statement script.
  2. auth.linq - Authentication for our REST services and a common RestClient class, that inherits from HtpClient. Loads 1. This is a program script.
  3. extensions.linq - Extension methods for the RestClient for a specific set of REST calls. Loads 1. and 2. This is a program script.
  4. test_cases.linq - Loads all 3 above. This is a program script.

The RestClient class has a static method that creates an instance of the class which authentication done, so it is ready to go.
This RestClient.CreateClient works as expected in 2. and 3. but in 4. I get an exception on an async call, stating that "a task may only be disposed if it is in a completion state".

The call chain is like this:
1. await RestClient.AuthenticationLogin
2. await PostAsync (extension method for HttpClient)
3. await ValidateSuccess (extension method for Task)
4. await ReadAsAsync (extension method for Task)

I have a Main() in both script 2 and 3 for testing RestClient, and these work fine. But the test case script fails. Specifically it seems to die in ValidateSuccess. It looks like the "task chain" breaks when the the script references is more than 2 deep, so to speak.

async Task Main()
{
    var environment = Config.LoadEnvironment(EnvironmentType.Test); // loads environment variables
    var client = await RestClient.CreateClient(environment.ApiKey, environment.System);
}

Auth.linq

public async Task<TokenDTO> AuthenticationLogin(LoginDTO dto) 
    => await this.PostAsync<TokenDTO>("authentication/login", (r) => r.AsJson(dto));

rest_extensions.linq

public static async Task<T> PostAsync<T>(this HttpClient client, string path, Action<HttpRequestMessage>? requestPre = null) where T : class
{
    using (var req = path.Uri().Post())
    {
         requestPre?.Invoke(req);
        using (var res = client.SendAsync(req))
        {
            return await res.ValidateSuccess().ReadAsAsync<T>();
        }
   }
}

public static Uri Uri(this string text) => new Uri(text, UriKind.RelativeOrAbsolute);

public static HttpRequestMessage Post(this Uri uri) => new HttpRequestMessage(HttpMethod.Post, uri);

public static async Task<HttpResponseMessage> ValidateSuccess(this Task<HttpResponseMessage> msg)
{
    var m = await msg;
    if (m.IsSuccessStatusCode) return m;

    string result = null;
    try
    {
        result = await m.Content.ReadAsStringAsync();
    }
    catch { }
    throw new HttpResponseInfoException(m, result);
}

public static async Task<T?> ReadAsAsync<T>(this Task<HttpResponseMessage> msg) where T : class
{
    var m = await msg;
    if (m.Content.Headers.ContentType.MediaType.EndsWith("/json", StringComparison.CurrentCultureIgnoreCase))
    {
        try
        {
            return await m.Content.ReadFromJsonAsync<T>();
        }
        catch (Exception ex)
        {
            string content = await m.Content.ReadAsStringAsync();
            throw new DeserializeErrorException(content, ex);
        }
    }
}

public static HttpRequestMessage AsJson(this HttpRequestMessage msg, object content)
{
    if (content == null)
        return msg;

    var opts = new JsonSerializerOptions(JsonSerializerDefaults.Web) { IgnoreNullValues = true };
    var json = JsonSerializer.SerializeToUtf8Bytes(content, content.GetType(), opts);

    msg.Content = new ByteArrayContent(json);
    msg.Content.Headers.ContentLength = json.Length;
    msg.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json");

    return msg;
}

Comments

  • I eventually tracked the issue to an exception caused by ReadFromJsonAsync: Something about "Method not found".
    It turned out the issue was caused by some additional references. So once I removed some of these the issue went away.
    I am guessing one of these referenced an older dll where the signature of ReadFromJsonAsync was different, or method itself didn't exist yet.

  • Are you using Linqpad 8 by any chance? I noticed that a lot of my scripts had to be "recompiled" when I transitioned over. Whether they were My Extensions, "library" scripts that are #loaded in, and such. Even had to delete things in localappdata to remove some of those cached files. I think that while Linqpad is generally good at separating things out for side installations, there may be some things that aren't 100% working in that area.

Sign In or Register to comment.