基础构造函数与DI和争论

时间:2017-01-03 19:33:01

标签: c# asp.net-web-api dependency-injection

我有一个基本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();
}

但是这样做不起作用,因为调用此基类的类未正确设置

  

没有给出与...对应的论据   所需的正式参数&#39; repo&#39;回购

我是以正确的方式来做这件事的吗?我可以从没有DI的Auth控制器调用Repo类

3 个答案:

答案 0 :(得分:2)

回答你的问题:

您需要将参数注入到继承的类中并将其传递给父级:

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;
    }
}

另外注意:要进行身份验证,最好不要使用基本控制器。而是创建一个继承自SharepointAuthAttribute的属性(例如:AuthorizeAttribute)并在那里进行身份验证。

然后,您可以将该属性应用于需要它的控制器。

答案 1 :(得分:0)

您已在基类中添加了一个参数&#39;构造:

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

但是你没有在派生类中提供该参数&#39;构造:

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

需要提供:

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

否则派生类将无法构造基类,因此无法成为基类的实例。

答案 2 :(得分:0)

一般不鼓励使用基类。常见的说法是:

  

Composition over inheritance

基类通常是个坏主意,因为:

  • 它们导致对具体类的额外依赖性,并且引入强耦合,而DI促进松散耦合。
  • 这种强大的耦合使得您的具体控制器更难以测试,这在您的情况下会夸大,因为您强制在构造函数中调用业务逻辑,而injection constructors should be simple
  • 当基类被用于横切关注时(就像你的情况一样),它们开始成为越来越多跨领域关注的磁铁。这会导致基类成为违反Single Responsibility PrincipleOpen/Closed Principle的不断变化的类。
  • 这些基类往往需要自己的依赖。这是有问题的,因为它可以轻松吸引您进入Temporal Coupling code smellService Locator anti-pattern。通过基类的构造函数应用依赖项时,派生类的构造函数也需要这些依赖项。这意味着每次更改或向基类添加依赖项时,它都会在整个应用程序中引起彻底的更改,因为每个派生类也需要更改。为了缓解这种情况,您的选择是恢复属性注入(导致时间耦合)或恢复到服务定位器反模式。这两种风格都有严重的缺点。

因此,组合不是使用基类,而是设计系统的更好方法,尤其是应用跨安全问题(例如安全性)的更好方法。

应用横切关注点的典型方法是使用装饰器。但是,Web API无法使用装饰器包装控制器类型。使用Web API,designed pattern for applying cross-cutting concerns on the controller levelDelegatingHandler s。

的使用