mercredi 22 septembre 2021

Can't get razor views work with custom assembly loading context

I'm using assembly loading context to load plugin web apps in my host app (previously I used AppDomains)


public class PluginAssemblyLoadContext : AssemblyLoadContext
    {
        private AssemblyDependencyResolver _resolver;
        private IDictionary<string, Assembly> _loadedAssemblies;
        private IDictionary<string, Assembly> _sharedAssemblies;

        private string _path;

        /// <summary>
        /// 
        /// </summary>
        /// <param name="path">assembly file path</param>
        /// <param name="sharedTypes"></param>
        public PluginAssemblyLoadContext(string path, params Type[] sharedTypes)
             : base(isCollectible: false) // TODO: make collectible
        {

            _path = path;

            var fileInfo = new FileInfo(_path);
            if (fileInfo.Exists)
            {
                _resolver = new AssemblyDependencyResolver(_path);

                _sharedAssemblies = new Dictionary<string, Assembly>(StringComparer.OrdinalIgnoreCase);
                foreach (var t in sharedTypes)
                {
                    LoadReferencedAssemblies(t.Assembly);
                }

                _loadedAssemblies = new Dictionary<string, Assembly>();

                var assembly = LoadFromAssemblyPath(fileInfo.FullName);

                _loadedAssemblies.Add(fileInfo.FullName, assembly);
            }
            else
            {
                throw new FileNotFoundException($"File does not exist: {_path}");
            }
        }

        public bool LoadReferencedAssemblies(Assembly assembly)
        {
            if (!_sharedAssemblies.ContainsKey(assembly.Location))
            {
                _sharedAssemblies.Add(assembly.Location, assembly);
                foreach (var an in assembly.GetReferencedAssemblies())
                {
                    var ra = Assembly.Load(an);
                    LoadReferencedAssemblies(ra);
                }
                return true;
            }
            else
            {
                return false;
            }


        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public IEnumerable<Type> GetCommandTypes<T>()
        {
            var cmdType = typeof(T);
            return _loadedAssemblies.Values.SelectMany(a => a.GetTypes()).Where(t => cmdType.IsAssignableFrom(t));
        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="assembly"></param>
        /// <returns></returns>
        public IEnumerable<T> CreateCommands<T>(Assembly assembly)
        {
            foreach (var cmdType in GetCommandTypes<T>())
            {
                yield return (T)Activator.CreateInstance(cmdType);
            }
        }

        protected override Assembly Load(AssemblyName assemblyName)
        {
            var path = _resolver.ResolveAssemblyToPath(assemblyName);
            if (path != null)
            {
                if (_sharedAssemblies.ContainsKey(path))
                {
                    return _sharedAssemblies[path];
                    // return null; // should be the same (using default ALC)
                }
                if (_loadedAssemblies.ContainsKey(path))
                {
                    return _loadedAssemblies[path];
                }
                return LoadFromAssemblyPath(path);
            }
            return null; // using default ALC
        }
    }

The problem is that razor cant load load views normally.

I'm getting:



System.ArgumentNullException: Value cannot be null. (Parameter 'item')
   at Microsoft.AspNetCore.Mvc.Razor.Compilation.CompiledViewDescriptor..ctor(RazorCompiledItem item)
   at Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation.RuntimeViewCompiler.CompileAndEmit(String relativePath)
   at Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation.RuntimeViewCompiler.OnCacheMiss(String normalizedPath)

I've used dnSpy and detected that view assembly generation works ok, but RazorCompiledItemLoader.LoadItems(assembly) gives 0 items because assembly.GetCustomAttributes() gives 0 attributes.

But RazorCompiledItemAttribute is actually there! I've saved view assembly via dnSpy and dumped to disk. When I'am trying to GetCustomAttributes inside plugin during ConfigureServices(IServiceCollection services)... (for example) I'm getting 0 matching attributes. But outside plugin call I.m getting 1 matching attribute! How can I make razor load this RazorCompiledItemAttribute?





Aucun commentaire:

Enregistrer un commentaire