ASP.net mvc EntityCollection is null when saving - asp.net-mvc

I've been trying to figure this out for over a day now but I just can't get this to work.
I have an asp.net MVC website which uses the entityframework for its datamodel.
I need to be able to edit a complex Release entity which contains a List<ReleaseDescription>
I have the following model (Apparantly I cannot upload pictures so I'll just type it out):
public class Release
{
public string Version
..some other primitive properties
public EntityCollection<ReleaseDescription>
}
public class ReleaseDescription
{
public string Description
public Language Language
}
public class Language
{
public string ISOCode
public string Description
}
When looking for a solution for this problem on the web. I found out that using an EntityCollection (see list Release.ReleaseDescription) is not a good idea so in the partial class Release I made an extra property ReleaseDescriptionList which transforms this entityCollection into a List<ReleaseDescription> through the getter, it has no setter.
The problem is that, when saving, my release.ReleaseDescription or even the release.ReleaseDescriptionList is always empty when items should be in it.
Here follows the rest of my code:
My Edit.aspx code looks like this
<%# Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<Server.DM.Release>" %>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<h2>Edit</h2>
<% using (Html.BeginForm()) {%>
<%: Html.ValidationSummary(true) %>
... (Code for the primitive properties (works all fine)
<fieldset>
<legend>Descriptions</legend>
<% for(var i =0; i<Model.ListReleaseDescriptions.Count; i++)
{%>
<%: Html.EditorFor(x => Model.ListReleaseDescriptions[i], "ReleaseDescriptionRow")%>
<%} %>
<%= Html.ActionLink("Add another...", "AddDescription", Model) %>
</fieldset>
<p>
<input type="submit" value="Save" />
</p>
<% } %>
<div>
<%: Html.ActionLink("Back to List", "Index") %>
</div>
</asp:Content>
The asp code in the DescriptionRelease looks like this:
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<Server.DM.ReleaseDescription>" %>Language: <%= Html.TextBoxFor(x => x.Language.ISOCode) %>Qty: <%=Html.TextBoxFor(x => x.Description)%>
(I couldn't get above codeblock on multiple lines sorry)
When I click the save button in the edit screen and I get to my ActionController
[HttpPost]
public ActionResult Edit(Release release)
release.DescriptionRelease does not contain any data when 3 items should be in it.
Any help is appreciated in resolving this issue!
(ps: yes I know there are similar threads on this forum and others but none of it seems to work for me.)

OK, After a lot of searching I finally found an answer for my problem.
I already made an extra property to make up for the EntityCollection but it looked like this:
public IList<ReleaseDescription> ListReleaseDescription
{
get
{
return ReleaseDescription.ToList();
}
}
That didn't work so I made a simple property out of it like so:
public IList<ReleaseDescription> ListReleaseDescription{get; set;}
and I populated this property in my controller. This finally fixed my problem and I got all the data back when saving! I can't believe I wasted 1,5 days on this when the solution was this simple.

Related

foreach GetEnumerator error when converting to Razor syntax

I'm currently in the process of converting the MVC 2 portion of a hybrid Web Forms/MVC 2 application to MVC 3 (I'm not familiar with Web Forms or MVC 2) and have been encountering some problems with some of the foreach statements (which work fine in the original hybrid application).
When converting to Razor, some (not all) of my foreach statements will say something along the lines of:
foreach statement cannot operate on variables of type
'System.Data.Objects.DataClasses.EntityCollection<Project.Activity.PortalMember>'
because
'System.Data.Objects.DataClasses.EntityCollection<Project.Activity.PortalMember>'
does not contain a public definition for 'GetEnumerator'
This is an example .ascx from the Web Forms/MVC 2 application where the foreach works fine:
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<Project.Activity.PortalGroup>" %>
<% if (Model.PortalMembers.Count() > 0) { %>
<div>
<% foreach (var item in Model.PortalMembers) { %>
<div>
<%: item.SubAccount.FirstName %> <%: item.SubAccount.LastName %>
</div>
<%} %>
</div>
<%} %>
This is my attempt for the .cshtml conversion that is giving me the error:
#model Project.Activity.PortalGroup
#if (Model.PortalMembers.Count() > 0)
{
<div>
#foreach (var item in Model.PortalMembers)
{
<div>
#item.SubAccount.FirstName #item.SubAccount.LastName
</div>
}
</div>
}
Try add reference to System.Data.Linq.dll in your project
Try to add reference to System.Data.Entity.

Make a ASP.Net MVC Site, filtering the content

I recently started an MVC project to query and to report the company's users, everything seemed to be fine, except when I made a finduser form, I got all stuck!
you see I want the operator to be able to find the appropriate user by entering either hos pin,serial or calling number but the action links I make for the search operation all fail because they are made at the form_load time and so the empty string of text boxes get injected to them.
So my requests are:
How to make this filters work.
Now a little Ajax textbox suggester on user pin or serial would be great, please gimme a hint or two.
<table>
<tr>
<td>
PIN:</td>
<td>
<asp:TextBox ID="txt_pin" runat="server"></asp:TextBox>
</td>
</tr>
<tr>
<td>
Serial Number:</td>
<td>
<asp:TextBox ID="txt_sn" runat="server"></asp:TextBox>
</td>
</tr>
<tr>
<td>
CallingNumber:</td>
<td>
<asp:TextBox ID="txt_callingNo" runat="server"></asp:TextBox>
</td>
</tr>
<tr>
<td>
CalledThisNumberToday:</td>
<td>
<asp:TextBox ID="txt_calledNo" runat="server"></asp:TextBox>
</td>
</tr>
<tr>
<td>
</td>
<td>
</td>
</tr>
<tr>
<td>
</td>
<td>
</td>
</tr>
</table>
<%:Html.ActionLink("Search for user", "Details", new { pin = txt_pin.Text })%>
<asp:TextBox>? In an ASP.NET MVC application? I am afraid you got it all wrong.
I would recommend you going through the getting started tutorials here in order to learn the basic concepts of ASP.NET MVC: http://asp.net/mvc
In ASP.NET MVC you use models and in the views you use helpers in order to generate input fields.
So in your case you could design a view model:
public class SearchViewModel
{
public string Pin { get; set; }
public string SerialNumber { get; set; }
public string CallingNumber { get; set; }
public string CalledThisNumberToday { get; set; }
}
then you could design a controller action which will pass this view model to the view for rendering the search form:
public ActionResult Index()
{
var model = new SearchViewModel();
return View(model);
}
and finally in your strongly typed view you would use HTML helpers:
<%# Page
Language="C#" MasterPageFile="~/Views/Shared/Site.Master"
Inherits="System.Web.Mvc.ViewPage<AppName.Models.SearchViewModel>" %>
<asp:Content ID="MainContent" ContentPlaceHolderID="MainContent" runat="server">
<%= Html.ValidationSummary() %>
<% using (Html.BeginForm()) { %>
<div>
<%= Html.LabelFor(x => x.Pin) %>
<%= Html.EditorFor(x => x.Pin) %>
</div>
<div>
<%= Html.LabelFor(x => x.SerialNumber) %>
<%= Html.EditorFor(x => x.SerialNumber) %>
</div>
<div>
<%= Html.LabelFor(x => x.CallingNumber) %>
<%= Html.EditorFor(x => x.CallingNumber) %>
</div>
<div>
<%= Html.LabelFor(x => x.CalledThisNumberToday) %>
<%= Html.EditorFor(x => x.CalledThisNumberToday) %>
</div>
<p><input type="submit" value="Search for user" /></p>
<% } %>
</asp:Content>
and the final step would be to implement the controller action that will perform the search and to which this form will be subimtted:
[HttpPost]
public ActionResult Index(SearchViewModel model)
{
if (!ModelState.IsValid)
{
// the model was not valid => redisplay the form
// so that the user can fix his errors
return View(model);
}
// TODO: perform the search
...
}
Along with Darin's excellent answer (which you really need to follow in order to do things the "MVC" way...), you can also do AJAX filtering on a table using jQuery. There's even a plugin for jQuery that makes live table filtering as simple as adding a script reference and a textbox, then a couple lines of Javascript code to wire up the search. This will end up looking something like this:
<script language="javascript" type="text/javascript" src="/Scripts/jquery.js"></script>
<script language="javascript" type="text/javascript" src="/Scripts/jquery.uitablefilter.js"></script>
<script language="javascript" type="text/javascript">
$(document).ready(function () {
$("#myFilter").keyup(function () {
$.uiTableFilter($("#myTable"), $(this).val());
});
});
</script>
<input type="text" id="myFilter" />
<table id="myTable">
...
</table>
As you start typing in the filter box, the table will be automatically filtered to only show rows that contain the specified value.
As for auto-complete suggestions, you might want to look into jQUery UI - they've got
support for auto-complete using several different mechanisms to provide the hint values.

Example of using HTML forms with Sparkview engine on ASP.NET MVC2

I am playing with Sparkview 1.0 on ASP.NET 4.0 with MVC2. Trying to create a simple HTML form.
When the form loads, it renders as expected. Click Save button on the form, the model validates, returns an error about length of field (expected) but then the !{ Model.Name } tag gets rendered as the text ${ Model.Name } rather than the actual expected output of "test blah blah".
Where can I find an example of creating HTML forms using the HTML helpers with Sparkview?
!{ Model.Name }
${ Html.ValidationSummary("Edit was unsuccessful. Please correct the errors and try again.") }
# Html.BeginForm();
<fieldset>
<legend>Fields</legend>
<p>
<label for="Name">Name:</label>
!{ Html.TextBox("Name", Model.Name) }
</p>
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
# Html.EndForm();
And here is the controller method:
[HttpPost]
public ActionResult Add(Project project)
{
if(ModelState.IsValid)
{
// save to db
Response.Redirect("/");
}
ViewData["Model"] = project;
return View();
}
Model is a special name in Spark, it represents a strongly typed model for your view.
Therefore, you can either change the name of your dictionary key to something other than Model (call it ViewData["SuperModel"]:) or simply return the strongly typed viewresult overload of the view method.

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?

How to make update panel in ASP.NET MVC

How do I make an update panel in the ASP.NET Model-View-Contoller (MVC) framework?
You could use a partial view in ASP.NET MVC to get similar behavior. The partial view can still build the HTML on the server, and you just need to plug the HTML into the proper location (in fact, the MVC Ajax helpers can set this up for you if you are willing to include the MSFT Ajax libraries).
In the main view you could use the Ajax.Begin form to setup the asynch request.
<% using (Ajax.BeginForm("Index", "Movie",
new AjaxOptions {
OnFailure="searchFailed",
HttpMethod="GET",
UpdateTargetId="movieTable",
}))
{ %>
<input id="searchBox" type="text" name="query" />
<input type="submit" value="Search" />
<% } %>
<div id="movieTable">
<% Html.RenderPartial("_MovieTable", Model); %>
</div>
A partial view encapsulates the section of the page you want to update.
<%# Control Language="C#" Inherits="ViewUserControl<IEnumerable<Movie>>" %>
<table>
<tr>
<th>
Title
</th>
<th>
ReleaseDate
</th>
</tr>
<% foreach (var item in Model)
{ %>
<tr>
<td>
<%= Html.Encode(item.Title) %>
</td>
<td>
<%= Html.Encode(item.ReleaseDate.Year) %>
</td>
</tr>
<% } %>
</table>
Then setup your controller action to handle both cases. A partial view result works well with the asych request.
public ActionResult Index(string query)
{
var movies = ...
if (Request.IsAjaxRequest())
{
return PartialView("_MovieTable", movies);
}
return View("Index", movies);
}
Basically the 'traditional' server-controls (including the ASP.NET AJAX ones) won't work out-of-the-box with MVC... the page lifecycle is pretty different. With MVC you are rendering your Html stream much more directly than the abstracted/pseudo-stateful box that WebForms wraps you up in.
To 'simulate' an UpdatePanel in MVC, you might consider populating a <DIV> using jQuery to achieve a similar result. A really simple, read-only example is on this 'search' page
The HTML is simple:
<input name="query" id="query" value="dollar" />
<input type="button" onclick="search();" value="search" />
The data for the 'panel' is in JSON format - MVC can do this automagically see NerdDinner SearchController.cs
public ActionResult SearchByLocation(float latitude, float longitude) {
// code removed for clarity ...
return Json(jsonDinners.ToList());
}
and the jQuery/javascript is too
<script type="text/javascript" src="javascript/jquery-1.3.2.min.js"></script>
<script type="text/javascript">
// bit of jquery help
// http://shashankshetty.wordpress.com/2009/03/04/using-jsonresult-with-jquery-in-aspnet-mvc/
function search()
{
var q = $('#query').attr("value")
$('#results').html(""); // clear previous
var u = "http://"+location.host+"/SearchJson.aspx?searchfor=" + q;
$("#contentLoading").css('visibility',''); // from tinisles.blogspot.com
$.getJSON(u,
function(data){
$.each(data, function(i,result){
$("<div/>").html(''+result.name +''
+'<br />'+ result.description
+'<br /><span class="little">'+ result.url +' - '
+ result.size +' bytes - '
+ result.date +'</span>').appendTo("#results");
});
$("#contentLoading").css('visibility','hidden');
});
}
</script>
Of course UpdatePanel can be used in much more complex scenarios than this (it can contain INPUTS, supports ViewState and triggers across different panels and other controls). If you need that sort of complexity in your MVC app, I'm afraid you might be up for some custom development...
If you are new to asp.mvc I recommend you to download the NerdDinner (source) sample application. You will find in there enough information to start working effectively with mvc. They also have ajax examples. You will find out you don't need and update panel.

Resources