Unity容器構造函數參數循環引用問題及解決

關鍵字:

Unity .NET5 .NET6 循環引用 循環依賴 Quartz StdSchedulerFactory

起因

在.NET6/.NET5環境中,使用Unity替換默認容器,用到了Quartz任務管理,發現在註冊ISchedulerFactory為StdSchedulerFactory後,獲取ISchedulerFactory會報錯:

System.StackOverflowException:「Exception_WasThrown」

根據報錯推測是產生了循環引用,導致堆棧溢出;

進一步嘗試發現不用Unity用默認容器,沒有這個問題;

直接看解決方法,到文章末尾。

重現

定義測試實體

public class TestModel
{
    public string Code { get; set; }

    public string Name { get; set; }

    public TestModel()
    { 
    }

    //自身引用; 注意這裡
    public TestModel(TestModel model)
    {
        this.Code = model.Code;
        this.Name = model.Name;
    }
}

定義接口及實現

public interface ITest
{
    string Hello(string name);
}

public class TestImpl : ITest
{
    public TestImpl()
    {
    }
         
    //用unity容器時,model定義成循環引用了,會報錯
    public TestImpl(TestModel model)
    {
    }

    public string Hello(string name)
    {
        return $"Hello, {name}";
    }
}

應用

註冊及獲取ITest

默認容器運行正常

引用unity容器:略

再次運行,報錯

解決

這是個很「奇怪」的問題,接口實現類TestImpl,因為有一個「沒用到」的構造函數,其參數TestModel有一個「沒用到」的構造函數,引用自身;會導致Unity容器獲取該接口時報錯。好繞,看圖。

看「起因」Quartz中的接口實現StdSchedulerFactory,NameValueCollection有引用自身的構造函數。

解決辦法:刪除接口實現中的帶參構造函數或自身引用的構造函數都可以;但在涉及三方dll的時候不方便。

 

更好的辦法:對自身引用的參數做下註冊。

public void ConfigureServices(IServiceCollection services)
{
    //添加TestModel註冊
    services.AddTransient(typeof(TestModel), _ => new TestModel());

    //註冊ITest
    services.AddScoped<ITest, TestImpl>();
}

添加註冊之後,運行正常。