I have this classes:
public class GroupMetadata
{
[HiddenInput(DisplayValue = false)]
public int Id { get; set; }
[Required]
public string Name { get; set; }
}
[MetadataType(typeof(GrupoMetadata))]
public partial class Group
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
}
And this action:
[HttpPost]
public ActionResult Edit(Group group)
{
if (ModelState.IsValid)
{
// Logic to save
return RedirectToAction("Index");
}
return View(group);
}
That's my view:
<%# Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<Group>" %>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<% using (Html.BeginForm()) {%>
<fieldset>
<%= Html.EditorForModel() %>
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
<% } %>
<div>
<%=Html.ActionLink("Back", "Index") %>
</div>
</asp:Content>
But ModelState is always invalid! As I can see, for MVC validation 0 is invalid, but for me is valid.
How can I fix it since, I didn't put any kind of validation in Id property?
UPDATE:
I don't know how or why, but renaming Id, in my case to PK, solves this problem.
Do you know if this an issue in my logic/configuration or is an bug or expected behavior?
Just before if (ModelState.IsValid) remove Id index using this line ModelState.Remove("Id") this way when MVC team remove this bug, you just need to remove this line code of your projects.
You have a required field, Id. It's required because it's non-nullable. You must either (1) submit it with your form or (2) change your model to make Id nullable or (3) use a different type.
I can confirm that removing the id index using ModelState.Remove("Id") works.
Could anyone give an elaborate explanation for this alleged "bug"?
Maybe someone from the Microsoft MVC Team could give an explanation?
There are no bugs like this when using the ADO.NET Entity Data Model as a data source - only on Linq To SQL.
Your problem is that you are using a property named 'id' in your model. Try using a different name and you will see it works. Mvc seems to have a problem with that.
I experienced the exact same issue.
One other reason can be:
If you declare your key as some type other than a number e.g String, then since by default it can't auto generate the keys for you, you'll face this error.
Related
I work on an asp.net mvc project with Breeze.
I have a page where I display details about a transport and list linked transports in a table below it.
Consider the following entities:
public class Transport
{
[Key]
public int Id { get; set; }
public string TransportNumber { get; set; }
public string Description { get; set; }
public virtual List<LinkedTransport> LinkedTransports { get; set; }
...
}
public class LinkedTransport
{
[Key, Column(Order = 0)]
public int TransportId { get; set; }
[Key, Column(Order = 1)]
public int TransportRelatedId { get; set; }
public virtual Transport Transport { get; set; }
public virtual Transport TransportRelated { get; set; }
}
So these 2 entities allows me to define my transports and (for each) linked transports.
First, I load my (main) transport into an observable named transport:
var query = entityQuery.from('Transports')
.where('id', '==', transportId)
.expand("LinkedTransports.transportRelated");
query = query.using(breeze.FetchStrategy.FromServer);
Please pay attention to the expand where I retrieve also the linked transports.
Now I have the following code for showing my transport and linked transports:
<div data-bind="with: transport()">
<input type="text" data-bind="text: transportNumber"></input>
<input type="text" data-bind="value: description"></input>
...
<table data-bind="foreach: linkedTransports">
<tr>
<td data-bind="text: transportRelated().transportNumber()"></td>
<td data-bind="text: transportRelated().description()"></td>
</tr>
</table>
</div>
So far, so good. I can display my main transport informations and also linked transports informations. Now I need to let the user add some linked transports. So I have the following code:
var createLinkedTransport = function (transpId, transpRelatedId) {
var initialValues = ({
transportId: transpId,
transportRelatedId: transpRelatedId
});
manager.createEntity(entityNames.linkedTransport, initialValues);
};
At runtime, when I call this function, I can add a linked transport to my (main) transport.
My problem: the added element is not correctly showed in my foreach knockout loop. I see that a new row is added to the table but this one is empty. I think the problem is because I don't expand any information on my newly added element but I don't know how to proceed.
Any idea how to proceed?
UPDATE
To be clear, when I add a linkedTransport to my transport, immediately after the add (and without reloading the page) I cannot read properties of the targetted (*) linkedTransport. BUT if I saveChanges AND reload my page, THEN I can read properties of the targetted linkedTransport.
When I say 'the targetted linkedTransport' I mean the transport which is referenced by my TransportRelatedId (>> public virtual Transport TransportRelated).
So If I add the linkedTransport #123 to the transport #456 THEN immediately (without reloading the page) I cannot display the Description property of the #123.
<div data-bind="with: transport()">
...
<div data-bind="foreach: linkedTransports">
<label data-bind="text: transportRelated().description()"></label>
</div>
</div>
Hope I'm clear.
UPDATE 2
As suggested by Ward, I set a breakpoint immediately after adding the linkedTransports and check the transport().linkedTransports(). Below is the results:
transport().linkedTransports()[0].transport() >> the properties are there
transport().linkedTransports()[0].transportId() >> 1
transport().linkedTransports()[0].transportNumber >> '123456' ("dummy" property, does not help)
transport().linkedTransports()[0].transportRelated() >> null
transport().linkedTransports()[0].transportRelatedId() >> 5 (the id of the linkedTransport)
So my problem is that the transportRelated() is null.
UPDATE 3
Finally I got it working. It is important to have the added (referenced) element in cache when it is added. For my case I do something like:
ctxTransport.getTransportById(5, obs);
ctxTransport.createLinkedTransport(1, 5);
So before calling the createLinkedTransport I call the getTransportById with the 5th id (the id of the linkedTransport).
Anothing important thing is how we display the linkedTransports on our page:
<div data-bind="foreach: linkedTransports">
<span class="input-control text">
<!-- ko with: transportRelated -->
<!-- /ko -->
<i class="icon-remove btn" data-bind="click: $root.removeLinkedTransport" style="cursor:pointer;"/>
</span>
</div>
Pay attention to the ko with: transportRelated followed by <a href="#" data-bind="text: id()". At first I do wrong <a href="#" data-bind="text: transportRelated().id()". We cannot proceed like that.
Updated Answer
The short of it: the related transport entity wasn't showing up because it was not in cache.
Mr. B had the ID of the related entity and used that id while creating the new transport-link entity. But that id referred to an entity which is not in cache. Therefore, the navigation property to the related transport entity returned ... null.
Breeze navigation properties do not lazy load (as a server-side ORM such as EF might do). You have to ensure that the referenced entities are in cache.
This is a design choice, not an omission. In our experience, lazy loading leads to misuse which leads to performance problems that are blamed on the framework. We decided to bypass this particular hassle .. at least for now.
See the comments for more information.
Perhaps as important ... see how we diagnosed the problem. Good lessons learned there.
Original "wrong" Answer
[This answer turned out to be "wrong" but it lead to the "right" answer. Preserved for continuity.]
How is EF treating your LinkedTransport entity? It looks like it lacks data properties of its own; it only has foreign keys to related entities. Could it be that EF regards LinkedTransport as the junction table of a many-to-many association.
As you probably know, Breeze does not (yet ... and won't soon) support many to many. What happens if you give this table a data property ... even a dummy data property such as a Boolean? That will force EF to treat it as its own entity type ... with a compound key. The OrderDetail in Northwind has this same structure: a compound key (Order, Product) and small pay load (quantity, price).
There is probably another way to tell EF not to treat this as many-to-many without adding a dummy property. I just don't remember what that is.
In the ever increasingly epic failure that are my Create and Edit forms, I'm still having problems generating checkboxes via Html.Checkbox(). I'm not sure if I should just manually write the HTML at this point.
I have a view model:
public class AdminGameReviewViewModel
{
public Game GameData { get; set; }
public List<Genre> AllGenres { get; set; }
public List<PlatformListing> AllPlatforms { get; set; }
}
And a helper model:
public class PlatformListing
{
public Platform Platform { get; set; }
public bool IsSelected { get; set; }
}
And, I'm trying to generate the checkboxes like so:
<%: Html.Label("Platforms") %><br />
<% Model.AllPlatforms.ForEach(p => Html.Encode(Html.CheckBox("PlatformIDs", p.IsSelected, new { value = p.Platform.PlatformID }))); %>
But, they're not displaying at all.
I'm curious as to why I need to supply a boolean for the checkboxes themselves. I'd think it would simply send back checked values, as an array or list similar to PHP's $_POST. So, I'm not sure if I'm implementing my boolean property correctly, in addition to whatever other brain dead errors I'm committing. The boolean is forcing me to bury the data I want to display/bind further than I'd like, and it's giving me problems 'seeing' what I'm doing wrong.
I keep getting the feeling I'm looking too deeply at my problem, and trying to make the solution more complicated than it should be. Given my inexperience with MVC2/C#, I'm just not sure where to go from here. It doesn't help that this seems to be the one part of form handling that's either glossed over or completely ignored by most demos and tutorials.
So, again, any help would be greatly appreciated.
The problem is you're not writing anything to the Response stream.
Html.Checkbox() returns a string, it doesn't write directly to the response object (nor does Html.Encode() - that just escapes reserved characters and returns the result string). On top of all that, the ForEach extension method only executes an action, it doesn't return a value.
So you have to ditch the ForEach extension, and use the <%= %> or <%: %> code nuggets:
<% foreach (var p in Model.AllPlatforms) { %>
<%= Html.CheckBox("PlatformIDs", p.IsSelected, new { value = p.Platform.PlatformID }) %>
<% } %>
I'm new to MVC!
I am trying to use two DropDownLists (Cities, Categories) in a PartialView that will be used in MasterPage, meaning they will be visble all the time.
I tried to load them in HomeCOntroller, but that didn't work. I got an Exception.
I read something about making a baseController that the other controllers will inherit from, I have tried that, kind of, but I guess i'm doing something wrong.
This is the only code I got today:
Masterpage
<% Html.RenderPartial("SearchForm"); %>
PartialView (SearchForm.ascx)
<% using (Html.BeginForm("Search", "Search")) { %>
<% } %> // dont know why I need two BeginForms, if I dont have this the other form won't trigger at all! Weird!
<% using (Html.BeginForm("Search", "Search", FormMethod.Get)) { %>
<%= Html.DropDownList("SearchForm.Category", new SelectList(ViewData["Categories"] as IEnumerable, "ID", "Name", "--All categories--")) %>
<%= Html.DropDownList("Search.City", Model.Cities, "--All cities--") %>
<input name="search" type="text" size="16" id="search" />
<input type="submit" id="test" title="Search" />
<% } %>
Two question:
Where and how to load the DropDownLists is the problem. I have tried to load it in the HomeController, but when go to another page then it says that the DDLs is empty and I get a Excecption.
Why do I have to use two forms for the ActionMethod to trigger ?
Hope anyone can help me out!
It sounds like you're only setting the property for a single action result. The Model.Cities data will have to be populated for every single view that needs to use it.
One solution would be to move the population of it to an ActionFilter
public class CityListAttribute : ActionFilterAttribute
{
public override void OnActionExecuted(ActionExecutedContext filterContext) {
var result = filterContext.Result as ViewResult;
result.ViewData.Model = //populate model
base.OnActionExecuted(filterContext);
}
}
and then add the filter to your controller
[CityList]
public class HomeController : Controller {
public ActionResult Index() {
return View();
}
}
As for the two forms issue, there should be no reason that i can think of that you need an empty form.
Take a look at the html that's being output and make sure it's ok. Also check the action is being generated correcly
Better way to do this, is to create something like MasterController and have action method on it like this:
[ChildActionOnly]
public ActionResult SearchForm()
{
//Get city data, category data etc., create SearchFormModel
return PartialView(model);
}
I recommend you create strongly typed view (SearchForms.ascx of type ViewUserControl<SearchFormModel>). Also it may be a good idea to have a model like this:
public class SearchViewModel
{
public IList<SelectListItem> Cities { get; set; }
public IList<SelectListItem> Categories { get; set; }
}
and use a helper like this: http://github.com/Necroskillz/NecroNetToolkit/blob/master/Source/NecroNet.Toolkit/Mvc/SelectHelper.cs to convert raw data to DDL friendly format beforehand.
In any case, you now use Html.RenderAction() instead of Html.RenderPartial() and specify you want "SearchForm" action from "MasterController".
I'm having difficulty getting data from a textbox into a Controller. I've read about a few ways to accomplish this in Sanderson's book, Pro ASP.NET MVC Framework, but haven't had any success.
Also, I've ran across a few similiar questions online, but haven't had any success there either. Seems like I'm missing something rather fundamental.
Currently, I'm trying to use the action method parameters approach. Can someone point out where I'm going wrong or provide a simple example? Thanks in advance!
Using Visual Studio 2008, ASP.NET MVC2 and C#:
What I would like to do is take the data entered in the "Investigator" textbox and use it to filter investigators in the controller. I plan on doing this in the List method (which is already functional), however, I'm using the SearchResults method for debugging.
Here's the textbox code from my view, SearchDetails:
<h2>Search Details</h2>
<% using (Html.BeginForm()) { %>
<fieldset>
<%= Html.ValidationSummary() %>
<h4>Investigator</h4>
<p>
<%=Html.TextBox("Investigator")%>
<%= Html.ActionLink("Search", "SearchResults")%>
</p>
</fieldset>
<% } %>
Here is the code from my controller, InvestigatorsController:
private IInvestigatorsRepository investigatorsRepository;
public InvestigatorsController(IInvestigatorsRepository investigatorsRepository)
{
//IoC:
this.investigatorsRepository = investigatorsRepository;
}
public ActionResult List()
{
return View(investigatorsRepository.Investigators.ToList());
}
public ActionResult SearchDetails()
{
return View();
}
public ActionResult SearchResults(SearchCriteria search)
{
string test = search.Investigator;
return View();
}
I have an Investigator class:
[Table(Name = "INVESTIGATOR")]
public class Investigator
{
[Column(IsPrimaryKey = true, IsDbGenerated = false, AutoSync=AutoSync.OnInsert)]
public string INVESTID { get; set; }
[Column] public string INVEST_FNAME { get; set; }
[Column] public string INVEST_MNAME { get; set; }
[Column] public string INVEST_LNAME { get; set; }
}
and created a SearchCriteria class to see if I could get MVC to push the search criteria data to it and grab it in the controller:
public class SearchCriteria
{
public string Investigator { get; set; }
}
}
I'm not sure if project layout has anything to do with this either, but I'm using the 3 project approach suggested by Sanderson: DomainModel, Tests, and WebUI. The Investigator and SearcCriteria classes are in the DomainModel project and the other items mentioned here are in the WebUI project.
Thanks again for any hints, tips, or simple examples!
Mike
try strongly typing the page to use SearchCriteria to autopost the data like that ex:
public partial class Search: ViewPage<SearchDetails>
This should do it for you (unable to verify this is perfect - typed this from memory):
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult SearchDetails(FormCollection formValues)
{
var txtContents = formValues["Investigator"];
// do stuff with txtContents
return View();
}
1.) Have you looked into ViewModels for your View? In essence that is what your SearchCriteria class is. Make sure you strongly type your view with that model:
<%# Page Title="" Language="C#" MasterPageFile="~/Views/Shared/MyMaster.Master" Inherits="System.Web.Mvc.ViewPage<SearchCritieria>"
Also make sure that you use the HtmlHelper.TextBoxFor method to map this Investigator property to the SearchCritiera model. On Post back your text box value should be there:
'<%=Html.TextBoxFor(model => model.Invesigator)%>'
Good luck!
Also here is a great reference on using ViewModels that I have looked at a lot recently:
http://geekswithblogs.net/michelotti/archive/2009/10/25/asp.net-mvc-view-model-patterns.aspx
Thanks for the tips everyone. For learning purposes, I need to go back and follow the strongly typed route. I'm curious if I would have run into this problem if I would have done that from the beginning.
Until then, the following worked:
Use a submit button
Use this code for the form:
<% using(Html.BeginForm(new { Action = "SearchResults"})) { %> <% } >
Thanks again for you help!
Mike
I'm trying to use a resource file to hold label text for the Model in an MVC 2 project.
I've got the following class...
public class Person
{
[Display(ResourceType = typeof(Resources.Labels),Name="First")]
public string FirstName { get; set; }
public string LastName { get; set; }
}
...and have tried using...
<%: Html.EditorForModel() %>
<%: Html.EditorFor(m => m) %>
<%: Html.LabelFor(m => m.FirstName) %>
...but I'm getting "FirstName" for the label in all instances. Resource file is called Labels.resx, has an entry for "First" and is in Properties folder.
Having read a few posts, I believe this should work in .NET 4 (I'm using VS2010 RTM and have targetted .NET 4).
Should this work?
Thanks in advance
http://weblogs.asp.net/rajbk/archive/2010/04/27/localization-in-asp-net-mvc-2-using-modelmetadata.aspx
must be like below ;
[Required(ErrorMessageResourceType = typeof(Resources), ErrorMessageResourceName = "Required")]