开发者

MVC - Multiple Strong Type object ...inside one view

After so many years using ASP.Net, I’m still trying to figure out how to achieve the same results using MVC.

I have a materpage with a control that is strongly type to something. When I navigate to a view of a different strongly type model ...and click on the button to execute something, I get "The model item passed into the dictionary is of type Site.Models.RegisterModel', but this dictionary requires a model item of type Site.Models.LogOnModel'".

For the sake of this example, we can take the Default MVC app that is provided with VS 2010, let’s imagine I want to change the “LogonUserControl.ascx” so that it either tells me the logged user (as it works currently) OR allow me to login from there, showing me the text boxes for username and password (therefore in this case from the home page).

So I take the control and strongly type it as:

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<Gioby.Models.LogOnModel>" %>
<%
    if (Request.IsAuthenticated) {
%>
        Welcome <b><%: Page.User.Identity.Name  %></b>
        [ <%: Html.ActionLink("Log Off", "LogOff", "Account")%> ]
<%
    }
    else {
%> 
    <% using (Html.BeginForm()) { %>
        <div id="logon">
                <div class="editor-label">
                    <%: Html.LabelFor(m => m.UserName)%>
                    <%: Html.TextBoxFor(m => m.UserName)%>
                    <%: Html.ValidationMessageFor(m => m.UserName, "*") %>
                    <%: Html.LabelFor(m => m.Password)%>
                    <%: Html.PasswordFor(m => m.Password)%>
                    <%: Html.ValidationMessageFor(m => m.Password, "*") %>
                    <input type="submit" value="Log On" />
                </div>

                <div class="editor-label">
                    <%: Html.ActionLink("Register here", "Register", "Account")%> 
                    <%: Html.CheckBoxFor(m => m.RememberMe, new { @class = "pad-left" })%>
                    <%: Html.LabelFor(m => m.RememberMe) %>
                </div>
        </div>
    <% } %>
<%
    }
%>

Then 开发者_高级运维on the HomeController, I add a procedure as:

 [HttpPost]
 public ActionResult Index(LogOnModel model, string returnUrl)
 {
     if (ModelState.IsValid)
     {
          // ==>> Check Login against your DB

          // Now check if param returnUrl is empty
          if (!String.IsNullOrEmpty(returnUrl))
              return Redirect(returnUrl);

          return RedirectToAction("Index", "Home");
     }

     // If we got this far, something failed, redisplay form
     return View(model);
 }

I tested it from the home page … it works !!!

BUT when I navigate to the “Register” view (remember that the “LogonUserControl.ascx” is located inside the “MasterPage”, therefore visible from the Register view).

So when I click on the Register button, I get the error: The model item passed into the dictionary is of type Site.Models.RegisterModel', but this dictionary requires a model item of type Site.Models.LogOnModel'.

QUESTION: Does that mean that I will never be able to different pieces together into one view?

Let’s say I want to write an eCommerce site and on the home page I want to see “Most used Tags”, “Most bought products”, “Product of the Month”, “List of Categories” …all within the same view and each one with his own HTTP POST action.

If this is possible using MVC?


If I'm understanding the problem correctly, you have two Views that use the same MasterPage, but which are strongly typed against different ViewModels. The master page is able to include a Partial View that is also strongly typed, as long as its expected ViewModel is the same as that of the parent view. However, if you're using a view with a different ViewModel type, it doesn't know what to do.

Consider the following:

<% Html.RenderPartial("LogOn") %>

The above code implicitly includes the model data for the current view being rendered. It's exactly the same as if you had said:

<% Html.RenderPartial("LogOn", Model) %>

So this will only work if Model is a LogOnModel. Remember that the MasterPage is really a part of whatever View inherits it, so even if you're putting this in the MasterPage, it's as if you'd put the same code in every view that inherits it. So if your View's Model is not the same as the PartialView's Model, this won't work. One alternative is to use inheritance to ensure that every ViewModel will include all the information required by the Master Page. This approach is described in detail here.

But that approach means that you have to always use a factory to produce your view model, and every view model has to be somewhat aware of which master page it will use. In our product, we can use a different master page on the same view depending on what mode the user is viewing the site in, so it doesn't make sense to tie the ViewModel to that of the Master Page. We accomplish what you're describing using the RenderAction method, which allows you to render an entire controller action as if it were just a part of the larger view. Some of the advantages of this approach are discussed here.

So now you can have your MasterPage include whatever little partial views you want, but you separate the logic for building the ViewModel of each of these Views into an individual controller action that's responsible for that particular Partial View:

<% Html.RenderAction("LogOnBox") %>

The Action:

public ActionResult LogOnBox()
{
    LogOnModel model = GetLogOnModel();
    return PartialView("LogOnUserControl", model);
}

Now, regardless of what model your current view uses, your Master Page can include “Most used Tags”, “Most bought products”, “Product of the Month”, “List of Categories”, etc. Better still, these portions of the page can leverage output caching so they don't have to be regenerated with every page load if they don't change very often.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜