После выгрузки плагина не освобождается dll из AssemblyLoadContext
За основу взят пример с github dotnet https://github.com/dotnet/samples/blob/main/core/tutorials/Unloading/Host/Program.cs, однако метод GetMessage() из PluginClass был переработан, возвращает Task<string>, имеет следующее тело:
using (var httpClient = new HttpClient())
{
var response = await httpClient.GetAsync(new Uri("https://jsonplaceholder.typicode.com/posts"));
var responseContent = await response.Content.ReadAsStringAsync();
var doc = JsonDocument.Parse(responseContent);
var models = doc.RootElement.EnumerateArray().Select(element => new Model
{
UserId = element.GetProperty("userId").GetInt32(),
Id = element.GetProperty("id").GetInt32(),
Title = element.GetProperty("title").GetString(),
Body = element.GetProperty("body").GetString()
}).ToList();
}
return "Hello from the unloadable plugin";
Этот код прекрасно работает и Assembly выгружается после вызова Unload() у AssemblyLoadContext. Однако если использовать System.Text.Json или Newtonsoft.Json, то dll не собирается выгружаться из контекста, как долго я бы ни ждал (я пробовал вплоть до 100 секунд и это сильно запредельное время в моих условиях использования).
Пример проблемного кода с использование System.Text.Json ниже:
using (var httpClient = new HttpClient())
{
var response = await httpClient.GetAsync(new Uri("https://jsonplaceholder.typicode.com/posts"));
var responseContent = await response.Content.ReadAsStringAsync();
var models = JsonSerializer.Deserialize<List<Model>>(responseContent, new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
}
return "Hello from the unloadable plugin";
Пример проблемного кода с использование Newtonsoft.Json ниже:
using (var httpClient = new HttpClient())
{
var response = await httpClient.GetAsync(new Uri("https://jsonplaceholder.typicode.com/posts"));
var responseContent = await response.Content.ReadAsStringAsync();
var models = JsonConvert.DeserializeObject<List<Model>>(responseContent);
}
return "Hello from the unloadable plugin";
Как считаете, с чем данное поведение связано и можно ли его каким-либо образом избежать?
Ответы (1 шт):
Спустя некоторое время мне удалось разобраться в проблеме в контексте System.Text.Json (что в моём случае абсолютно устраивает меня).
Для рефлексии при работе с типами пользователя создаются долгоживущие экземпляры JsonSerializerOptions (скорее всего под этим подразумевается кэш опций, подробнее здесь).
Для решения данной проблемы предусмотрен метод ClearCache() в классе System.Text.Json.JsonSerializerOptionsUpdateHandler. Иначе говоря, в сборке Host (на основе примера из dotnet/samples из вопроса), где мы подключаем плагин, перед Unload() достаточно добавить следующее, и проблема уйдет:
var assembly = typeof(JsonSerializerOptions).Assembly;
var updateHandlerType = assembly.GetType("System.Text.Json.JsonSerializerOptionsUpdateHandler");
var clearCacheMethod = updateHandlerType?.GetMethod("ClearCache", BindingFlags.Static | BindingFlags.Public);
clearCacheMethod?.Invoke(null, new object?[] { null });