簡體   English   中英

使用ASP.NET MVC和StructureMap的最佳實踐DI - 如何在ActionResult中注入依賴項

[英]Bestpractice DI with ASP.NET MVC and StructureMap - How to inject dependencies in an 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接口的任何信息。

MappingActionResultstatic 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結合起來要好得多。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM