1

我將從這裏開始,介紹一些背景知識。我們有一個基於Onion Architecture概念的ASP.Net MVC Web應用程序。因此,我們有以下的(簡化的)垂直結構:根據特定值選擇接口的實例

  • ASP.Net MVC控制器層
  • 應用服務層
  • 業務服務層

注意:上述被簡化,因爲它不涉及與此問題無關的視圖,存儲庫,域對象等。對於水平結構,我們有一些主要區域由我們稱之爲「物品類型」定義(爲了簡單起見,這個問題將處理兩個樣品物品類型:「ItemTypeA」,「ItemTypeB」等) 。

我們必須擁有每個項目類型都有一個單獨實施業務服務接口:

public interface ISampleBusinessService 
{ 
    string SampleMethod(string arg); 
} 

public class ItemTypeASampleBusinessService : ISampleBusinessService 
{ 
    public string SampleMethod(string arg) 
    { 
     return "Item Type A: " + arg; 
    } 
} 

public class ItemTypeBSampleBusinessService : ISampleBusinessService 
{ 
    public string SampleMethod(string arg) 
    { 
     return "Item Type B: " + arg; 
    } 
} 

上面坐着的是,使用業務服務的應用服務:

public interface ISampleAppService 
{ 
    string SampleMethod(string arg); 
} 

public class SampleAppService 
{ 
    private readonly ISampleBusinessService service; 

    public SampleAppService(ISampleBusinessService service) 
    { 
     this.service = service 
    } 

    public string SampleMethod(string arg) 
    { 
     return service.SampleMethod(arg); 
    } 
} 

而且坐在上面是我們的控制器,它使用應用服務:

public class SampleController : Controller 
{ 
    private ISampelAppService service 

    public SampleController(ISampleAppService service) 
    { 
     this.service = service; 
    } 

    public PartialViewResult SampleAction(string arg) 
    { 
     return PartialView(service.SampleMethod(arg)); 
    } 
} 

請注意,控制器,應用程序服務接口和實現以及業務服務接口都是通用的 - 它們並不關心正在使用哪種項目類型。但是,業務服務實現特定於項目類型。在我們調用控制器上的操作方法(通過視圖中的RenderAction)時,我們知道我們正在處理的是哪種項目類型,但我們不確定要確定使用哪種業務服務實現的最佳方式。我們已經考慮了幾個選項:

  1. 基類控制器和創建項類型特定的控制器繼承者,然後類似的應用程序服務。這感覺就像是一個弱點 - 我們最終會寫出幾個類,除了找出我們正在處理的項類型之外,其他功能都沒有增加。
  2. 將一個標誌傳遞給服務層並在工廠中創建服務實現(例如,在其CreateInstance方法中採用「itemType」參數的SampleBusinessServiceFactory)。這個問題是我們將一個變量向下傳遞幾層,以便我們可以決定實現。迄今爲止我們已經使用了這種方法。泛型 - 我們並沒有真正想到這一點,但似乎也會遇到一些困難(如何在視圖中調用ActionResult調用的泛型的Action方法?)。從某種意義上說,它會是類似的,但是會基於強類型對象/服務而不是使用枚舉/魔術字符串。

什麼方法最適合解決這個問題?新的選擇將受到歡迎。

提供的任何幫助將不勝感激。

乾杯,
扎克

回答

2

這聞起來像大的設計在前面(http://en.wikipedia.org/wiki/Big_Design_Up_Front

但是它很可能調用兩個控制器和操作方法一般: In asp.net mvc is it possible to make a generic controller?

有關調用一些信息具有通用操作結果的操作(導致相同的效果)。

http://www.lostechies.com/blogs/jimmy_bogard/archive/2009/12/12/enabling-ioc-in-asp-net-actionresults-or-a-better-actionresult.aspx

我這種做法的大風扇和了解這些技術有助於誰想要保持自己的控制器非常渺茫任何人。


評論答案:

你並不真的需要調用通用的操作方法,你只需要一種方法來泛型參數傳遞給控制器​​。基本流程是將您的modelType作爲路由參數。您可以使用正確的RouteValueDictionary輕鬆調用通用渲染操作。下面是我的一箇舊的項目一些示例代碼:

開始我的通用控制器:

public GenericController() 
    { 
     TypeTranslator.Configure("Brainnom.Web.Model", "Brainnom.Web"); 
    } 

    [UrlRoute(Path="admin/{modelType}/add", Order=9000)] 
    public virtual ActionResult Add() 
    { 
     return View(new MODEL()); 
    } 

    [HttpPost] 
    [UrlRoute(Path = "admin/{modelType}/add", Order = 9000)] 
    public virtual ActionResult Add(MODEL model, string modelType) 
    { 
     if (!ModelState.IsValid) 
      return View(model); 

     var postedModel = new MODEL(); 

     UpdateModel(postedModel); 

     using (var session = new MongoSession()) 
     { 
      session.Add(postedModel); 
     } 

     return RedirectToRoute("Browse", new { modelType }); 
    } 

和我GenericControllerFactory(我確實需要一天重構)

using System; 
using System.Web.Mvc; 
using System.Web.Routing; 

namespace Brainnom.Web.Controllers 
{ 
    public class GenericControllerFactory : DefaultControllerFactory 
    { 
     protected override Type GetControllerType(System.Web.Routing.RequestContext requestContext, string controllerName) 
     { 
      //the generic type parameter doesn't matter here 
      if (controllerName.EndsWith("Co"))//assuming we don't have any other generic controllers here 
       return typeof(GenericController<>); 

      return base.GetControllerType(requestContext, controllerName); 
     } 

     protected override IController GetControllerInstance(System.Web.Routing.RequestContext requestContext, Type controllerType) 
     { 
      //are we asking for the generic controller? 
      if (requestContext.RouteData.Values.ContainsKey("modelType")) 
      { 
       string typeName = requestContext.RouteData.Values["modelType"].ToString(); 
       //magic time 
       return GetGenericControllerInstance(typeName, requestContext); 
      } 

      if (!typeof(IController).IsAssignableFrom(controllerType)) 
       throw new ArgumentException(string.Format("Type requested is not a controller: {0}",controllerType.Name),"controllerType"); 

      return base.GetControllerInstance(requestContext, controllerType); 
     } 

     /// <summary> 
     /// Returns the a generic IController tied to the typeName requested. 
     /// Since we only have a single generic controller the type is hardcoded for now 
     /// </summary> 
     /// <param name="typeName"></param> 
     /// <returns></returns> 
     private IController GetGenericControllerInstance(string typeName, RequestContext requestContext) 
     { 
      var actionName = requestContext.RouteData.Values["action"]; 

      //try and resolve a custom view model 

      Type actionModelType = Type.GetType("Brainnom.Web.Models." + typeName + actionName + "ViewModel, Brainnom.Web", false, true) ?? 
       Type.GetType("Brainnom.Web.Models." + typeName + ",Brainnom.Web", false, true); 

      Type controllerType = typeof(GenericController<>).MakeGenericType(actionModelType); 

      var controllerBase = Activator.CreateInstance(controllerType, new object[0] {}) as IController; 

      return controllerBase; 
     } 
    } 
} 
+0

我可以看到它的設計看起來有點過於頂端,但在我們的情況下,這是非常合理的。我們正在構建一個平臺,該平臺將提供大約30個網站和其他幾個基於服務器的應用程序 - 共同使用一個共享域圖層;我們需要一個真正可擴展的設計。我們決定的架構是經過大量討論和調查後選定的。至於控制器中的泛型,您是否遇到過從RenderAction調用調用通用操作方法的方法?這就是我們需要做的事情......歡呼你的回覆。 – 2010-10-11 20:59:04

+0

有點遲了(當我問這個問題時,我不再工作在那個地方),但答案看起來很穩固。乾杯,扎克 – 2011-03-08 16:53:08