I have a View with a Master Page. The user control makes use of a Model:
<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<BudgieMoneySite.Models.SiteUserLoginModel>" %>
This user control is shown on all screens (Part of the Master Page). If the user is logged in, it shows a certain text, and if the user isn't logged in, it offers a login box.
That is working OK.
Now, I am adding my first functional screen. So I created a new view... and, well, i generated the basic view code for me when I selected the controller method, and said 'Create View'.
My Controller has this code:
public ActionResult Transactions()
{
List<AccountTransactionDetails> trans = GetTransactions();
return View(trans);
}
private List<AccountTransactionDetails> GetTransactions()
{
List<AccountTransactionDto> trans = Services.TransactionServices.GetTransactions();
List<AccountTransactionDetails> reply = new List<AccountTransactionDetails>();
foreach(var t in trans)
{
AccountTransactionDetails a = new AccountTransactionDetai开发者_开发百科ls();
foreach (var line in a.Transactions)
{
AccountTransactionLine l = new AccountTransactionLine();
l.Amount = line.Amount;
l.SubCategory = line.SubCategory;
l.SubCategoryId = line.SubCategoryId;
a.Transactions.Add(l);
}
reply.Add(a);
}
return reply;
}
So, my view was generated with this:
<%@ Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<System.Collections.Generic.List<BudgieMoneySite.Models.AccountTransactionDetails>>" %>
Found <%=Model.Count() %> Transactions.
All I want to show for now is the number of records I will be displaying.
When I run it, I get an error:
"The model item passed into the dictionary is of type 'System.Collections.Generic.List`1[BudgieMoneySite.Models.AccountTransactionDetails]', but this dictionary requires a model item of type 'BudgieMoneySite.Models.SiteUserLoginModel'."
It looks like the user control is being rendered first, and as the Model from the controller is my List<>, it's breaking!
What am I doing wrong?
LoginModel should not be set as the model for the page but for the partial view only. You can use RenderAction to display a partial view with a model that is not connected to the model for the page. Here's how I have implemented a login status control that appears on the master page.
The partial view (Views/Auth/LoginStatus.ascx):
<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<IUserProfile>" %>
<% if (Request.IsAuthenticated && Model != null) { %>
Welcome <b><%= Model.DisplayName %></b>!
[ <%= Html.ActionLink("Log Off", "LogOff", "Auth") %> ]
<%
}
else {
%>
[ <%= Html.ActionLink("Log On", "Login", "Auth") %> ]
<%
}
%>
In the master page, which does not have a strongly typed model:
<% Html.RenderAction("LoginStatus", "Auth"); %>
And in the controller (AuthController)
public ActionResult LoginStatus()
{
TProfile p = null;
if (User.Identity.IsAuthenticated)
{
p = Data.Profiles.Get(ulong.Parse(User.Identity.Name));
}
return View(p);
}
It's not that it's rendered "first" per se, it's more that all the views take the Model that the controller passes down, unless otherwise specified.
To solve this, you can either create a base class that all your view models will inherit from, defining things that the Master and its partial views need (and then you'd pass the appropriate property into one of the RenderPartial overloads), or you can use the untyped ViewData bag to do something similar.
* EDIT * So I realized that I was answering your question as-asked, not as I would have solved it. The above response is true, if you want to handle model stuff in your master page you need to either stuff them into the ViewData dictionary (e.g. ViewData["myData"] = mydata;) Or you have to create a base class for all your models to inherit from.
That said, in most cases (esp. something like profiel that you gave as an example) I don't do either. In most cases like this, I use a render action instead, which calls for a partial view from another controller. That other controller then can feed its partial whatever data is appropriate. Some folks consider this a violation of MVC, I tihnk it's a good way to compose views and actually keep your logical structures intact, rather than muddying the waters by requiring a controller to know what the master page needs even when it's got nothing to do w/ that controller's job. The usual solution to that is to also have a base controller, or else use action filters, but I find that to be just more bloat that you have to maintain.
精彩评论