Skip to content

Cannot unload assembly because a reference is stored in System.Dynamic.Utils.TypeExtensions.s_paramInfoCache._entries #119697

@zgabi

Description

@zgabi

Description

I have plugin, loaded into a collectible AssemblyLoadContext.
When I try to unload the assembly, it never unloads, since a refenrece to something which belongs to my collectible assembly stored in the System.Dynamic.Utils.TypeExtensions.s_paramInfoCache._entries array.

Reproduction Steps

I don't know how to reproduce easily, but I logged the Entry from the cache:
Method: System.Linq.IQueryable1[<>f__AnonymousType05[System.Guid,System.String,System.String,Rsoe.PannonRis.Framework.Common.Infrastructure.Nts.enums.LanguageCodeEnum,System.DateTime]] Select[RisMessage,<>f__AnonymousType05](System.Linq.IQueryable1[Rsoe.PannonRis.Framework.Common.Infrastructure.Nts.RisMessage], System.Linq.Expressions.Expression1[System.Func2[Rsoe.PannonRis.Framework.Common.Infrastructure.Nts.RisMessage,<>f__AnonymousType05[System.Guid,System.String,System.String,Rsoe.PannonRis.Framework.Common.Infrastructure.Nts.enums.LanguageCodeEnum,System.DateTime]]])`

Parameters: System.Linq.IQueryable1[Rsoe.PannonRis.Framework.Common.Infrastructure.Nts.RisMessage] source,System.Linq.Expressions.Expression1[System.Func2[Rsoe.PannonRis.Framework.Common.Infrastructure.Nts.RisMessage,<>f__AnonymousType05[System.Guid,System.String,System.String,Rsoe.PannonRis.Framework.Common.Infrastructure.Nts.enums.LanguageCodeEnum,System.DateTime]]] selector

The assebmly I want to unload is: RSOE.PannonRIS.Framework.Services
I can't see this namespace in the logged method/parameters, but the anonymouse type is in this assembly:

// q is NHibernate IQueryable
                var messages = q.Select(t => new
                {
                    t.Id,
                    t.Identification.Originator,
                    t.Identification.From,
                    t.Identification.LanguageCode,
                    t.EntityLastUpdated,
                }).ToList();

Expected behavior

My collectible assebmly unloads

Actual behavior

Not unloading.

Regression?

No response

Known Workarounds

I can clear this cache with reflection:

    public static void ClearCacheDynamic(int idx)
    {
        foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
        {
            try
            {
                var teType = assembly.GetType("System.Dynamic.Utils.TypeExtensions");
                if (teType != null)
                {
                    var cacheField = teType.GetField("s_paramInfoCache", BindingFlags.Static | BindingFlags.NonPublic);
                    var cacheDictType = cacheField.FieldType;
                    var entriesField = cacheDictType.GetField("_entries", BindingFlags.Instance | BindingFlags.NonPublic);
                    var arr = (object[])entriesField.GetValue(cacheField.GetValue(null));

                    var entryType = cacheDictType.GetNestedType("Entry", BindingFlags.NonPublic).MakeGenericType(cacheDictType.GenericTypeArguments);
                    var hashField = entryType.GetField("_hash", BindingFlags.Instance | BindingFlags.NonPublic);
                    var keyField = entryType.GetField("_key", BindingFlags.Instance | BindingFlags.NonPublic);
                    var valueField = entryType.GetField("_value", BindingFlags.Instance | BindingFlags.NonPublic);

                    for (int i = 0; i < arr.Length; i++)
                    {
                        if (idx != -1 && i != idx)
                        {
                            continue;
                        }
                        
                        if (arr[i] != null)
                        {
                            int hash = (int)hashField.GetValue(arr[i]);
                            var key = (MethodBase)keyField.GetValue(arr[i]);
                            var parameters = (ParameterInfo[])valueField.GetValue(arr[i]);
                            string paramsStr = string.Join(",", parameters);
                            ServiceLogger.Instance.Info($"Entry {i}: {hash} {key}, parameters: {paramsStr}");
                        }

                        arr[i] = null;
                    }

                    //cacheField.SetValue(null,
                    //    Activator.CreateInstance(cacheField.FieldType, BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.CreateInstance, null, [75], null));
                }
            }
            catch (Exception ex)
            {
                ServiceLogger.Instance.Info($"Failed to clear paramInfoCache: {assembly.FullName}. {ex.Message}");
            }
        }
    }

After clearing the appropriate element (I did it one by one) the assembly unloads.

Configuration

.NET 9.0.9
Win 11, all updates installed
x64

Other information

No response

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions