使用依赖注入创建接口实例

时间:2016-10-28 17:14:16

标签: c# dependency-injection asp.net-mvc-5

我有一个基本控制器,在每次加载页面之前,我想获得当前用户。我最初在我的BaseController中有一个看起来像这样的构造函数

public BaseController(ISystemUserCommand command)
{
    _systemUserCommand = command
}

这样做的问题是,每个继承自BaseController的控制器都必须在其构造函数中包含ISystemUserCommand,我认为这不会很好。

相反,我试图只创建一个服务类的实例(如下所示 - 它是var sid下的注释行...)但是我需要传递用户服务。我如何在这里传递用户服务,或者这是一种不好的方式吗?

public abstract class BaseController : Controller
{
    public SystemUserViewModel CurrentUser { get; set; }

    private readonly ISystemUserCommand _systemUserCommand;

    public SystemUserViewModel GetCurrentUser()
    {
        if (HttpContext == null || HttpContext.User == null) return null;
        if (CurrentUser != null) return CurrentUser;

        var sid = System.Web.HttpContext.Current.Request.LogonUserIdentity.User.ToString();

        //var command = new SystemUserCommand();

        CurrentUser = _systemUserCommand.GetUser(sid);

        return CurrentUser;
    }

    public void SetUserInformation(SystemUserViewModel currentUser)
    {
        ViewBag.UserId = currentUser.SystemUserId;
        ViewBag.FullName = string.Format("{0} {1}", currentUser.FirstName, currentUser.LastName);
        ViewBag.FirstName = currentUser.FirstName;
        ViewBag.LastName = currentUser.LastName;
        ViewBag.CurrentUser = currentUser;
    }

    protected override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        var currentUser = GetCurrentUser();

        if (currentUser != null)
        {
            if (currentUser.IsActive)
            {
                SetUserInformation(currentUser);
            }
            else
                filterContext.Result = RedirectToAction("denied", "unauthorized");
        }
        else
            filterContext.Result = RedirectToAction("denied", "unauthorized");

        base.OnActionExecuting(filterContext);
    }
}

public class SystemUserCommand : ISystemUserCommand
{
    private readonly ISystemUserBusiness _systemUserBusiness;

    public SystemUserCommand(ISystemUserBusiness systemUserBusiness)
    {
        _systemUserBusiness = systemUserBusiness;
    }

    ...
}

2 个答案:

答案 0 :(得分:2)

您可以通过基类使用属性注入而不是构造函数注入,例如使用unity:

public abstract class BaseController : Controller
{
    [Dependency]
    public ISystemUserCommand SystemUserCommand { get; set; }
}

这意味着接口引用仅在基类上。

有关完整示例,请参阅here

编辑,Autofac示例:

您不需要依赖项的属性属性

public abstract class BaseController : Controller
{
    public ISystemUserCommand SystemUserCommand { get; set; }
}

只需在autofac构建器上注册要自动解析的属性:

builder.RegisterControllers(typeof(MvcApplication).Assembly).Where(t => t.IsAssignableFrom(typeof(BaseController))).PropertiesAutowired();

请参阅autofac属性注入here

答案 1 :(得分:1)

首先,在控制器中覆盖OnActionExecuting似乎不太好。您可以使用专门为此目的而设计的过滤器。这似乎是你创建BaseController的主要原因。

关于在所有必需的服务中注入系统命令的问题,我会这样做,但是没有从基类继承,因为我通常更喜欢聚合到继承。这意味着需要使用该服务的每个控制器都能获得它。

我用了几次抽象一些操作的另一个选项是创建一个UserSerivce,它将为控制器提供所需的操作。它将注入ISystemUserCommand和HttpContext,这样你的所有控制器都不必完成这项工作。如果需要可测试性,可以将HttpContext.Current用作静态,也可以将其抽象出来。

此外,我不建议使用属性注入,因为它比构造函数注入更加模糊,如果可能的话应该首选。

您可以阅读有关过滤器here的更多信息。不幸的是,如果你使用过滤器,那么注入过滤器本身并不容易,而且主要使用属性注入或ServiceLocator模式(通常不好)。虽然有一些伏都教可以做得更好。我认为SimpleInjector有很多关于如何将DI应用到MVC中的过滤器的示例和教程,也许他们现在甚至可以使用nuget包来解决这个问题。