ASP.NET Web Application (.NET Framework) 프로젝트의 숨겨진 예외 - System.IO.FileNotFoundException
지난 글에 이어,
ASP.NET Web Application (.NET Framework) 프로젝트의 숨겨진 예외 - System.Web.HttpException
; https://www.sysnet.pe.kr/2/0/13240
ASP.NET Web Application 프로젝트의 경우 기본 생성된 예제를 F5 디버깅하면 "System.IO.FileNotFoundException" 예외 메시지 하나를 볼 수 있습니다. thrown 옵션을 켜면 이런 메시지가 나오는데,
System.IO.FileNotFoundException
  HResult=0x80070002
  Message=Could not load file or assembly 'System.Web' or one of its dependencies. The system cannot find the file specified.
  Source=mscorlib
  StackTrace:
   at System.Reflection.RuntimeAssembly._nLoad(AssemblyName fileName, String codeBase, Evidence assemblySecurity, RuntimeAssembly locationHint, StackCrawlMark& stackMark, IntPtr pPrivHostBinder, Boolean throwOnFileNotFound, Boolean forIntrospection, Boolean suppressSecurityChecks)
   at System.Reflection.RuntimeAssembly.InternalLoadAssemblyName(AssemblyName assemblyRef, Evidence assemblySecurity, RuntimeAssembly reqAssembly, StackCrawlMark& stackMark, IntPtr pPrivHostBinder, Boolean throwOnFileNotFound, Boolean forIntrospection, Boolean suppressSecurityChecks) in f:\dd\ndp\clr\src\BCL\system\reflection\assembly.cs:line 1863
  This exception was originally thrown at this call stack:
    System.Reflection.RuntimeAssembly.InternalLoadAssemblyName(System.Reflection.AssemblyName, System.Security.Policy.Evidence, System.Reflection.RuntimeAssembly, ref System.Threading.StackCrawlMark, System.IntPtr, bool, bool, bool) in assembly.cs
^^; System.Web 어셈블리를 찾을 수 없다니... 절망적이군요. 게다가 FileNotFoundException 예외는 개발 중에 자주 삼켜지는 예외라서 "깨진 예외" 하나 방치했다가 향후 개발자들이 작성하게 될 코드에서 FileNotFoundException 예외가 숨겨지면 더 귀찮은 디버깅 문제로 발전할 여지가 있습니다.
그런데, 오류 메시지를 보면 좀 특이한 점이 있습니다. 보통 "System.Web" 어셈블리는 서명을 포함하기 때문에 원래 다음과 같은 이름으로 나왔어야 합니다.
System.Web, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
그러니까, 저 오류가 발생하는 코드에서는 서명된 어셈블리를 지정한 것이 아니고 단순히 "System.Web"이라고 했기 때문에 LoadAssembly에서 GAC를 찾지 않아 오류가 발생하는 것입니다.
따라서, 이것을 수정하려면 2가지 방법이 있습니다. 하나는, 웹 애플리케이션의 빌드 디렉터리인 ./bin에 (C:\Windows\Microsoft.NET\assembly\GAC_64\System.Web\v4.0_4.0.0.0__b03f5f7f11d50a3a 디렉터리에 있는) System.Web.dll 파일을 복사하는 것입니다. 물론, 일일이 복사하는 것은 귀찮은 작업이므로 csproj 파일에 다음과 같이 복사 작업을 맡길 수 있습니다.
<ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
    <Content Include="..\Lib\System.Web.dll">
        <Link>System.Web.dll</Link>
        <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </Content>
</ItemGroup>
또는, AppDomain.CurrentDomain.AssemblyResolve를 이용하면 됩니다. 이를 위해 Global.asax.cs 파일에 다음과 같이 코드를 넣어두면 됩니다.
...[생략]...
namespace WebApplication1
{
    public class Global : HttpApplication
    {
        void Application_Start(object sender, EventArgs e)
        {
            ...[생략]...
            AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
        }
        private System.Reflection.Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
        {
            string text = args.Name;
            foreach (Assembly asm in AppDomain.CurrentDomain.GetAssemblies())
            {
                string asmName = asm.GetName().Name;
                if (asmName == args.Name)
                {
                    return asm;
                }
            }
            return null;
        }
    }
}
[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]