Subsonic 5 Minute Demo newbie problems - asp.net-mvc

I have looked at the "Subsonic 5 Minute Demo", and got my data objects generated from the database after some initial problems. I am using Visual Web Developer Express 2010 and my project is, as in the demo, an ASP.NET MVC 2 Web Application.
So, I've written this code in my HomeController:
public ActionResult Index()
{
ViewData["Message"] = "Welcome to Food Recipe Web!";
//lets get some data
var db = new FoodRecipeWeb.Data.FoodRecipeWebDB();
var recipes = from r in db.Recipes
select r;
return View(recipes);
}
My View looks like this:
<%# Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage" %>
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
Home Page
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<h2><%: ViewData["Message"] %></h2>
<ul>
<%foreach (var recipe in Model)
{ %>
<li><%= recipe.Name %></li>
<% } %>
</ul>
</asp:Content>
This is quite like the demo, but when I try to look at the page, I get this message:
Compiler Error Message: CS1579: foreach statement cannot operate on variables of type 'object' because 'object' does not contain a public definition for 'GetEnumerator'
Source Error:
Line 6: <h2><%: ViewData["Message"] %></h2>
Line 7: <ul>
Line 8: <%foreach (var recipe in Model)
Line 9: { %>
Line 10:
So what am I doing wrong here?

Your View needs to know about the Model.
Change it from:
Inherits="System.Web.Mvc.ViewPage"
Inherits="System.Web.Mvc.ViewPage<IEnumerable<Recipe>>"

Related

ASP.NET MVC - MasterPage cannot be applied to this page because the control collection is read-only. If the page contains code blocks

This error message doesn't make a lot of sense in the context of MVC:
MasterPage cannot be applied to this page because the control collection is read-only. If the page contains code blocks, make sure they are placed inside content controls (i.e. <asp:Content runat=server />)
What's it mean?
My view looks something like this:
<%# Page Inherits="System.Web.Mvc.ViewPage<Site.Controllers.ProductCategoryController.ProductListViewModel>" MasterPageFile="~/site.master" %>
<% foreach (var prod in Model.Products) { %>
<li><%=prod.Description%></li>
<% } %>
I figured it out, it is quite a simple mistake. The problem was my view code wasn't wrapped in a content control (which maps to a contentplaceholder in the master page).
Of course my view should be like this:
<%# Page Inherits="System.Web.Mvc.ViewPage<Site.Controllers.ProductCategoryController.ProductListViewModel>" MasterPageFile="~/site.master" %>
<asp:Content runat="server" ID="Content1" ContentPlaceHolderID="BodyContent">
<% foreach (var prod in Model.Products) { %>
<li><%=prod.Description%></li>
<% } %>
</asp:Content>
Pretty obvious but I just couldn't see it!
And the error message didn't really help me.

Why did my custom HtmlHelpers stop working after I upgraded to MVC3?

I just finished going through this checklist to upgrade my asp.net mvc2 site to mvc3: http://www.asp.net/learn/whitepapers/mvc3-release-notes#upgrading
Everything is compiling, but when I run the application I get errors in the views.
Example View:
<%# Page Title="" Language="C#" MasterPageFile="~/Views/Shared/site.Master" Inherits="System.Web.Mvc.ViewPage<Genesis.Domain.Entities.StreamEntry>" %>
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
<%: Model.seTitle %>
</asp:Content>
<asp:Content ID="Content3" ContentPlaceHolderID="MetaContent" runat="server">
<%: Html.GetMetaTag("description", Model.seDescription )%>
<%: Html.GetMetaTag("keywords", Model.seKeywords )%>
</asp:Content>
<asp:Content ID="Content4" ContentPlaceHolderID="HeadlineContent" runat="server">
<%: Model.seHeadline %>
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<p>
<%//: Html.AddThis() %>
</p>
<p>
Created: <br /><strong><%: Model.seDateCreated %></strong><br />
Last Modified: <br /><strong><%: Model.seDateModified %></strong>
</p>
<%: MvcHtmlString.Create(Model.seBody) %>
<% Html.RenderAction("Comments", "Comments", new { streamEntryID = Model.seID, allowComments = Model.AllowComments }); %>
</asp:Content>
Error text:
Compilation Error
Description: An error occurred during the compilation of a resource required to service this request. Please review the following specific error details and modify your source code appropriately.
Compiler Error Message: CS1061: 'System.Web.Mvc.HtmlHelper<Genesis.Domain.Entities.StreamEntry>' does not contain a definition for 'GetMetaTag' and no extension method 'GetMetaTag' accepting a first argument of type 'System.Web.Mvc.HtmlHelper<Genesis.Domain.Entities.StreamEntry>' could be found (are you missing a using directive or an assembly reference?)
Source Error:
Line 6:
Line 7: <asp:Content ID="Content3" ContentPlaceHolderID="MetaContent" runat="server">
Line 8: <%: Html.GetMetaTag("description", Model.seDescription )%>
Line 9: <%: Html.GetMetaTag("keywords", Model.seKeywords )%>
Line 10: </asp:Content>
How do I get my views to recognize my custom html helpers again?
Try adding an Import directive on the top of the view to see if it makes any difference:
<%# Import Namespace="Namespace.Of.The.Class.Containing.The.Helper" %>
Also you could add this namespace to the <namespaces> section of web.config.

Client side validation script is not generated for partial views fetched using AJAX

I am trying to setup client side validation using MicrosoftMvcJQueryValidation
to work with ajax submitted forms.
It works perfectly fine if the partial view is rendered directly from a view.
However when I try to fetch it over XHR, for example to show it in a JQuery Dialog,
client validation javascript is not generated for the output html.
Any ideas?
Working code - partial view is rendered using Html.RenderPartial:
View:
<%# Page Title="" Language="C#" Inherits="System.Web.Mvc.ViewPage<dynamic>" %>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<% Html.RenderPartial("New"); %>
</asp:Content>
Partial View:
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<Product>" %>
<% Html.EnableClientValidation();%>
<% Html.BeginForm();%>
<%= Html.EditField(m => m.price)%>
<%= Html.ValidationMessageFor(m => m.price)%>
<% Html.EndForm();%>
Not working code - partial view is fetched with JQuery load() function.
View:
<%# Page Title="" Language="C#" Inherits="System.Web.Mvc.ViewPage<dynamic>" %>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
....
$("#dialog").load('~/Product/New/');
$("#dialog").dialog("open");
....
<div id="dialog" title=""></div>
</asp:Content>
Relevant controller action:
public PartialViewResult New(int id)
{
return PartialView(service.GetProduct());
}
Thanks.
This will happen for all partial views which return JavaScript inserted into the DOM via AJAX. The problem is that the JavaScript returned with the partial view is not executed/interpreted when it is inserted into the document.
Unfortunately, because what you are dealing with is generated JavaScript I can explain why you are experiencing the problem but I'm at a loss for a solution. Were this a function you wrote I'd suggest adding it to the OnComplete. All I can offer is that this is a JavaScript limitation and to start looking there.
BTW: this looks promising http://adammcraventech.wordpress.com/2010/06/11/asp-net-mvc2-ajax-executing-dynamically-loaded-javascript/
After reading the blog ARM has mentioned it seems the solution really depends
on how one is loading the partial view.
What eventually worked for me is to call __MVC_EnableClientValidation
after view has been assigned to an element:
$.get('~/Product/New/', function(data) {
$("#dialog").html(data);
// extracted from MicrosoftMvcJQueryValidation.js
var allFormOptions = window.mvcClientValidationMetadata;
if (allFormOptions) {
while (allFormOptions.length > 0) {
var thisFormOptions = allFormOptions.pop();
__MVC_EnableClientValidation(thisFormOptions);
}
}
},'html');

ASP.NET MVC Using Multiple user controls on a single .aspx(view)

I am getting this following error , when i am tring to having two user controls in one page.
The model item passed into the dictionary is of type 'System.Linq.EnumerableQuery1[Data.EventLog]' but this dictionary requires a model item of type 'System.Collections.Generic.IEnumerable1[Data.Notes]'.
<%# Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage" %>
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
Test
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<h2>Test</h2>
<% Html.RenderPartial("~/Views/Shared/UserControl/Tracking.ascx"); %>
<% Html.RenderPartial("~/Views/Shared/UserControl/Notes.ascx"); %>
</asp:Content>
Your Notes.ascx control is strongly-typed, but you are not passing a model in your call to RenderPartial. Hence, the model for the page is passed instead, which is the wrong type for the user control. You need to pass a model in your call to RenderPartial.
For example, if the event log page model has a property called NotesModel:
<%# Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<MyNamespace.EventLogModel>" %>
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
Test
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<h2>Test</h2>
<% Html.RenderPartial("~/Views/Shared/UserControl/Tracking.ascx"); %>
<% Html.RenderPartial("~/Views/Shared/UserControl/Notes.ascx", Model.NotesModel); %>
</asp:Content>
Note that I've:
Changed the Inherits bit of the #Page directive to specify the model type and,
Added the model argument to RenderPartial.
You would of course need to populate Model.NotesModel in your controller. If it's null you'll see the same bug you presently see.

SelectList, ASP.NET MVC

I'm working with a SelectList, and populating it with the data from a table. I'm trying to bind it to the ID of another object.
EDIT
Updated the Schema to Reflect something I neglected. I have updated it to show the exact names of each item. I think the problem comes in the fact that each Unit has a sheet, and each sheet has a product. (The sheet will hold more information for the product, but a Unit will have a great deal of other information, so I wanted to separate them in order to keep it clear what was what.)
I think something is happening and the "Sheet" isn't being initialized as an object, and when the binding occurs, it doesn't have an object to bind to, since it is one-level deep. Tell me if this makes sense, or if I am just completely off base.
**Unit**
UnitID (PK)
**ProductSheet**
UnitId (FK)(PK)
ItemId (FK)
**Items**
ItemId (PK)
ItemTitle
It just ...isn't working though. I have this code.
DatabaseDataContext db = new DatabaseDataContext();
Unit unit = new Unit();
ViewData["Items"] = new SelectList( db.Items, "Id", "ItemTitle", unit.ProductSheet.ItemId);
But in the postback, the selectList is always null and empty! This is the View code. I'm really getting lost here, I've followed a lot of examples and it still comes up with bunk.
<%= Html.DropDownList("Items") %>
Your view code should read:
<% var selectItems = ViewData["Items"] as SelectList; %>
<%= Html.DropDownList("ProductSheet.ItemId", selectItems) %>
An entire sample of the project is available at this url
These are the action methods.
public ActionResult Create()
{
DatabaseDataContext database = new DatabaseDataContext();
Unit u = new Unit();
u.Sheet = new Sheet();
ViewData["ProductListing"] = new SelectList(database.Products, "ProductId", "ProductName", u.Sheet.ProductId);
return View();
}
//
// POST: /Home/Create
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create(Unit u)
{
try
{
DatabaseDataContext database = new DatabaseDataContext();
database.Units.InsertOnSubmit(u);
database.SubmitChanges();
// TODO: Add insert logic here
return RedirectToAction("Index");
}
catch
{
return View();
}
}
Here is the Create View.
<%# Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<MVC.Models.Contexts.Unit>" %>
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
Create
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<h2>Create</h2>
<%= Html.ValidationSummary("Create was unsuccessful. Please correct the errors and try again.") %>
<% using (Html.BeginForm()) {%>
<fieldset>
<legend>Fields</legend>
<%= Html.DropDownList("ProductListing") %>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
<% } %>
<div>
<%=Html.ActionLink("Back to List", "Index") %>
</div>
</asp:Content>
<asp:Content ID="Content3" ContentPlaceHolderID="ScriptContent" runat="server">
</asp:Content>
I've marked the answer that seems to have made some progress now, I think I'm starting to understand it better.
When I passed the 'path' of the object to assign as a string in the name field of DropDownList, it seems to work. But this doesn't really make a lot of sense to me. Can you explain what is going on any better?

Resources