首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

C# 反射機制實戰避坑:Assembly.Load 找不到 DLL、命名空間與檔名不匹配、暫存檔衝突完整解決方案

     編輯:關於C語言

問題現場重現

專案採用 **工廠模式 + 反射 + 快取** 動態建立 DAL 實例:

// 設定檔

   


// 工廠核心代碼
string path = ConfigurationManager.AppSettings["DAL"]; // "SmsSystem.SQLServerDAL"
object obj = Assembly.Load(path).CreateInstance(cacheKey); // 拋 FileNotFoundException

異常:`FileNotFoundException: 未能載入檔案或組件 "SmsSystem.SQLServerDAL" 或其某一相依性。系統找不到指定的檔案。`

根本原因:組件名稱 ≠ DLL 檔名

  • `Assembly.Load(string)` 參數是 **組件完全名稱 (Assembly FullName)** 或 **簡短名稱 (Assembly Name)**,**不是檔案路徑**。
  • 專案屬性 → **Application → Assembly name** 決定編譯輸出的 **組件名稱** 與 **DLL 檔名**。
  • 若專案 Assembly name = `SQLServerDAL`,輸出 `SQLServerDAL.dll`,但設定檔寫 `SmsSystem.SQLServerDAL` (命名空間) → **CLR 找不到名為 `SmsSystem.SQLServerDAL` 的組件**。

解決方案:統一組件名稱與設定檔

方法一:修改專案 Assembly Name (推薦)

  1. 專案右鍵 → Properties → Application → **Assembly name** 改為 `SmsSystem.SQLServerDAL`。
  2. 2. 重新建置 → 輸出 `SmsSystem.SQLServerDAL.dll`。 3. 設定檔 `` **完全匹配** → `Assembly.Load` 成功。

方法二:設定檔寫簡短名稱 (需 DLL 在探測路徑)

  • 若 Assembly name = `SQLServerDAL`,設定檔 ``。
  • 確保 `SQLServerDAL.dll` 在 **應用程式基底目錄** (`AppDomain.CurrentDomain.BaseDirectory`) 或 `privatePath` 指定的子目錄。

方法三:用 `Assembly.LoadFrom` / `LoadFile` 指定完整路徑 (不推薦常規用)

string dllPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "SmsSystem.SQLServerDAL.dll");
Assembly asm = Assembly.LoadFrom(dllPath); // 或 LoadFile (不載入相依性上下文)
object obj = asm.CreateInstance("SmsSystem.SQLServerDAL.CustEmployee"); // 完整型別名稱

⚠️ `LoadFrom` 會進入 **LoadFrom 上下文**,可能導致同一組件載入多份 (如本文後續暫存檔衝突)。**優先用 `Assembly.Load` + 正確組件名稱**。

後續衝突:暫存 ASP.NET 檔案雙重載入

錯誤:

類型 "SmsSystem.BLL.CustEmployee" 同時存在於 `Temporary ASP.NET Files\...\BLL.DLL` 和 `Temporary ASP.NET Files\...\SmsSystem.BLL.DLL` 中。

原因

    1. 修改 Assembly Name 後重新建置,**舊 DLL (`SQLServerDAL.dll`) 仍殘留在 `bin/` 目錄**。 2. ASP.NET 動態編譯將 `bin/` 下**所有 DLL** 複製到暫存目錄編譯。 3. 同時存在 `SQLServerDAL.dll` (舊) 和 `SmsSystem.SQLServerDAL.dll` (新),兩者**包含相同類型** (命名空間未變) → **型別身分衝突** (同一型別從不同組件載入視為不同型別)。

徹底解決步驟

  1. **清理解決方案** (Build → Clean)。
  2. **手動刪除 `bin/`、`obj/` 目錄下所有舊 DLL**。
  3. **清除 ASP.NET 暫存檔**:
    • 命令列:`RunDll32.exe %windir%\Microsoft.NET\Framework\v4.0.30319\aspnet_isapi.dll,ASP.NET_Clean`
    • 或手動刪除:`C:\Windows\Microsoft.NET\Framework64\v4.0.30319\Temporary ASP.NET Files\\`
  4. **重新建置整個方案**。

最佳實踐:反射工廠模式標準化

public static class DalFactory
{
    private static readonly ConcurrentDictionary _cache = new();
    private static readonly string _assemblyName = ConfigurationManager.AppSettings["DalAssembly"]; // 組件名稱
    private static readonly string _namespace = ConfigurationManager.AppSettings["DalNamespace"];   // 命名空間前綴

    public static T Create() where T : class
    {
        string cacheKey = typeof(T).FullName; // 介面完整名稱作為 Key
        return (T)_cache.GetOrAdd(cacheKey, _ => CreateInstance());
    }

    private static T CreateInstance()
    {
        string typeName = $"{_namespace}.{typeof(T).Name.Substring(1)}"; // IUserDal → UserDal
        try
        {
            // 1. 載入組件 (依組件名稱,需與專案 Assembly Name 一致)
            Assembly asm = Assembly.Load(_assemblyName);
            // 2. 建立實例 (依完整型別名稱)
            object instance = asm.CreateInstance(typeName, true, BindingFlags.CreateInstance, null, null, null, null);
            if (instance is T t) return t;
            throw new InvalidOperationException($"型別 {typeName} 不實作 {typeof(T)}");
        }
        catch (Exception ex)
        {
            Log.Error(ex, $"建立 DAL 實例失敗: {typeName}");
            throw new DalFactoryException($"無法建立 {typeof(T).Name} 實例", ex);
        }
    }
}

關鍵檢查清單 (部署前必驗)

檢查項目正確標準
專案 Assembly Name與設定檔 `DalAssembly` 完全一致 (含大小寫)
輸出 DLL 檔名`AssemblyName.dll`
實作類別完整名稱`{Namespace}.{ClassName}` (如 `SmsSystem.SQLServerDAL.UserDal`)
介面與實作專案分離介面在 `.IDAL`,實作在 `.SQLServerDAL` / `.MySqlDAL`
`bin/` 目錄無殘留舊 DLL每次建置前 Clean 或 CI 流程自動清理
暫存檔無舊版本部署腳本包含清理 Temporary ASP.NET Files

總結

反射動態建立實例**核心前提:組件名稱 (Assembly Name) 與設定檔、專案屬性、輸出 DLL 三者完全一致**。`Assembly.Load` 載入的是**組件身分**,而非檔案路徑。**修改 Assembly Name 後必須徹底清理 `bin/`、`obj/` 與 ASP.NET 暫存目錄**,否則殘留舊 DLL 導致型別身分衝突。封裝統一的 `DalFactory`,集中管理組件名稱、命名空間慣例、快取與異常處理,是企業級專案的標準做法。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved