从内部MVC控制器确定的局部视图模型 [英] Determine the model of a partial view from the controller within MVC

查看:121
本文介绍了从内部MVC控制器确定的局部视图模型的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我现在的问题是,我有我想确定正在使用什么样的模式由它的局部视图。

My current problem is that I have a partial view that I want to determine what model is being used by it.

我不得不应付我的项目几只怪的场景,所以我会尽量在这里勾勒出它,也许有人可以提供一个更好的方式来做到这一点。

I have had to deal with a few strange scenarios for my project so I will try to outline it here, maybe someone can offer a better way to do this.

我设计有点像谷歌的iGoogle页面。与多个小部件能够走动,或根据需要进行配置的主页。当前系统加载实际控件的数据不同步查看我的应用程序中的POST到控制器。该控制器要么渲染HTML的局部视图可以返回(然后加载到页面视图JQUERY),或只是直接的HTML / JavaScript的存储在数据库中。

I am designing something like the Google iGoogle page. A main page with multiple widgets that are able to move around or be configured as needed. The current system loads the actual widget's data asynchronously view a POST to a controller within my application. That controller will either render a partial view to HTML that can be returned (and then loaded into the page view JQUERY) or just straight HTML/JavaScript that is stored in a database.

这是对我工作的罚款,我有一个模型,它包含了很多正在通过数据库描述,然后使用的局部视图选项字典中的小部件。当我想将数据传递到局部视图的问题来了。最好的解决办法我能想出是该控制器具有确定哪种模式有问题的局部视图使用,有一些功能,将填补该模型,然后通过它,用局部视图一起,到将它呈现给函数在控制器内的HTML。

This was working fine for me, I had a model for the widgets that holds a dictionary of options that are described via the database, and then used by the partial view. The problem came when I wanted to pass data to a partial view. The best solution I could come up with was having the controller determine which model the partial view in question uses, have some function that will fill the model, and then pass it, along with the partial view, to the function that will render it to HTML within the controller.

我意识到这是MVC一个奇怪的场景(该层混合...)和基本设计任何意见或实施,这将是极大的AP preciated。

I realize this is an odd scenario for MVC (the layers are blending...) and any advice on fundamental design, or implementation of this would be greatly appreciated.

我目前正在使用MVC3 /剃刀。随意问任何其他问题。

I am currently using MVC3/Razor. Feel free to ask any other questions.

推荐答案

我试制出可能解决这一点,因为它似乎是一个有趣的问题。我希望这是对你有用。

I prototyped a possible solution to this, because it seemed like a fun problem. I hope it's useful to you.

首先,车型。我决定创建两个小部件,一个是新闻,以及一个用于时钟。

First, the models. I decided to create two 'widgets', one for news, and one for a clock.

public class NewsModel
{
    public string[] Headlines { get; set; }

    public NewsModel(params string[] headlines)
    {
        Headlines = headlines;
    }
}

public class ClockModel
{
    public DateTime Now { get; set; }

    public ClockModel(DateTime now)
    {
        Now = now;
    }
}

控制器

我的控制器不知道的意见事情。它的作用是返回一个模型,但模型所要求的视图动态获取正确的模型的能力。

Controller

My controller doesn't know anything about the views. What it does is returns a single model, but that model has the ability to dynamically fetch the right model as required by the view.

public ActionResult Show(string widgetName)
{
    var selector = new ModelSelector();
    selector.WhenRendering<ClockModel>(() => new ClockModel(DateTime.Now));
    selector.WhenRendering<NewsModel>(() => new NewsModel("Headline 1", "Headline 2", "Headline 3"));
    return PartialView(widgetName, selector);
}

代表们使用,所以,如果实际使用了正确的模型只创建/牵强。

Delegates are used so that the correct model is only created/fetched if it is actually used.

该控制器使用ModelSelector是pretty简单 - 它只是让与会代表创造每个模型类型的包:

The ModelSelector that the controller uses is pretty simple - it just keeps a bag of delegates to create each model type:

public class ModelSelector
{
    private readonly Dictionary<Type, Func<object>> modelLookup = new Dictionary<Type, Func<object>>();

    public void WhenRendering<T>(Func<object> getter)
    {
        modelLookup.Add(typeof(T), getter);
    }

    public object GetModel(Type modelType)
    {
        if (!modelLookup.ContainsKey(modelType))
        {
            throw new KeyNotFoundException(string.Format("A provider for the model type '{0}' was not provided", modelType.FullName));
        }

        return modelLookup[modelType]();
    }
}

的意见 - 简单的解决方案

现在,实现一个视图最简单的方法是:

The Views - Simple solution

Now, the easiest way to implement a view would be:

@model MvcApplication2.ModelSelector
@using MvcApplication2.Models
@{
    var clock = (ClockModel) Model.GetModel(typeof (ClockModel));
}

<h2>The time is: @clock.Now</h2>

您可以在这里结束,使用这种方法。

You could end here and use this approach.

这是pretty难看。我希望我的看法是这样的:

That's pretty ugly. I wanted my views to look like this:

@model MvcApplication2.Models.ClockModel
<h2>Clock</h2>
@Model.Now

@model MvcApplication2.Models.NewsModel
<h2>News Widget</h2>
@foreach (var headline in Model.Headlines)
{
    <h3>@headline</h3>
}

要完成这项工作,我不得不创建一个自定义视图引擎。

To make this work, I had to create a custom view engine.

在剃刀视图被编译,它继​​承了的ViewPage&LT; T&GT; ,其中 T @model 。所以我们可以使用反射来找出什么类型的观点通缉,并选择它。

When a Razor view is compiled, it inherits a ViewPage<T>, where T is the @model. So we can use reflection to figure out what type the view wanted, and select it.

public class ModelSelectorEnabledRazorViewEngine : RazorViewEngine
{
    protected override IView CreateView(ControllerContext controllerContext, string viewPath, string masterPath)
    {
        var result = base.CreateView(controllerContext, viewPath, masterPath);

        if (result == null)
            return null;

        return new CustomRazorView((RazorView) result);
    }

    protected override IView CreatePartialView(ControllerContext controllerContext, string partialPath)
    {
        var result = base.CreatePartialView(controllerContext, partialPath);

        if (result == null)
            return null;

        return new CustomRazorView((RazorView)result);
    }

    public class CustomRazorView : IView
    {
        private readonly RazorView view;

        public CustomRazorView(RazorView view)
        {
            this.view = view;
        }

        public void Render(ViewContext viewContext, TextWriter writer)
        {
            var modelSelector = viewContext.ViewData.Model as ModelSelector;
            if (modelSelector == null)
            {
                // This is not a widget, so fall back to stock-standard MVC/Razor rendering
                view.Render(viewContext, writer);
                return;
            }

            // We need to work out what @model is on the view, so that we can pass the correct model to it. 
            // We can do this by using reflection over the compiled views, since Razor views implement a 
            // ViewPage<T>, where T is the @model value. 
            var compiledViewType = BuildManager.GetCompiledType(view.ViewPath);
            var baseType = compiledViewType.BaseType;
            if (baseType == null || !baseType.IsGenericType)
            {
                throw new Exception(string.Format("When the view '{0}' was compiled, the resulting type was '{1}', with base type '{2}'. I expected a base type with a single generic argument; I don't know how to handle this type.", view.ViewPath, compiledViewType, baseType));
            }

            // This will be the value of @model
            var modelType = baseType.GetGenericArguments()[0];
            if (modelType == typeof(object))
            {
                // When no @model is set, the result is a ViewPage<object>
                throw new Exception(string.Format("The view '{0}' needs to include the @model directive to specify the model type. Did you forget to include an @model line?", view.ViewPath));                    
            }

            var model = modelSelector.GetModel(modelType);

            // Switch the current model from the ModelSelector to the value of @model
            viewContext.ViewData.Model = model;

            view.Render(viewContext, writer);
        }
    }
}

该视图引擎是通过将在此注册的Global.asax.cs:

The view engine is registered by putting this in Global.asax.cs:

ViewEngines.Engines.Clear();
ViewEngines.Engines.Add(new ModelSelectorEnabledRazorViewEngine());

渲染

我的主页视图包括以下行来测试这一切了:

Rendering

My home view includes the following lines to test it all out:

@Html.Action("Show", "Widget", new { widgetName = "Clock" })
@Html.Action("Show", "Widget", new { widgetName = "News" })

这篇关于从内部MVC控制器确定的局部视图模型的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆