Asp.net mvc 基于特定值选择接口实例

Asp.net mvc 基于特定值选择接口实例,asp.net-mvc,design-patterns,architecture,domain-driven-design,Asp.net Mvc,Design Patterns,Architecture,Domain Driven Design,我将从一点背景知识开始。我们有一个ASP.NETMVCWeb应用程序,它位于大致基于该概念的结构上。因此,我们有以下(简化的)垂直结构: Net MVC控制器层 应用服务层 业务服务层 注意:上面的内容被简化了,因为它不涉及与此问题无关的视图、存储库、域对象等 对于水平结构,我们有一些主要区域由我们所称的“项目类型”定义(为了简单起见,这个问题将处理两个示例项目类型:“ItemTypeA”、“ItemTypeB”等) 我们有一个业务服务接口,每个项目类型有一个单独的实现: public in

我将从一点背景知识开始。我们有一个ASP.NETMVCWeb应用程序,它位于大致基于该概念的结构上。因此,我们有以下(简化的)垂直结构:

  • 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) );
    }
}
请注意,控制器、应用程序服务接口和实现以及业务服务接口都是通用的,它们不关心使用哪种项类型。但是,业务服务实现是特定于项类型的。在调用控制器上的action方法时(通过视图中的RenderAction),我们知道要处理的是哪种项目类型,但我们不确定确定确定使用哪种业务服务实现的最佳方式。我们考虑了几个选项:

  • 基类控制器并创建特定于项类型的控制器继承器,然后创建与应用程序服务类似的内容。这感觉像是一个薄弱的解决方案——我们最终会编写一些类,这些类除了计算出我们要处理的项目类型之外,在功能方面什么都没有添加
  • 将标志向下传递到服务层,并在工厂中创建服务实现(即SampleBusinessServiceFactory,它在其CreateInstance方法中采用“itemType”参数)。问题在于,我们将一个变量向下传递几层,以便决定实现。到目前为止,我们已经使用了这种方法
  • 泛型-我们还没有真正考虑过这一点,但这似乎也会有一些困难(如何从视图中的ActionResult调用中使用泛型调用Action方法?)。从某种意义上讲,它与向下传递标志类似,但它将基于强类型化对象/服务,而不是使用枚举/魔术字符串
  • 什么方法最适合解决这个问题?新的选择将受到欢迎

    我们将非常感谢您提供的任何帮助

    干杯,

    Zac

    这闻起来像是前面的大设计()

    但是,很可能同时调用控制器和操作方法:

    有关使用通用操作结果调用操作(产生相同效果)的一些信息

    我非常喜欢这种方法,学习这些技术对任何想保持控制器超薄的人都有帮助


    答复:

    您实际上不需要调用泛型操作方法,只需要一种将泛型参数传递给控制器的方法。基本流程是将模型类型作为管线参数包含在内。您可以使用正确的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(有一天我确实需要重构)

    使用系统;
    使用System.Web.Mvc;
    使用System.Web.Routing;
    命名空间Brainnom.Web.Controllers
    {
    公共类GenericControllerFactory:DefaultControllerFactory
    {
    受保护的覆盖类型GetControllerType(System.Web.Routing.RequestContext RequestContext,string controllerName)
    {
    //泛型类型参数在这里并不重要
    if(controllerName.EndsWith(“Co”)//假设我们这里没有任何其他通用控制器
    返回类型(通用控制器);
    返回base.GetControllerType(requestContext,controllerName);
    }
    受保护的覆盖IController GetControllerInstance(System.Web.Routing.RequestContext RequestContext,类型controllerType)
    {
    //我们要的是通用控制器吗?
    if(requestContext.RouteData.Values.ContainsKey(“modelType”))
    {
    string typeName=requestContext.RouteData.Values[“modelType”].ToString();
    //神奇时刻
    返回GetGenericControllerInstance(typeName,requestContext);
    }
    如果(!typeof(IController).IsAssignableFrom(controllerType))
    抛出新的ArgumentException(string.Format(“请求的类型不是控制器:{0}”)、controllerType.Name、“controllerType”);
    返回base.GetControllerInstance(requestContext,controllerType);
    } 
    /// 
    ///返回与请求的typeName关联的通用IController。
    ///因为我们只有一个通用控制器,所以现在类型是硬编码的
    /// 
    /// 
    /// 
    私有IController GetGenericControllerInstance(字符串类型名、RequestContext RequestContext)
    {
    var actionName=requestContext.RouteData.Values[“action”];
    //尝试并解析自定义视图模型
    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=typeof(GenericController)。MakeGenericType(actionModelType);
    var controllerBase=Activator.CreateInstance(controllerType,新对象[0]{})作为IController;
    返回控制
    
    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;
            }
        }
    }