包装和控制器

时间:2013-08-28 17:39:52

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

我的MVC项目中有很多控制器,它们都有一个共同点。它们都有currentUser属性和currentSettings属性。我这样使用DI就是这样做的:

[Authorize]
public class _UsersController : Controller
{

    #region Fields

    private readonly ProfileService service;
    private readonly Profile currentUser;
    private readonly Settings currentSettings;

    #endregion

    #region Constructors

    public _UsersController()
    {
        var companyWrapper = new CompanyWrapper();
        var profileWrapper = new ProfileWrapper();
        var settingsWrapper = new SettingsWrapper();

        var companyId = companyWrapper.CurrentCompanyId();

        service = new ProfileService(companyId);
        currentUser = profileWrapper.CurrentUser();
        currentSettings = settingsWrapper.CurrentSiteSettings();
    }

    #endregion

}

有没有更好的方法来保持currentUser和当前设置的持续性? 我尝试这样做的一种方法是将它放在自定义的AtributeFilter类中,并将当前用户/设置存储在会话中。我只想得到一个整洁的设计模式,以满足我的需求。

2 个答案:

答案 0 :(得分:2)

您可以轻松地为项目创建基本控制器类:

public abstract class ProjectBaseController : Controller
{

    #region Fields

    protected readonly ProfileService service;
    protected readonly Profile currentUser;
    protected readonly Settings currentSettings;

    #endregion

    #region Constructor

    public ProjectBaseController()
    {
        var companyWrapper = new CompanyWrapper();
        var profileWrapper = new ProfileWrapper();
        var settingsWrapper = new SettingsWrapper();

        var companyId = companyWrapper.CurrentCompanyId();

        service = new ProfileService(companyId);
        currentUser = profileWrapper.CurrentUser();
        currentSettings = settingsWrapper.CurrentSiteSettings();
    }

    #endregion

}

然后记住让你继承的类调用基础构造函数:

public class _UserController : ProjectBaseController
{
    _UserController() : base()
    {
        // _UserConroller Specific initalization here...
    }
}

答案 1 :(得分:0)

第1步:停止使用区域。说真的,不要使用它们。

第2步:开始使用Dependency Injection pattern

[Authorize]
public class _UsersController : Controller
{
    private readonly ProfileService service;
    private readonly Profile currentUser;
    private readonly Settings currentSettings;

    public _UsersController(ProfileService service, Profile currentUser, 
        Settings currentSettings)
    {
        this.service = service;
        this.currentUser = currentUser;
        this.currentSettings = currentSettings;
    }
}

我们通过将类型实际需要的所有依赖项传入(注入)到构造函数中来简化_UsersController。那些包装类在_UsersController中是隐藏的,因为它不应该关心这个。

创建_UsersController现在看起来像这样:

var companyWrapper = new CompanyWrapper();
var profileWrapper = new ProfileWrapper();
var settingsWrapper = new SettingsWrapper();

var companyId = companyWrapper.CurrentCompanyId();

var service = new ProfileService(companyId);
var currentUser = profileWrapper.CurrentUser();
var currentSettings = settingsWrapper.CurrentSiteSettings();

new _UsersController(service, currentUser, currentSettings);

您必须将此代码放在自定义控制器工厂中:

public class MyControllerFactory : DefaultControllerFactory
{
    protected override IController GetControllerInstance(
        RequestContext requestContext, Type controllerType)
    {
        if (controllerType == typeof(_UsersController))
        {
            // Create controller here
            new _UsersController(service, currentUser, currentSettings);
        }

        return base.GetControllerInstance(requestContext, controllerType);
    }
}

并在启动期间在MVC中注册:

protected void Application_Start()
{
    ControllerBuilder.Current.SetControllerFactory(typeof(MyControllerFactory));
}

第3步:分组相关服务。

公司和用户信息似乎高度相关。例如,当前公司可能总是当前用户的公司。可能有多家公司的用户同时使用该网站。站点信息可能不太相关,可能包含在部署时修复的信息。根据具体情况,将这些类分组可能会很好,例如,将公司信息作为属性放在Profile类下:

[Authorize]
public class _UsersController : Controller
{
    private readonly Profile currentUser;
    private readonly Settings currentSettings;

    public _UsersController(Profile currentUser, Settings currentSettings)
    {
        this.currentUser = currentUser;
        this.currentSettings = currentSettings;
    }
}

创建代码现在看起来像这样(CompanyWrapperProfileService现在隐藏在Profile类中:

var profileWrapper = new ProfileWrapper();
var settingsWrapper = new SettingsWrapper();

var currentUser = profileWrapper.CurrentUser();
var currentSettings = settingsWrapper.CurrentSiteSettings();

new _UsersController(currentUser, currentSettings);

步骤4:开始使用依赖注入容器:

创建自定义控制器工厂只会移动问题,这个类很快就会成为维护的噩梦。因此,您可以使用依赖注入框架而不是编写自己的控制器。

由于我非常习惯使用Simple Injector,我将展示此框架,但请注意,还有很多其他框架(AutofacCastle Windsor,{{ 3}},StructureMap)可以做同样的事情。

我们可以执行以下操作,而不是编写自定义控制器工厂:

protected void Application_Start()
{
    var container = new Container();

    container.RegisterMvcControllers(Assembly.GetExecutingAssembly());

    container.Register<Profile>(() => new ProfileWrapper().CurrentUser());
    container.Register<Settings>(() => new SettingsWrapper().CurrentSiteSettings());

    DependencyResolver.SetResolver(new SimpleInjectorDependencyResolver(container));
}

这使用了Ninject NuGet包。

因为我们已将ProfileSettings类注册到容器中,所以我们可以代表我们创建_UsersController,因为它为我们分析了该控制器的构造函数参数并根据构造函数参数的类型信息注入其依赖项。

很高兴你只需要注册ProfileSettings一次,DI框架会在所有控制器(以及系统中的所有其他类)中注入这些类型,这些类型依赖于那些服务。

第5步:为您的注册申请适当的生活方式。

由于当前用户在单个网络请求期间不会更改,因此无意中创建新的ProfileWrapper并反复拨打CurrentUser()。可以在该请求的持续时间内缓存该用户对象。另一方面,当前站点设置可能不会在应用程序的生命周期内发生变化,因此重新创建SettingsWrapper并调用CurrentSiteSettings只是愚蠢。

我们应该做的是应用这些对象的正确缓存。这可以按如下方式完成:

container.RegisterPerWebRequest<Profile>(() => new ProfileWrapper().CurrentUser());
container.RegisterSingle<Settings>(() => new SettingsWrapper().CurrentSiteSettings());

我们将来自Register的呼叫更改为RegisterPerWebRequestRegisterSingle。该框架将为我们完成其余的工作。

哟dawg。我听说你喜欢模式,所以我在你的模式中加了一个模式,这样你就可以在抽象时抽象出来。

enter image description here