updating object using NHibernate in Asp.net MVC4 - asp.net-mvc-4

I have problem in updating object with nhibernate in ASP.Net MVC4
Im doing the update in this scenario:
the application loads an object in the first session
the object is passed up to the UI tier
some modifications are made to the object
the object is passed back down to the business logic tier
the application persists these modifications by calling SaveOrUpdate()
all this happen only in one session. I have static a class name NHibernateSessionPerRequest
and its constructor is static (singeleton)
[HttpPost]
public ActionResult Edit(Menu menu)
{
if (ModelState.IsValid)
{
repository.SaveOrUpdate(menu);
TempData["message"] = string.Format("{0} has been saved", menu.Name);
return RedirectToAction("Index");
}
else
{
// there is something wrong with the data values
return View(menu);
}
}
but menu ID is zero. and doesnt have its original ID (id is type of GUID). and SaveOrUpdate() alway treat it as a new object and save it not update it.
here is Edit.cshtml:
#model MyApp.Domain.Entities.MenuComponent
#{
ViewBag.Title = "Edit";
Layout = "~/Views/Shared/_AdminLayout.cshtml";
}
<h2>Edit #Model.Name
</h2>
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
<fieldset>
<legend>MenuComponent</legend>
<div class="editor-label">
#Html.LabelFor(model => model.Name)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Name)
#Html.ValidationMessageFor(model => model.Name)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Description)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Description)
#Html.ValidationMessageFor(model => model.Description)
</div>
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
how can I update the object?

From your comments, I see two problems :
it seems you have removed #Html.HiddenFor(model => model.ID) from your markup. You should put it back, or your ID won't be stored in the page to be posted back to your Controller.
Your ID code is public virtual Guid ID { get; private set; } You should remove the private modifier on the setter. I guess it prevents the ModelBinder to set the property when receiving the posted data

from what you have posted it seems that you are returning the entity to the view and there isn't any concept of view model being used.
Firstly
usually entities are defined with private setters which would prevent the id being posted back to the Edit action if you use the entity itself.
Secondly (i am not certain about this)
since you are getting the object in the post back and using a session per request (assumption since it is quite common) nhibernate might treat it as a new entity. I am highly doubtful for the second point but will try re creating this and update the answer

Related

asp.net-mvc: Adding a passwordFor to the view auto populates my fields

The scenario: I am trying to add a view to Create new users by admin. The app is form authentication. There is a logged in user(admin). When a Password For is added to the view, the view automatically populates the fields with the logged in user.
The controller code:
public ActionResult Create()
{
var userViewModel = new UserViewModel();
return View(userViewModel);
}
The view code:
#model MVC4.Models.UserViewModel
#{
ViewBag.Title = "Create";
}
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<fieldset>
<legend>UserVireModelcs</legend>
<div class="editor-label">
#Html.LabelFor(model => model.UserName)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.UserName)
#Html.ValidationMessageFor(model => model.UserName)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Password)
</div>
<div class="editor-field">
#Html.PasswordFor(model => model.Password)
#Html.ValidationMessageFor(model => model.Password)
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
What I suspect is happening here is that the application isn't pre-filling the fields, but the browser is. This is because this form looks exactly like a login prompt. (You can test this by clearing your information from the browser itself so that it doesn't auto-fill any login prompt on this site.)
What I would recommend is to semantically separate the concepts of logging in and creating a user. Basically... rename the fields. A simple view model with some more specific names would help:
public class CreateUserViewModel
{
public string NewUserUsername { get; set; }
public string NewUserPasswords { get; set; }
}
Then use that in your view:
<div class="editor-label">
#Html.LabelFor(model => model. NewUserUsername)
</div>
<div class="editor-field">
#Html.EditorFor(model => model. NewUserUsername)
#Html.ValidationMessageFor(model => model. NewUserUsername)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.NewUserPassword)
</div>
<div class="editor-field">
#Html.PasswordFor(model => model.NewUserPassword)
#Html.ValidationMessageFor(model => model.NewUserPassword)
</div>
It's a little more verbose than perhaps one might want it to be (I would agree that simpler is always better), but adding some explicit context to the naming in this case makes it more clear to the browser that this isn't a login form. Use any naming that makes sense for your needs, just make it more descriptive than Username and Password.

MVC 4 Model not returned to controller

I have three cascading dropdowns using Ajax to populate themselves on a view. Also in the view I call a partial view that iterates a container in the model and for each item calls another partial to display for editing the model properties appropriate according to the selected items in the dropdowns. I have built a complex model that contains the dropdown choices as well as the properties to be edited, and pass the portion of the model to the lowest level partial necessary to display the properties to be edited.
I want to update the db when the user clicks the submit button, through a normal Html.BeginForm, not by using Ajax. So I must wrap only the partials that display the properties in the form so that the existing Ajax functionality does not post to the controller. The problem is that although I can build this all, and the submit button connects to the controller as expected, the model returns to the controller null.
Does the model not come back from the partials up through the path that built them? Or more correctly stated, does the model not persist on the page even if it is built using partials?
I am sure someone is going to suggest that I post back using Ajax but that is not a best option for me. Or someone might ask what the html looks like on the page. Oddly, I can only see the complete html using browser developer tools, it does not show in a View Source selection.
Any ideas?
Moving this to where it belongs:
I'm not certain I understand. I get that the returning model needs to match the expected model but I don't get the explanation above. You say "So if your controller looks like this:", and of course it does, then what? That's wrong?
I have to look in dev tools for Chrome to see the actual html output and I see this in one case:
<input class="text-box single-line" id="status_Comments" name="status.Comments" type="text" value="Last Install to be completed this weekend">
So if the 'name' tag needs to look proper, I think it does. Am I wrong?
My date fields look like this:
<input type="text" value="8/19/2014" class="datepicker hasDatepicker" id="dp1391795955619">
So there's no 'name' tag but does have an id. Do I need to add a name?
Here's the code that generates the above:
#foreach (Status status in Model) {
string date = status.Date.HasValue ? status.Date.Value.ToShortDateString() : string.Empty;
<tr>
<td style="width: 175px;">#Html.DisplayFor(model => status.Name)</td>
<td style="width: 75px;">#Html.DisplayDropdownColorFor(model => status.StatusValue)</td>
<td style="width: 80px;"><input type="text" value="#date" class="datepicker" /></td>
<td style="width: 80px;"><input type="text" value="" class="datepicker" /></td>
<td style="width: 375px;">#Html.EditorFor(model => status.Comments)</td>
</tr>
}`
Geez, I sound like a desperate moron.
Thanks for your help.
Let's say you had a couple of classes like this:
public class SomeClass1
{
public string Name { get; set; }
public SomeClass2 class2Value { get; set; }
}
public class SomeClass2
{
public string Description { get; set; }
}
And you have a form in a Razor view:
#model SomeClass1
#using (Html.BeginForm()) {
<fieldset>
<div class="editor-label">
#Html.LabelFor(model => model.Name)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Name)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.class2Value.Description)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.class2Value.Description)
</div>
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
}
The actual HTML code is going to look something like this:
<form id="form1" action="/pageurl" method="POST">
<fieldset>
<div class="editor-label">
Name
</div>
<div class="editor-field">
<input type="text" name="Name" id="Name" value="Name val 1" />
</div>
<div class="editor-label">
Description
</div>
<div class="editor-field">
#Html.EditorFor(model => model.class2Value.Description)
<input type="text" name="class2Value.Description" id="class2Value_Description" value="Desc val 2" />
</div>
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
</form>
If you notice, the name attribute of the form fields are set by MVC to follow the model. In the case of the nested object, it uses the underscore in the ID and period in the name to keep track of the where in the post model the values should go.
So if your controller looks like this:
[HttpPost]
protected ActionResult Save(SomeClass1 model)
{
//Then
// SomeClass1.Name == "Name val 1"
// and
// SomeClass1.class2Value.Description == "Desc val 2"
}
That's what #David was getting at in the comments... No matter how complex your page, when the <form> posts back to the server, the name attributes of the fields you are returning have to line up with the model that the controller action is expecting... or you will get null values.
You can even create a custom class for the post model, and as long as the name attributes line up with the model, and even though it was not the same as the #model at the top of the Razor view, MVC will use the key/value pairs and populate the data in your post model.
UPDATE
I updated the above to reflect a correction. The underscore is the delimiter in the ID attribute, and the period is used in the NAME attribute.
#leemid: An HTML form only posts fields with a name attribute. Only the name'ed inputs, selects, textareas, etc. will be passed back to the server when the form is submitted. And those names have to be inline with the model that is expected by the controller. For the sake of sounding redundant, that date input field you showed the example for, does not have a name attibute, and if the name were the same as the id, there would have to be a property in your model like
public string dp1391795955619 { get; set; }
for it to show up in your in controller action. You can set the name manually, as long as it's named the way MVC is expecting so that it can pass it into the model when posting.
In my example, the parts I was trying to highlight was the relationship between the class and property names, versus the name attributes MVC writes to the HTML document, and how it keeps all that stuff straight. In simple examples, you don't have to think about it. But in your complex scenario, it looks like you're going to have to manage some of the naming conventions manually so that MVC can understand what you're trying to do.

Saving a form post in Sitecore MVC generates a "method not found" error

Has anyone else run into such an error? It would be great if anyone can suggest a way to examine this further or maybe has a direction for a solution.
I have tried to debug this but could not figure it out. The action specified in the cshtml file is reached and the posted information is available. The error occurs when this line is executed.
masterService.Save(page);
The WikiEntryModel does contain a ImageField. This is not filled in when the form is called up and is also not specified as a field in the form.
Additional information
I am using GlassMapper v2.0 in combination with Sitecore 7.1.
This is the actual error description as displayed on the page.
Method not found: 'System.String Sitecore.Data.Fields.ImageField.get_MediaPath()'.
The cshtml file of the form.
#model WikiEntryModel
#using(Html.BeginRouteForm(Sitecore.Mvc.Configuration.MvcSettings.SitecoreRouteName, FormMethod.Post))
{
#Html.Sitecore().FormHandler("Forms","PostEditWikiEntry")
<ul>
<li>
#Html.LabelFor(model => model.Title)<br />
#Html.TextBoxFor(model => model.Title)
</li>
<li>
#Html.LabelFor(model => model.Introduction)<br />
#Html.TextAreaFor(model => model.Introduction)
</li>
<li>
<input type="submit" value="Save" />
</li>
</ul>
}
The code of the specified action in the controller.
[HttpPost]
public ActionResult PostEditWikiEntry(WikiEntryModel postedModel)
{
var contextService = new SitecoreContext();
var masterService = new SitecoreService("master");
var page = contextService.GetCurrentItem<WikiEntryModel>();
if (ModelState.IsValid)
{
page.Title = postedModel.Title;
page.Introduction = postedModel.Introduction;
using (new SecurityDisabler())
{
masterService.Save(page);
}
}
return RenderComponent<WikiEntryModel>("Forms/Edit wiki entry");
}

html.beginform doesn't post back to controller, Error Loading Page message?

I have been trying post a value back to the controller so I can update a balance elsewhere, but I cannot get it to work. I get a message
Error Loading Page
All I want is a double value, but to be honest, I would just like to get the code into the controller in the first instance.
My .cshtml is like:
#using (Html.BeginForm("Deposit", "AccountController", FormMethod.Post))
{
<fieldset>
<legend>Deposit Amount</legend>
<div class="editor-label">
#Html.LabelFor(model => model.UserName)
</div>
<div class="editor-field">
#Html.TextBoxFor(model => model.UserName)
#Html.ValidationMessageFor(model => model.UserName)
</div>
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
}
And the controller is:
[HttpGet]
public ActionResult Deposit()
{
return View();
}
[HttpPost]
public ActionResult Deposit(LoginModel model)
{
// do things
return View(model);
}
I am new to MVC. I know the view code doesn't have the amount value that I ultimately would like to use, or I may add it to a model and just pass that, but I cannot get it to the controller at all.
Thanks in advance.
I think you're just slightly off on the naming conventions of MVC. Assuming the name of your controller class is AccountController, you should specify that minus the "Controller" part in the BeginForm() method:
#using (Html.BeginForm("Deposit", "Account", FormMethod.Post))
{
...
}

My MVC Controller is not getting the right values from Html.dropdownlist

I'm developing a MVC application.
I had 2 controllers, one for creating Risks and one for creating Tests.
I had to first create a Risk and when I created a Test, I had to use the created Risk as a field of the new Test. I had a dropdownlist to choose from.
Now I'm supposed to create both in the same page because one Test will have one Risk, ie, when I create one Test, I have to create its own Risk, which belongs to that Test and can not be used by any other Test. Business rules.
So, I created a new model with both Test and Risk, changed the model for the create page, included the Risk creation in the create page.
Ok, it seems to work fine, but all the dropdownlists lose their value when I go back to the control. Everything else works fine, except for that.
Here's my model
using CTTModel;
namespace MvcApplication1.ViewModels
{
public class CreateTestWithRisk
{
public Test Tests { get; set; }
public Risk Risks { get; set; }
}
}
The funny thing is that if I try use this model in the first version, Risks and Tests created apart, it works with Risks and not with Tests, meaning that all dropdownlists in Risks return the value and in Tests creation, they don't. Strange.
Here is the code from the controller
//
// GET: /Test/Create
public ActionResult Create()
{
// Risk
ViewBag.SubProcessId = new SelectList(_db.SubProcesses, "Id", "Name");
ViewBag.RiskSeverityId = new SelectList(_db.RiskSeverities, "Id", "Name");
ViewBag.RiskFrequencyId = new SelectList(_db.RiskFrequencies, "Id", "Name");
ViewBag.RiskRatingId = new SelectList(_db.RiskRatings, "Id", "Name");
ViewBag.RiskType1Id = new SelectList(_db.RiskType1, "Id", "Name");
ViewBag.RiskType2Id = new SelectList(_db.RiskType2, "Id", "Name");
// Test
ViewBag.RegionLevelId = new SelectList(_db.RegionLevels, "Id", "Name");
return View();
}
//
// POST: /Test/Create
[HttpPost]
public ActionResult Create(CreateTestWithRisk test)
{
if (ModelState.IsValid)
{
_db.Risks.Add(test.Risks);
Risk iD = _db.Risks.OrderByDescending(id => id.Id).FirstOrDefault();
if (iD != null)
{
test.Tests.RiskId = iD.Id;
test.Tests.Risk = test.Risks;
}
_db.Tests.Add(test.Tests);
_db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.RegionLevelId = new SelectList(_db.RegionLevels, "Id", "Name");
//ViewBag.RiskId = new SelectList(_db.Risks, "Id", "Name", test.Tests.RiskId);
ViewBag.SubProcessId = new SelectList(_db.SubProcesses, "Id", "Name");
ViewBag.RiskSeverityId = new SelectList(_db.RiskSeverities, "Id", "Name");
ViewBag.RiskFrequencyId = new SelectList(_db.RiskFrequencies, "Id", "Name");
ViewBag.RiskRatingId = new SelectList(_db.RiskRatings, "Id", "Name");
ViewBag.RiskType1Id = new SelectList(_db.RiskType1, "Id", "Name");
ViewBag.RiskType2Id = new SelectList(_db.RiskType2, "Id", "Name");
return View(test);
}
And here's part of teh code from the view
#model MvcApplication1.ViewModels.CreateTestWithRisk
#{
ViewBag.Title = "Create New Test";
}
<h2>
Create New Test</h2>
#using (Html.BeginForm())
{
#Html.ValidationSummary(true)
<div style="display:inline-block">
<fieldset style="float:left">
<legend>Test</legend>
<div class="editor-label">
#Html.LabelFor(model => model.Tests.RegionLevelId, "RegionLevel")
</div>
<div class="editor-field">
#Html.DropDownList("RegionLevelId", String.Empty)
#Html.ValidationMessageFor(model => model.Tests.RegionLevelId)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Tests.Code)
</div>
<div class="editor-field">
</div>
<div>
#Html.EditorFor(model => model.Tests.Code)
#Html.ValidationMessageFor(model => model.Tests.Code)
</div>
</fieldset>
<fieldset style="float:left;margin-left:10px">
<legend>Risk</legend>
<div class="editor-label">
#Html.LabelFor(model => model.Risks.SubProcessId, "SubProcess")
</div>
<div class="editor-field">
#Html.DropDownList("SubProcessId", String.Empty)
#Html.ValidationMessageFor(model => model.Risks.SubProcessId)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Risks.RiskSeverityId, "Severity")
</div>
<div class="editor-field">
#Html.DropDownList("RiskSeverityId", String.Empty)
#Html.ValidationMessageFor(model => model.Risks.RiskSeverityId)
</div>
</fieldset>
</div>
<p>
<input type="submit" value="Create" />
</p>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
I would have left this as a comment if I had enough rep. However, as a shot in the dark as an answer:
Is your controller properly passing the CreateTestWithRisk model to the View? I.e. are you still using the TestController and expecting it to use the new model? Difficult to see because you don't specify the Controller name, etc. in your question.
This would explain why no data of type CreateTestWithRisk is being passed back to the controller.