簡體   English   中英

當基類構造函數包含參數時應用依賴注入

[英]Applying Dependency Injection when base-class constructor contains arguments

我有一個基本 API 控制器,我希望所有控制器都應請求執行,以充當安全機制。 這是那個控制器

public abstract class SharepointAuthController : ApiController
{
    private ClientContext clientContext;
    public SharepointAuthController()
 : base()
    {
        ValidateContext();
    }

    protected void ValidateContext()
    {
        if (ControllerContext.Request != null)
        {
            var spContext = SharePointApiControllerContextProvider.Current.GetSharePointContext(ControllerContext);

            clientContext = spContext.CreateUserClientContextForSPHost();

            if (clientContext == null)
            {
                throw new AuthenticationException();
            }

        }
    }
    protected string GetUserName()
    {
        User spUser = null;
        var spContext = SharePointApiControllerContextProvider.Current.GetSharePointContext(ControllerContext);
        using (clientContext = spContext.CreateUserClientContextForSPHost())
        {
            if (clientContext != null)
            {
                spUser = clientContext.Web.CurrentUser;
                clientContext.Load(spUser, user => user);
                clientContext.ExecuteQuery();
                return spUser.Email;
            }
        }
        throw new AuthenticationException();
    }
}

和一個調用它的控制器

    public class CallPointsController : SharepointAuthController
{
    private readonly ICallPointRepository _callPointRepository;

    public CallPointsController(ICallPointRepository callPointRepository)
    {
        _callPointRepository = callPointRepository;
    }

    [SharePointContextFilter]
    [HttpGet]
    [Route("api/callpoints")]
    public List<CallPointDto> Get()
    {
        string user = base.GetUserName();
        if (!string.IsNullOrEmpty(user))
        {
            return _callPointRepository.ListAll();
        }
        return null;
    }
}

我現在想擴展 SharepointAuthController 以獲取有關用戶的其他信息(存在於 DB 中)。 我希望能夠將存儲庫傳遞給基類的構造函數以獲得正確的 DI,就像這樣

  private ClientContext clientContext;
  private _repo Repo;
public SharepointAuthController(Repo repo)
: base()
{
    ValidateContext();
    _repo = repo;
}



protected UserDto GetUserName()
{
    User spUser = null;
    var spContext = SharePointApiControllerContextProvider.Current.GetSharePointContext(ControllerContext);
    using (clientContext = spContext.CreateUserClientContextForSPHost())
    {
        if (clientContext != null)
        {
            spUser = clientContext.Web.CurrentUser;
            clientContext.Load(spUser, user => user);
            clientContext.ExecuteQuery();
            return _repo.GetAdditionalUserInfo(spUser.Email);
        }
    }
    throw new AuthenticationException();
}

然而,僅僅這樣做是行不通的,因為調用這個基類的類沒有正確設置

沒有給出與 Repo 所需的形式參數“repo”相對應的參數

我會以正確的方式解決這個問題嗎? 我可以在沒有 DI 的情況下從 Auth 控制器調用 Repo 類

要回答您的問題:

您需要將參數注入繼承的類並將其傳遞給父類:

public class SharepointAuthController
{
    public SharepointAuthController(Repo repo)
    {
        ValidateContext();
        _repo = repo;
    }
    // rest of controller ...
}

public class CallPointsController : SharepointAuthController
{
    private readonly ICallPointRepository _callPointRepository;

    public CallPointsController(ICallPointRepository callPointRepository, Repo repo) 
     : base(repo)
    {
        _callPointRepository = callPointRepository;
    }
}

另一方面:要進行身份驗證,最好不要使用基本控制器。 而是創建一個繼承自AuthorizeAttribute的屬性(例如: SharepointAuthAttribute )並在其中進行身份驗證。

然后,您可以將該屬性應用於需要它的控制器。

一般不鼓勵使用基類。 常見的說法是:

組合優於繼承

基類通常是個壞主意,因為:

  • 它們導致對具體類的額外依賴,從而引入了強耦合,而 DI 促進了松散耦合。
  • 這種強耦合使您的具體控制器更難測試,這在您的情況下被誇大了,因為您強制在構造函數內部調用業務邏輯,而注入構造函數應該是簡單的
  • 當基類用於橫切關注點時(如您的情況),它們開始成為吸引越來越多橫切關注點的磁石。 這導致基類成為這個不斷變化的類,違反了 單一責任原則開放/封閉原則
  • 這些基類往往需要它們自己的依賴項。 這是有問題的,因為它很容易讓您陷入Temporal Coupling 代碼異味Service Locator 反模式 當您通過基類的構造函數應用依賴項時,派生類的構造函數也需要這些依賴項。 這意味着每次更改或添加對基類的依賴項時,都會導致整個應用程序發生徹底的更改,因為每個派生類也需要更改。 為了緩解這種情況,您的選擇是恢復到屬性注入(這會導致時間耦合)或恢復到服務定位器反模式。 兩種風格都有嚴重的缺點。

因此,組合不是使用基類,而是設計系統的更好方法,尤其是應用橫切關注點(例如安全性)的更好方法。

應用橫切關注點的一種典型方式是使用裝飾器。 然而,Web API 使得無法用裝飾器包裝控制器類型。 對於 Web API, 在控制器級別應用橫切關注點的設計模式是使用DelegatingHandler

您已向基類的構造函數添加了一個參數:

public SharepointAuthController(Repo repo)
 : base()
{
    //...
}

但是您沒有在派生類的構造函數中提供該參數:

public CallPointsController(ICallPointRepository callPointRepository)
{
    //...
}

需要提供:

public CallPointsController(ICallPointRepository callPointRepository, Repo repo)
 : base(repo)
{
    //...
}

否則派生類將無法構造基類,因此不能成為基類的實例。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM