【问题标题】:ASP.NET MVC ViewHelpers and Dependency InjectionASP.NET MVC ViewHelpers 和依赖注入
【发布时间】:2015-03-27 09:23:03
【问题描述】:

我想创建一个 ViewHelper 来本地化我的 ASP.NET MVC 应用程序。像这样的:

public class Translator
{
    private readonly ITranslationRepository _repo;
    public Translator(ITranslationRepository repo)
    {
        _repo = repo;
    }

    public static string Translate(TranslationEnum translationEnum)
    {
        return _repo.GetTranslation(translationEnum, Session.LanguageId);
    }
}

(剃刀)视图中的用法如下所示:

<p>@Translator.Translate(TranslationEnum.WelcomeMessage)</p>

现在问题当然是,我不能将Translate方法设为静态,因为我需要访问实例变量_repo

如何将存储库注入到 ViewHelper 中,以便可以在上述视图中使用它?

【问题讨论】:

    标签: asp.net-mvc dependency-injection ninject view-helpers onion-architecture


    【解决方案1】:

    视图的职责只是将从控制器返回的数据转换为 HTML 结构。视图很难(甚至不可能)自动测试,所以最好是让它们尽可能地愚蠢。

    不要在您的视图中使用Translator,而是将其注入您的控制器并让控制器调用Translator。这解决了一系列问题:

    • 它使视图保持简单。
    • 它提高了可维护性。
    • 它提高了可测试性。
    • 它提高了对象图的可验证性(因为您不会依赖静态方法调用或服务定位器反模式)。

    长话短说,向控制器的视图模型添加一个属性并将其返回给视图。示例:

    public class HomeController : Controller {
        private readonly ITranslator translator;
        public HomeController(ITranslator translator) {
            this.translator = translator
        }
        public ActionResult Index() {
            this.View(new HomeViewModel {
                WelcomeMessage = this.translator.Translate(TranslationEnum.WelcomeMessage)
            });
        }
    }
    

    您的视图可能如下所示:

    @model HomeViewModel
    
    <p>@Model.WelcomeMessage</p>
    

    【讨论】:

    • 这就是我首先尝试的。但是我无法从 View 访问 Controller 属性,还是我错过了什么?
    • @Sandro:视图应该对控制器一无所知,反之亦然。查看我的更新。
    • 我明白了,但这对于这个项目是不可行的。在某些视图中,我们甚至使用实体作为模型。这真的是唯一的方法吗?我的意思是,这种方式比使用 ViewHelper 需要更多的步骤,并且需要大量的翻译......唷
    • 你能解释一下为什么这不可行吗?您可以通过将其包装在视图模型类中来很容易地用额外的数据“丰富”您的实体类。例如:new HomeViewModel { Message = "foo", Product = product }.
    • 抱歉,我上次的评论有一个迟到的编辑:这是不可行的,因为它需要额外的工作。例如为每个翻译扩展模型,并在控制器中分配它。将它与大量翻译结合起来......这不再有趣了。
    【解决方案2】:

    首先,您的设计意图是错误的,因为它违反了单一责任原则。为什么翻译器依赖存储库?

    其次,为什么需要翻译,可以使用asp.net全球化? click me我们不应该重新发明轮子。

    第三,所有的 html 助手都是扩展方法,必须是静态的。

    所以我的建议是,如果您必须使用翻译器,请重构 Translator 类,将存储库与它解耦,然后从那里创建扩展方法。

    或者你可以使用全球化,这听起来很可怕,但相信我,它并不像看起来那么难。

    public class Translator
    {
        private static ITranslationRepository _repo;
    
        public static ITranslationRepository Repo
        {
             get { /*check null here before return*/ return _repo; } set { _repo = Repo; }
        }
    
        public Translator()
        {
    
        }
    
        public static string Translate(TranslationEnum translationEnum)
        {
            return _repo.GetTranslation(translationEnum, Session.LanguageId);
        }
    }
    

    【讨论】:

    • 违反 SRP 的情况如何?翻译器只有一个责任:它接受一个键并根据注入的 ITranslationRepository 实现的逻辑为该键返回一个值。我不在乎实现最终是否使用 ASP.NET 全球化、数据库甚至网络服务。
    • 如果它接受一个key并返回一个字符串,为什么要传入repository呢?好的,您需要存储库来检索密钥,但这不是翻译者的责任。
    • 不要考虑数据库存储库。 ITranslationRepository 只是一个接口。我也可以将其命名为 ITranslationServiceITranslationHelper。我从助手中抽象出检索逻辑,以便随时更改。
    • @Sandro 嗨,伙计,在这种情况下,我认为您可以使用 setter 注入。你要做的是你需要对翻译器做一个小的改动。我已经编辑了我的帖子。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-02-25
    • 1970-01-01
    • 2010-12-10
    • 2013-12-04
    • 1970-01-01
    • 2010-12-07
    相关资源
    最近更新 更多