简体   繁体   English

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

[英]Create instance of interface with dependency injection

I have a base controller and before every page load I want to get the current user. 我有一个基本控制器,在每次页面加载之前,我都希望获得当前用户。 I originally had a constructor in my BaseController that looked like this 我最初在BaseController中有一个类似下面的构造函数

public BaseController(ISystemUserCommand command)
{
    _systemUserCommand = command
}

The problem with this then is that every controller that inherits from the BaseController would have to contain the ISystemUserCommand in its constructor, which I don't think would be good. 这样的问题是,从BaseController继承的每个控制器都必须在其构造函数中包含ISystemUserCommand ,我认为这不是很好。

Instead I tried to create just an instance of the service class (shown below - it's the commented line under var sid...) but I need to pass in user service. 相反,我尝试仅创建服务类的实例(如下所示-这是var sid下的注释行...),但是我需要传递用户服务。 How would I pass in the user service here or is this a bad way of doing it? 我将如何在此处传递用户服务,或者这样做是一种不好的方式?

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

    ...
}

You could use property injection instead of constructor injection, via the base class, eg using unity: 您可以通过基类使用属性注入而不是构造函数注入,例如,使用unity:

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

This would mean the interface reference is only on the base class. 这意味着接口引用仅在基类上。

See here for the full examples. 有关完整示例,请参见此处

EDIT, Autofac example: 编辑,Autofac示例:

You don't need property attributes on the dependency, 您不需要依赖项上的属性属性,

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

Just to register the properites to auto resolve on the autofac builder: 只需注册可在autofac构建器上自动解析的属性:

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

See autofac property injection here . 请参阅此处的 autofac属性注入。

First of all, it does not seem a good idea to have OnActionExecuting override in the controller. 首先,在控制器中具有OnActionExecuting覆盖似乎不是一个好主意。 You can use filters, that are specially designed for this purpose. 您可以使用专门为此目的设计的过滤器。 And it seems that is the main reason you created the BaseController at all. 似乎这就是您创建BaseController主要原因。

Regarding the problem with injecting the system command in all the required service, I would do so, but without inheriting from a base class, since I generally prefer aggregation to inheritance. 关于在所有必需的服务中注入系统命令的问题,我会这样做,但不从基类继承,因为我通常更喜欢聚合而不是继承。 That would mean that each controller that needs to work with the service will get it. 这意味着需要与该服务一起使用的每个控制器都将获得它。

Another option that I have used few times to abstract some operations is to create a UserSerivce that will provide the required operations to the controllers. 我几次用于抽象某些操作的另一个选项是创建UserSerivce ,它将为控制器提供所需的操作。 It will have ISystemUserCommand and HttpContext injected inside so that all of your controllers won't have to do the job. 它将在内部注入ISystemUserCommand和HttpContext,以便您的所有控制器都不必执行此工作。 You can either use HttpContext.Current as static or abstract it away if you need testability. 您可以将HttpContext.Current用作静态对象,也可以将其抽象化(如果需要可测试性)。

Moreover I would not recommend property injection since it is more obscure than constructor injection that should be preferred if possible. 此外,我不建议使用属性注入,因为它比构造函数注入更为晦涩,如果可能的话,应该首选属性注入。

You can read more about filters here . 您可以在此处阅读有关过滤器的更多信息。 Unfortunately if you use filters it's not that easy to inject in filters themselves and mostly done with property injection or ServiceLocator pattern (which is not good usually). 不幸的是,如果您使用过滤器,则本身并不容易插入过滤器,并且通常通过属性注入或ServiceLocator模式(通常不好)来完成。 It's possible to do better with some amount of voodoo though. 可以用一些伏都教做得更好。 I think that SimpleInjector has a lot of examples and tutorials on how to apply DI to filters in MVC, maybe they even have a nuget package now to ahieve that. 我认为SimpleInjector上有很多示例和教程,介绍如何将DI应用于MVC中的过滤器,也许他们现在甚至拥有一个nuget软件包来实现这一目标。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM