ASP.NET MVC和StructureMap的最佳实践DI - 如何在ActionResult中注入依赖项

我编辑了我的整个问题,所以不要怀疑:) 好吧,我想要一个带有域模型数据和一些附加参数的
ActionResult
,即用于分页列表的页面索引和页面大小。它决定自己是否返回PartialViewResult或ViewResult,具体取决于Web请求的类型(ajax请求与否)。 应使用IMappingService自动映射引用的数据,IMappingService负责将任何域模型数据转换为视图模型。 为了简单起见,MappingService使用AutoMapper。 MappingActionResult:
public abstract class MappingActionResult : ActionResult
{
    public static IMappingService MappingService;
}
BaseHybridViewResult:
public abstract class BaseHybridViewResult : MappingActionResult
{
    public const string defaultViewName = "Grid";

    public string ViewNameForAjaxRequest { get; set; }
    public object ViewModel { get; set; }

    public override void ExecuteResult(ControllerContext context)
    {
        if (context == null) throw new ArgumentNullException("context");
        var usePartial = ShouldUsePartial(context);
        ActionResult res = GetInnerViewResult(usePartial);

        res.ExecuteResult(context);
    }

    private ActionResult GetInnerViewResult(bool usePartial)
    {
        ViewDataDictionary viewDataDictionary = new ViewDataDictionary(ViewModel);
        if (String.IsNullOrEmpty(ViewNameForAjaxRequest))
        {
            ViewNameForAjaxRequest = defaultViewName;
        }

        if (usePartial)
        {
            return new PartialViewResult { ViewData = viewDataDictionary, ViewName = ViewNameForAjaxRequest };
        }

        return new ViewResult { ViewData = viewDataDictionary };
    }

    private static bool ShouldUsePartial(ControllerContext context)
    {
        return context.HttpContext.Request.IsAjaxRequest();
    }
}
AutoMappedHybridViewResult:
public class AutoMappedHybridViewResult<TSourceElement, TDestinationElement> : BaseHybridViewResult
{
    public AutoMappedHybridViewResult(PagedList<TSourceElement> pagedList)
    {
        ViewModel = MappingService.MapToViewModelPagedList<TSourceElement, TDestinationElement>(pagedList);
    }

    public AutoMappedHybridViewResult(PagedList<TSourceElement> pagedList, string viewNameForAjaxRequest)
    {
        ViewNameForAjaxRequest = viewNameForAjaxRequest;
        ViewModel = MappingService.MapToViewModelPagedList<TSourceElement, TDestinationElement>(pagedList);
    }

    public AutoMappedHybridViewResult(TSourceElement model)
    {
        ViewModel = MappingService.Map<TSourceElement, TDestinationElement>(model);
    }

    public AutoMappedHybridViewResult(TSourceElement model, string viewNameForAjaxRequest)
    {
        ViewNameForAjaxRequest = viewNameForAjaxRequest;
        ViewModel = MappingService.Map<TSourceElement, TDestinationElement>(model);
    }
}
控制器中的用法:
public ActionResult Index(int page = 1)
{
    return new AutoMappedHybridViewResult<TeamEmployee, TeamEmployeeForm>(_teamEmployeeRepository.GetPagedEmployees(page, PageSize));
}
所以你可以看到
IMappingService
是隐藏的。当使用
AutoMappedHybridViewResult
时,控制器不应该对
IMappingService
接口有任何了解。
MappingActionResult
static IMappingServer
是否合适,还是我违反了DI原则?     
已邀请:
我认为更好的设计是拥有一个依赖于IMappingService的ViewResultFactory,然后你可以将它注入你的控制器。然后你这样称呼它:
public class MyController : Controller
{
    IViewResultFactory _viewResultFactory;
    ITeamEmployeeRepository _teamEmployeeRepository;

    public MyController(IViewResultFactory viewResultFactory)
    {
        _viewResultFactory = viewResultFactory;
    }

    public ActionResult MyAction(int page, int pageSize)
    {
        return
            _viewResultFactory.GetResult<TeamEmployee, TeamEmployeeForm>(
                _teamEmployeeRepository.GetPagedEmployees(page, pageSize));
    }
}
实现是这样的(您需要为每个HybridViewResult构造函数创建重载):
public HybridViewResult<TSourceElement, TDestinationElement> GetResult<TSourceElement, TDestinationElement>(PagedList<TSourceElement> pagedList)
{
    return new HybridViewResult<TSourceElement, TDestinationElement>(_mappingService, pagedList);
}
这样您就可以从控制器中隐藏实现,而不必依赖容器。     
您可以注入IMappingService的几个不同点。 http://codeclimber.net.nz/archive/2009/04/08/13-asp.net-mvc-extensibility-points-you-have-to-know.aspx是一个很好的网站,可以帮助您选择适当的可扩展性.NET MVC的要点。 如果你想坚持让这个功能成为派生的ActionResult,那么我认为如果你愿意,可以将依赖项放在ActionInvoker中,但是Controller对我来说更有意义。如果您不想在Controller中使用IMappingService,则可以始终将其包装在HybridViewResultFactory中,并在Controller中访问该对象。在这种情况下,您的快捷方式如下所示:
public HybridViewResult<TSourceElement, TDestinationElement> AutoMappedHybridView<TSourceElement,TDestinationElement>(PagedList<TSourceElement> pagedList, string viewNameForAjaxRequest)
{
    HybridViewResultFactory.Create<TSourceElement, TDestinationElement>(pagedList, viewNameForAjaxRequest);
 }
等等 我不确定为什么你需要使用ActionResult,但如果没有理由明确需要它,你可以创建一个HybridViewModel类和一个注入了映射服务依赖项的HybridViewModelBinder类。 我假设您想要使用构造函数注入,但如果您在UI程序集中具有StructureMap依赖项,则可以访问静态依赖项解析程序类(如Clowers所述)。 如果我明白你使用ActionResult的原因,这个问题会更容易给出明确的答案。 您似乎正在使用操作结果来处理两个功能,这些功能不一定总是在一起,并且可以单独使用。此外,没有明确的迹象表明它需要在ActionResult中。 据推测,您可以(a)利用Automapper功能获得除html(ViewResult)输出之外的其他结果,以及(b)您可以利用自动检测Ajax请求的功能,而无需自动化模型。 在我看来,可以使用视图模型的自动化将视图模型直接注入控制器操作,从而消除控制器对IMappingService的依赖性。您需要的是一个要注入IMappingService的ModelBinder类(我假设其实现包含存储库或数据存储区类型依赖项)。 这是一篇很好的文章,解释了如何利用模型绑定器:http://odetocode.com/blogs/scott/archive/2009/04/27/6-tips-for-asp-net-mvc-model-binding.aspx。 然后,您可以覆盖需要自动执行的类中的DefaultModelBinder,如下所示:
   public ActionResult DoItLikeThis([AutoMap(typeof(MyDomainModelClass))]MyViewModelClass viewModel){
               //controller action logic
   } 
现在,关于HybridViewResult,我建议您使用Action Filter来处理它。因此,您可以使用ActionResult或ViewResultBase作为操作方法的Result类型,并使用动作过滤器对其进行装饰,即:
   [AutoSelectViewResult]
   public ViewResultBase AndDoThisLikeSo(){
               //controller action logic
   } 
我认为总的来说这比将这两个功能与ActionResult结合起来要好得多。     

要回复问题请先登录注册