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.
- 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.
- auth.linq - Authentication for our REST services and a common RestClient class, that inherits from HtpClient. Loads 1. This is a program script.
- extensions.linq - Extension methods for the RestClient for a specific set of REST calls. Loads 1. and 2. This is a program script.
- 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.