MVC3 view not updating using EF 4.1 - asp.net-mvc-3

I am having a problem updating a record. For some reason it is not even hitting the post action in the controller and just returning:
"An item with the same key has already been added."
It seems to be behaving as if it is doing an insert rather than an update. I would appreciate a new set of eyes on this. It is probably something very simple that I have missed.
Controller:
// GET: /Manage/Regions/Edit/5
public ActionResult Edit(int id)
{
Region_CU regionEdit = (from r in db.Venues_Regions
where r.RegionsID == id
select new Region_CU
{
RegionsID = r.RegionsID,
Name = r.Name,
CalendarLink = r.CalendarLink,
MapIcon = r.MapIcon,
QtrStart = r.QtrStart,
QtrEnd = r.QtrEnd,
FacebookLikeBox = r.FacebookLikeBox,
FacebookId = r.FacebookId
// region = r
}).Single();
return View(regionEdit);
}
//
// POST: /Manage/Regions/Edit/5
[HttpPost]
public ActionResult Edit(Region_CU r)
{
if (ModelState.IsValid)
{
var v = db.Venues_Regions.First(i => i.RegionsID == r.RegionsID);
//v.RegionsID = r.RegionsID;
v.Name = r.Name;
v.CalendarLink = r.CalendarLink;
v.MapIcon = r.MapIcon;
v.QtrStart = r.QtrStart;
v.QtrEnd = r.QtrEnd;
v.FacebookLikeBox = r.FacebookLikeBox;
v.FacebookId = r.FacebookId;
//Venues_Regions v = new Venues_Regions
//{
// RegionsID = r.RegionsID,
// Name = r.Name,
// CalendarLink = r.CalendarLink,
// MapIcon = r.MapIcon,
// QtrStart = r.QtrStart,
// QtrEnd = r.QtrEnd,
// FacebookLikeBox = r.FacebookLikeBox,
// FacebookId = r.FacebookId
//};
//db.Venues_Regions.Attach(v);
//db.ObjectStateManager.ChangeObjectState(v, EntityState.Modified);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(r);
}
View :
#model THPT_Razor.Areas.Manage.Models.Region_CU
#{
ViewBag.Title = "Edit Region";
}
<h2>Edit</h2>
<link href="#Url.Content("~/Content/themes/base/jquery.ui.all.css")" rel="stylesheet" type="text/css" />
<script src="#Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"> </script>
<script>
$(document).ready(function () { $('.date').datepicker({ dateFormat: "mm/dd/yy" }); });
</script>
#using (Html.BeginForm()) {
#* #Html.ValidationSummary(true)*#
<fieldset>
<legend>#Html.DisplayFor(model => model.Name)</legend>
#Html.HiddenFor(model => model.RegionsID)
#Html.HiddenFor(model => model.Name)
#Html.HiddenFor(model => model.CalendarLink)
<div class="editor-label">
#Html.Label("Map Icon")
</div>
<div class="editor-field">
#Html.DropDownListFor(model => model.MapIcon, new SelectList(Model.mapicons,"id","Description"))
</div>
<div class="editor-label">
#Html.Label("Quarter Start")
</div>
<div class="editor-field">
#Html.TextBoxFor(model => model.QtrStart, new { #class = "date" })
#Html.ValidationMessageFor(model => model.QtrStart)
</div>
<div class="editor-label">
#Html.Label("Quarter End")
</div>
<div class="editor-field">
#Html.TextBoxFor(model => model.QtrEnd, new { #class = "date" })
#Html.ValidationMessageFor(model => model.QtrEnd)
</div>
<div class="editor-label">
#Html.Label("Region Facebook ID")
</div>
<div class="editor-field">
#Html.EditorFor(model => model.FacebookId)
#Html.ValidationMessageFor(model => model.FacebookId)
</div>
<div class="editor-label">
#Html.Label("Region Facebook LikeBox Code")
</div>
<div class="editor-field">
#Html.EditorFor(model => model.FacebookLikeBox)
#Html.ValidationMessageFor(model => model.FacebookLikeBox)
</div>
<p>
<input type="submit" value="Update" />
</p>
</fieldset>
Edit:
I have received several good suggestions but I guess I have not been clear. Region_CU is not an Entity. Venues_Regions is what I am trying to update. see the comments in the class below for clarification. The original objective was to build a simple wrapper that had the Venues_Regions object and a list object for the map icons. However, the data annotation for the field likebox was not being passed through resulting in the the Venues_Regions object to be broken out. Now when I try to save the update it is not even hitting the http post action. I hope this clears up what I am trying to accomplish and asking for help with. Thanks again for all the help and quick responses.
//create and update
public class Region_CU
{
public Region_CU()
{
}
public List<MapIcon> mapicons { get; set; }
//public Venues_Regions region { get; set; }
// The fields below are what makes up Veunes_Region
// this was broken out from the above Venues_Region
// because the UIHint was not being passed through
public int RegionsID { get; set; }
public string Name { get; set; }
public string CalendarLink { get; set; }
public int MapIcon { get; set; }
public DateTime? QtrStart { get; set; }
public DateTime? QtrEnd { get; set; }
[UIHint("tinymce_jquery_full"), AllowHtml]
public string FacebookLikeBox { get; set; }
public string FacebookId { get; set; }
public string mapIcon { get; set; }
}
Edit #2:
After a good nights sleep the solution presented itself. In the update action all I needed to do was change from the wrapper being passed in to the Venues_Region object being passed in and now everything works.
Thanks for all the help and suggestions.
Thanks in advance for the help,
Chris

Actually all you need to do is load the venues and call TryUpdateModel. You don't even need to pass in the object. Then save the venue. Another approach is to use automapper to copy the fields between objects or use the attach method as mentioned but either way no manual field copying is required.

Related

Cannot Bind Model Object to TextBoxFor

I am trying to enter a 'UserId' in the texbox and when I click on 'Find User' button, I need to get 'UserName' and 'Role' from Database based on the UserId and display them in the texboxes so that if Username and Role have to be edited they will be edited and Updated to Database.
But, when I enter UserId, the UserId value is available in the Model, and I am able to query teh database and get the results and assign them to the Model's UserName and Role (Model.UserName and Model.Role), but I am unable bind them to HTML.TextBoxFor.
I have a model like below
public class EditUserInfoModel
{
[Display(Name = "User ID")]
public string UserId { get; set; }
[Display(Name = "User Name")]
public string UserName { get; set; }
[Display(Name = "Role")]
[DataType(DataType.Text)]
public string Role { get; set; }
}
..and Controller like below
[HttpPost]
public ActionResult EditUserInfo(EditUserInfoModel model)
{
if (ModelState.IsValid)
{
string con = ConfigurationManager.ConnectionStrings["DMSCON"].ConnectionString;
DataTable dt = new DataTable();
SqlConnection dbCon = new SqlConnection(con);
SqlCommand cmd = new SqlCommand();
cmd.CommandText = "select user_name,user_role from user_info where
user_id=#userid";
cmd.Parameters.Add(new SqlParameter("#userid", model.UserId));
cmd.Connection = dbCon;
dbCon.Open();
SqlDataAdapter dap = new SqlDataAdapter(cmd);
dap.Fill(dt);
EditUserInfoModel myModel = new EditUserInfoModel();
foreach (DataRow row in dt.Rows)
{
myModel.UserName = row["user_name"].ToString();
myModel.Role = row["user_role"].ToString();
//model.isBlocked = (bool)row[""];
//model.isExpired = (bool)row[""];
// model.UserId = "somevalue";
}
return View("EditUserInfo", myModel);
}
return View();
}
and the View like below
#model WebDMS.Models.EditUserInfoModel
#{
ViewBag.Title = "EditUserInfo";
//Layout = "~/Views/Shared/_LayoutPage1.cshtml";
}
<h2>
EditUserInfo</h2>
#using (Html.BeginForm())
{
#Html.ValidationSummary(true)
<fieldset>
<legend>EditUserInfoModel</legend>
<div class="editor-label">
#Html.LabelFor(model => model.UserId)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.UserId)
#Html.ValidationMessageFor(model => model.UserId)
</div>
<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.Role)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Role)
#Html.ValidationMessageFor(model => model.Role)
</div>
<input type="submit" value="Find User" />
</fieldset>
}
you need to do little modification in your controller action method and view model.
Model
public class EditUserInfoModel
{
[Required]
[Display(Name = "User ID")]
public string UserId { get; set; }
[Required]
[Display(Name = "User Name")]
public string UserName { get; set; }
[Required]
[Display(Name = "Role")]
[DataType(DataType.Text)]
public string Role { get; set; }
}
Controller:
[HttpPost]
public ActionResult EditUserInfo(EditUserInfoModel model)
{
string con = ConfigurationManager.ConnectionStrings["DMSCON"].ConnectionString;
DataTable dt = new DataTable();
SqlConnection dbCon = new SqlConnection(con);
SqlCommand cmd = new SqlCommand();
cmd.CommandText = "select user_name,user_role from user_info where
user_id=#userid";
cmd.Parameters.Add(new SqlParameter("#userid", model.UserId));
cmd.Connection = dbCon;
dbCon.Open();
SqlDataAdapter dap = new SqlDataAdapter(cmd);
dap.Fill(dt);
EditUserInfoModel myModel = new EditUserInfoModel();
foreach (DataRow row in dt.Rows)
{
myModel.UserName = row["user_name"].ToString();
myModel.Role = row["user_role"].ToString();
}
//return View("EditUserInfo", myModel); //try removing this line
return View(myModel);
}
If you use dataannotations then you do not need to check ModelState.IsValid in your action method. Your #ValidationMessage on your form fields inside your view will have more meaning then and will work as expected
you do not need to explicitly call the view name. Let the MVC-3 handles that. Instead try it as mentioned in this response.
these are not i would say managed and structured approach though. Always Close() and Dispose() your Db connections by putting them in using() block or try-catch-finally block. Make a layered structure so you can separate your database interaction from your controller actions. Hope it helps.

Dropdownlist not selecting the preselected value-mvc3

I am having strange issue, MVC dropdown selected value is not preselected on page Load.
My Models are:
public class DocumentTypesViewModel
{
[Required(ErrorMessage = "DocumentType is required")]
public int OHDocumentTypeId { get; set; }
public string OHDocumentTypeDescription { get; set; }
}
public class ClientAdvancedSearchViewModel
{
[Display(Name = "Name")]
public string Name { get; set; }
[Display(Name = "DocumentType")]
public string DocumentTypeId { get; set; }
public IEnumerable<SelectListItem> DocumentTypes { get; set; }
}
In My Controllers I am populating the ClientAdvancedSearchViewModel like this
[HttpGet]
public ActionResult ClientAdvancedSearch()
{
ClientAdvancedSearchViewModel clientAdvancedSearchViewModel = iClientReferralRecordsRepository.GetDocumentMetadata();
//DocumentTypes Dropdown
var ddlDocumentTypes = iDocumentTypeRepository.GetDocumentTypes();
clientAdvancedSearchViewModel.DocumentTypes = new SelectList(ddlDocumentTypes, "OHDocumentTypeId", "OHDocumentTypeDescription",clientAdvancedSearchViewModel.DocumentTypeId);
return View(clientAdvancedSearchViewModel);
}
Finally in the View:
<td>
<div class="editor-label">
#Html.LabelFor(model => model.DocumentTypes)
</div>
<div class="editor-field">
#Html.DropDownListFor(x => x.DocumentTypeId, Model.DocumentTypes, "Please Select", new { #id = "ddlDocumentType" })
</div>
</td>
I believe the Name of the dropdown is same is x => x.DocumentTypeId, becuase of this I think, my value is not preselected.
This is the ViewSource for generated HTML for the Drop Down
<select id="ddlDocumentType" name="DocumentTypeId">
<option value="">Please Select</option>
<option value="20">records</option>
<option value="21"> record1</option>
..
How can I rename my dropdownlist name or How can I solve my problem?
Thank you
Updated: Added the missed line
ClientAdvancedSearchViewModel clientAdvancedSearchViewModel = iClientReferralRecordsRepository.GetDocumentMetadata();
Your code on your view is just right. You forgot to set the value for DocumentTypeId. This is your code as you posted:
[HttpGet]
public ActionResult ClientAdvancedSearch()
{
//DocumentTypes Dropdown
var ddlDocumentTypes = iDocumentTypeRepository.GetDocumentTypes();
clientAdvancedSearchViewModel.DocumentTypes = new SelectList(ddlDocumentTypes, "OHDocumentTypeId", "OHDocumentTypeDescription",clientAdvancedSearchViewModel.DocumentTypeId);
return View(clientAdvancedSearchViewModel);
}
And you missed this:
clientAdvancedSearchViewModel.DocumentTypeId = some_value;
Also, do you intend to have DocumentTypeId as an int instead of a string?
UPDATE:
You can also check that you set the id like this:
#Html.DropDownListFor(x => x.DocumentTypeId, new SelectList(Model.DocumentTypes, "Id", "Value", Model.DocumentTypeId), new { #id = "ddlDocumentType" })
Notice I used the overload with new SelectList. I don't remember all the overloads and I do it like that all the time, so you might check our the other overloads that suits your need.

Partial View and ajax

I want to update Partial View via ajax, but it does not work. Look at this model class:
public class LogOnModel
{
[Required]
[Display(Name = "User name")]
public string UserName { get; set; }
[Required]
[DataType(DataType.Password)]
[Display(Name = "Password")]
public string Password { get; set; }
[Display(Name = "Remember me?")]
public bool RememberMe { get; set; }
public bool IsLoggedIn { get; set; }
public string ReturnUrl { get; set; }
}
the following view:
#model ITW2012Mobile.ViewModels.LogOnModel
<div id='LogOn' style="background-color: White;">
#using (Ajax.BeginForm("LogOnAjax", "Home", new AjaxOptions { UpdateTargetId = "LogOn", OnSuccess = "logInComplete" }))
{
ITW2012Mobile.ViewModels.LogOnModel m = Model;
#Html.EditorFor(model => model.IsLoggedIn)
#Html.EditorFor(model => model.ReturnUrl)
<div>
#Html.ValidationSummary()
</div>
<div>
#Html.LabelFor(model => model.UserName)
#Html.EditorFor(model => model.UserName)
</div>
<div>
#Html.LabelFor(model => model.Password)
#Html.EditorFor(model => model.Password)
</div>
<div>
<input type="submit" value="Login" />
</div>
}
</div>
and the following controller class:
public ActionResult LogOnAjax(LogOnModel model)
{
if (!User.Identity.IsAuthenticated)
{
if (ModelState.IsValid)
{
if (Membership.ValidateUser(model.UserName, model.Password))
{
FormsAuthentication.SetAuthCookie(model.UserName, model.RememberMe);
model.IsLoggedIn = true;
model.ReturnUrl = Url.Action("Index", "Home");
}
else
{
ModelState.AddModelError("", "The user name or password provided is incorrect.");
}
}
// If we got this far, something failed, redisplay form
return PartialView("PartialViewAjaxLogOn", model);
}
else
{
return PartialView("PartialViewLogOut");
}
}
even when username/password are correct and IsLoggedIn = true and ReturnUrl!=empty view shows empty fields for these variables (but debugger shows values inside). Why and how to make it correctly?
Try clearing the values you are modifying in your action from modelstate or if you use them in html helpers the old values will be used:
ModelState.Remove("IsLoggedIn");
model.IsLoggedIn = true;
ModelState.Remove("ReturnUrl");
model.ReturnUrl = Url.Action("Index", "Home");
Also bear in mind that upon successful authentication and cookie emission you should not display a view (partial in your case). You should redirect so that the authentication cookie is sent by the client on the subsequent request. You should redirect to the return url. But since you are doing this using AJAX you should probably send some indication to the client that the authentication was successful so that you can redirect on the client.

Can ASP.NET MVC Html Helpers work with descendant classes?

If a partial view is based upon a base class, is it possible to check if it is a descendant class and if so, use the descndant class' properties within the Html helpers (LabelFor, EditorFor etc.)?
#model ProjectX.Models.VehicleModel
<div>
#Html.LabelFor(model => model.Fuel)
#Html.TextBoxFor(model => model.Fuel)
</div>
#{
if (Model is CarModel)
{
CarModel car = (CarModel)Model;
#Html.LabelFor(car => car.Doors)
#Html.TextBoxFor(car => car.Doors)
}
}
Yes its possible; try this ...
Model classes
namespace MvcApplication2.Models
{
public class Vehicle
{
public string Fuel { get; set; }
}
public class Car : Vehicle
{
public int Doors { get; set; }
}
}
View
#model MvcApplication2.Models.Vehicle
#{
ViewBag.Title = "Index";
}
<h2>Index</h2>
<div>
#Html.LabelFor(model => model.Fuel)
#Html.TextBoxFor(model => model.Fuel)
</div>
#{
if (Model is MvcApplication2.Models.Car)
{
#Html.LabelFor(model => ((MvcApplication2.Models.Car)model).Doors)
#Html.TextBoxFor(model => ((MvcApplication2.Models.Car)model).Doors)
}
}
Hope this helps.
If your question is whether you can use descendants of Models with #Html, then I see no reason why not. The code you provide should work.

MVC3 Entity Framework 4.1RC how does #Html.DropDownListFor actually work?

OK, I've been over Google and StackOverflow and ASP.net - am I really the only person who doesn't get this?
Rough problem = I have an Employee entity that references an Office entity. I was previously able to create new employees (beat me with a hammer for forgetting how that code worked) but now I just can't create an employee nor edit an existing one.
Now, here's what I learned;
1) Make sure you add the Offices list to the ViewBag at every step
2) That includes a failed POST/edit; call the function to re-populate the ViewBag with the Offices list
3) I think(!!) that you always want to set the Employee.Office, not the Employee.Office.OfficeID; the latter leads to "is part of the object's key information and cannot be modified" errors
So, what I have is;
A controller that has the following method;
private void AddOfficesToViewBag()
{
Dictionary<string, Office> list = new Dictionary<string, Office>();
foreach (Office office in company.GetAllOffices())
list.Add(office.ToString(), office);
SelectList items = new SelectList(list, "Value", "Key");
ViewBag.OfficeList = items;
}
Create pair looking like;
public ActionResult Create()
{
if (company.Offices.Count() < 1)
return RedirectToAction("Create", "Office", (object) "You need to create one or more offices first");
AddOfficesToViewBag();
return View(new Employee());
}
//
// POST: /Employee/Create
[HttpPost]
public ActionResult Create(Employee emp)
{
if (TryUpdateModel<Employee>(emp))
{
company.Employees.Add(emp);
company.SaveChanges();
return RedirectToAction("Index");
}
else
{
AddOfficesToViewBag();
return View(emp);
}
}
and an Edit pair that looks like;
public ActionResult Edit(int id)
{
Employee emp = company.Employees.Single(e => e.EmployeeID == id);
AddOfficesToViewBag();
return View(emp);
}
//
// POST: /Employee/Edit/5
[HttpPost]
public ActionResult Edit(int id, FormCollection collection)
{
Employee emp = company.Employees.Single(e => e.EmployeeID == id);
if (TryUpdateModel(emp))
{
company.SaveChanges();
return RedirectToAction("Index");
}
else
{
AddOfficesToViewBag();
return View(emp);
}
}
I'll pick the Edit View, which is pretty much the same as the Create View;
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
Employee
#Html.HiddenFor(model => model.EmployeeID)
<div class="editor-label">
#Html.LabelFor(model => model.Office)
</div>
<div class="editor-field">
#Html.DropDownListFor(model => model.Office, (SelectList) ViewBag.OfficeList)
#Html.ValidationMessageFor(model => model.Office)
</div>
<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.Age)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Age)
#Html.ValidationMessageFor(model => model.Age)
</div>
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
}
I would say that the Edit, in particular, looks almost there. It manages to bind to the Employee object passed in and sets the dropdown to the appropriate entry.
Viewing the original HTML source shows that the output value is the Office.ToString() value.
The odd thing to me is that some magic is happening that binds Employee->Office to the correct entry, which makes the Edit view work, but there is no corresponding conversion of the selected item (a string, aka object->ToString()) to the original list.
This seems so basic (MVC / EF4 / DropDownList) that I feel I'm missing something incredibly fundamental.
All thoughts appreciated.
Regards
Scott
Based on the following you can
http://forums.asp.net/t/1655622.aspx/1?MVC+3+Razor+DropDownListFor+and+Model+property+from+EFCodeFirst
Do the following:
[HttpPost]
public ActionResult Edit(Guid id, FormCollection collection)
{
CollectionViewModel cvc = new CollectionViewModel();
cvc.Collection = _db.Collections.Where(c => c.CollectionId == id).Include("CollectionType").First();
Guid collectionTypeId = Guid.Parse(collection["CollectionTypeId"].ToString());
cvc.Collection.CollectionType =_db.CollectionTypes.Where(ct =>ct.CollectionTypeId == collectionTypeId).First();
if (TryUpdateModel(cvc))
{
_db.SaveChanges();
return RedirectToAction("Index");
}
}
ViewModel
public class CollectionViewModel
{
public Collection Collection {get; set; }
public Guid CollectionTypeId { get; set; }
public SelectList CollectionTypes { get; set; }
}

Resources