控制器和视图之间的抽象层

时间:2014-06-27 16:09:01

标签: c# asp.net-mvc design-patterns asp.net-mvc-5 architecture

我正在尝试在我的控制器和我的视图之间创建另一个层,以便我可以根据用户的“客户端ID”将不同版本的视图传递给用户。

我有以下代码:

public class HomeController : Controller
{
    //
    // GET: /Home/
    public ActionResult Index()
    {
        // set client
        var client = new Client();
        client.Id = Guid.NewGuid();
        client.Name = "Foo";

        // set user
        var user = new User();
        user.Id = Guid.NewGuid();
        user.ClientId = client.Id;
        user.Name = "Foo";

        return ViewRenderer.RenderView("AddComplete", client);
    }
}

我的ViewRenderer类如下所示:

public static class ViewRenderer
{
    public static ViewResult RenderView(string view, Guid clientId)
    {
        string viewName = GetViewForClient(view, clientId);
        return Controller.View(view);
    }

    public static string GetViewForClient(string view, Guid clientId)
    {
        // todo: logic to return view specific to the company to which a user belongs...
    }
}

问题是,return Controller.View(view);中的行RenderView(string view, Guid clientId)给了我错误:

  

System.Web.Mvc.Controller.View()'因其无法访问   保护水平

我很想知道如何解决这个错误,或者是否有更好的方法来做我想做的事情,即显示不同版本的视图,这些版本特定于用户所属的公司所属

编辑:我在脑海中挣扎的另一种选择......

是否有办法覆盖View()方法,以便我可以在其前面加上目录名,例如,属于“Acme Co.”的用户将调用与View("MyView")之类的其他所有人相同的控制器操作,但该方法实际上将调用View("AcmeCo/MyView")但是,我实际上并没有在我的控制器中编写该代码,它只是从用户的客户端ID属性派生的

3 个答案:

答案 0 :(得分:3)

您只需替换视图引擎,而不是添加其他抽象。

编写自己的View引擎(以下是如何从RazorViewEngine开始)

public class ByIdRazorViewEngine : RazorViewEngine
{
   protected override IView CreateView(ControllerContext controllerContext, 
        string viewPath, string masterPath)
{
    var id = // get something from controller context controllerContext
    var newViewPath = CalculateViewPathFromId(id);

    return base.CreateView(controllerContext, newViewPath, masterPath);
}

并在Global.asax.cs注册:

protected void Application_Start()
{
    ViewEngines.Engines.Clear();

    ViewEngines.Engines.Add(new ByIdRazorViewEngine());
}

答案 1 :(得分:2)

View()方法是受保护的成员。您只能从派生类型(例如HomeController类)中访问它。另外,您尝试以静态方式访问它。

您可以创建一个公开您的专用视图逻辑的基本控制器。为了便于说明,我将其称为DynamicViewControllerBase

public class HomeController : DynamicViewControllerBase
{
    //
    // GET: /Home/
    public ActionResult Index()
    {
        // set client
        var client = new Client();
        client.Id = Guid.NewGuid();
        client.Name = "Foo";

        // set user
        var user = new User();
        user.Id = Guid.NewGuid();
        user.ClientId = client.Id;
        user.Name = "Foo";

        return RenderView("AddComplete", client);
    }
}

public class DynamicViewControllerBase : Controller
{
    protected ViewResult RenderView(string view, Guid clientId)
    {
        string viewName = GetViewForClient(view, clientId);
        return View(view);
    }

    // Unless you plan to use methods and properties within 
    // the instance of `Controller`, you can leave this as 
    // a static method.
    private static string GetViewForClient(string view, Guid clientId)
    {
        // todo: logic to return view...
    }
}

答案 2 :(得分:1)

如果您想拥有的是控制器前面的公司名称,请将RoutePrefix属性应用于您的控制器。

示例:

[RoutePrefix(@"{company}")]
public partial class HomeController : Controller 
{


}

在你的RouteConfig文件中,

public static void RegisterRoutes(RouteCollection routes)
{
    routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

    // Make sure this line is added
    routes.MapMvcAttributeRoutes();

}

由于您的用户必须通过身份验证才能登录自己的帐户,因此一旦他们对自己进行了身份验证,您就可以:

  • 使用公司名称在用户计算机上存储Cookie
  • 在每个请求上调用您的数据库以检索此信息
  • 使用ViewData []
  • 等。

获得公司名称后,您可以构建具有该名称的网址。

示例:

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Login(LoginViewModel model)
{
    // ... authenticate user etc

    // Redirect to 
    // foo.com/abc/home
    return this.RedirectToAction("Index", "Home", new { company = "abc" });
}

如果您正试图解决这个问题,我怀疑您是否能够首先通过路线获取Web请求,并且路由决定执行哪个控制器/操作,但是知道您的行动需要执行以检索的公司名称。