2 minute read

다른 위치에 있는 dll을 pligin으로 사용하려고 할때 dll을 못 찾는 이슈가 발생 할 수 있다.

배경

3가지 Dll이 있다고 가정해보자.

  • Core.dll
  • Plugin.dll
  • Library.dll

Core.dll이 Plugin.dll을 Reflection을 사용하려 로드 한다. Plugin.dll은 Library.dll을 사용한다. Plugin.dll과 Library.dll은 같은 위치에 배치되어 있다.

이때 Library.dll을 못 찾는 문제가 발생 한다.

해결 방법

AssemblyDependencyResolver를 사용하는 방법

.NET Core에서 어셈블리 언로드 기능을 사용하고 디버그하는 방법을 참고해서 Custom으로 어셈블리 로더를 작성 할 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
using System.Reflection;
using System.Runtime.Loader;

namespace complex
{
    class TestAssemblyLoadContext : AssemblyLoadContext
    {
        private AssemblyDependencyResolver _resolver;

        public TestAssemblyLoadContext(string mainAssemblyToLoadPath) : base(isCollectible: true)
        {
            _resolver = new AssemblyDependencyResolver(mainAssemblyToLoadPath);
        }

        protected override Assembly Load(AssemblyName name)
        {
            string assemblyPath = _resolver.ResolveAssemblyToPath(name);
            if (assemblyPath != null)
            {
                return LoadFromAssemblyPath(assemblyPath);
            }

            return null;
        }
    }
}

이런 방식으로 resolver를 구성하여 작성 할 수 있다.

부가적인 문제가 발생 하는 경우에는 아래처럼 Custom으로 resolver를 작성해야 사용하자

Custom으로 reslover를 작성하는 방법

AssemblyLoadContext 클래스에는 Resolving 이벤트가 있다. 해당 이벤트는 AssemblyLoadContext에 로드 하려고 할 때 어셈블리를 확인하는데 실패하는 경우에 발생 한다.

이를 이용하여 확인에 실패 했을때 같은 위치에 있는 dll을 로드 하는것으로 해결 하는 방법을 생각 할 수 있다.

아래의 코드는 아이디어를 구현한 코드이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
using System.Reflection;
using System.Runtime.Loader;

namespace complex
{
    public class AssemblyLoader : AssemblyLoadContext
    {
        private const string DllAssemblySuffix = ".dll";
        private static readonly string[] s_suffixes = new string[] {  DllAssemblySuffix };

        private SortedSet<string> _dllDirectories = new SortedSet<string>();

        private HashSet<FileInfo> _assemblyCache = new HashSet<FileInfo>();

        public AssemblyLoader()
        {
            AssemblyLoadContext.Default.Resolving += OnResolving;
        }

        public void AddSearchableDirectory(string directory)
        {
            if (Directory.Exists(directory))
            {
                _dllDirectories.Add(directory);

                foreach (var file in Directory.GetFiles(directory))
                {
                    var info = new FileInfo(file);

                    if (s_suffixes.Contains(info.Extension))
                    {
                        _assemblyCache.Add(info);
                    }
                }
            }
        }

        public void RemoveSearchableDirectory(string directory)
        {
            _dllDirectories.Remove(directory);

            _assemblyCache.RemoveWhere(x => x.DirectoryName == directory);
        }

        private Assembly Resolve(AssemblyName assemblyName)
        {
            foreach (string suffix in s_suffixes)
            {
                var info = _assemblyCache.FirstOrDefault(x => x.Name == assemblyName.Name + suffix);

                if (info != null)
                {
                    return LoadFromAssemblyPath(info.FullName);
                }
            }

            return null;
        }

        private Assembly OnResolving(AssemblyLoadContext context, AssemblyName assemblyName)
        {
            return Resolve(assemblyName);
        }
    }

이처럼 Plugin.dll을 로드할때 Library.dll을 확인 하는데 실패하면 OnResolveing이 호출되고 Plugin.dll과 같은 폴더에 있는 dll을 로드 하는 방식으로 문제를 해결 할 수 있다.

Tags: , , , , , , ,

Categories:

Source File: 2021-10-27-custom-loader.md

Updated:

Comments