Duplicates on runtime MVC4 Entity Framework - asp.net-mvc-4

I get this strange "error". When i run my app (edit action i.e update) i get duplicate values stored in my dB. If i use the debugger, step by step, its working (no duplicates)...
It's a Many-to-Many Relationship so don't bother the Courses NULL value in the images, just trying to figure things out...
All Help is appreciated!
[HttpPost]
public ActionResult Edit(CourseStudentViewModel model)
{
var course = db.Courses
.Where(c => c.Id == model.CourseId)
.Single();
if (ModelState.IsValid)
{
course.Name = model.CourseName;
course.Description = model.CourseDescription;
course.Students = model.Students;
if(course.Id != 0) {
db.Entry(course).State = System.Data.EntityState.Modified;
}
else {
db.Courses.Add(course);
}
db.SaveChanges();
return RedirectToAction("Index");
}
//modelstate not valid, display form
return View(model);
}
I get my viewModel back. All good.
My Old values from dB. I want to update this data. So everything is still good...
My old values are updated to my new Values. Great!
Ok everything works great IF I step with debugger like this. But if i run the app i will get duplicates.... Anyone?
New content:
My Edit-view
#model ValueInjecter.Web.Models.CourseStudentViewModel
#{
ViewBag.Title = "Edit"; }
Edit Course
#using (Html.BeginForm()) {
#Html.HiddenFor(c => Model.CourseId)
#Html.LabelFor(c => Model.CourseName)
#Html.EditorFor(c => Model.CourseName)
#Html.LabelFor(c => Model.CourseDescription)
#Html.EditorFor(c => Model.CourseDescription)
</div>
<hr />
<h2>Students</h2>
<div class="editor-field">
#for (int i = 0; i < Model.Students.Count(); i++)
{
<div style="border: dotted 1px; padding: 5px; margin: 10px;">
#Html.HiddenFor(s => s.Students[i].Id)
#Html.LabelFor(s => s.Students[i].Name[i + 1])
#Html.EditorFor(s => s.Students[i].Name)
</div>
}
</div>
<p>
Number of Students:
<b>#Html.DisplayFor(s => Model.StudentCount)</b>
</p>
<hr />
<p>
<input type="submit" value="Save" />
</p> }

It probably works in the debugger because by inspecting course.Students collection in a property watch window you trigger actually a second database query (after the query that loads the course) due to lazy loading of the Students collection. (Your Course.Students collection is most likely declared as virtual.) If you run without debugger no lazy loading occurs and course.Students stays empty.
You can force that the course.Students collection is always loaded by using eager loading instead of lazy loading (which also saves the second database roundtrip):
var course = db.Courses
.Include(c => c.Students)
.Where(c => c.Id == model.CourseId)
.Single();
Honestly I have no clue why your code works correctly with the loaded collection (in the debugger) and why it works at all. Assigning a complete detached collection like this: course.Students = model.Students, and then just setting the state of the parent to Modified is normally not enough to update a child collection.
But I see "ValueInjecter" in your screenshots. Maybe there is some automatic mapping magic happening that does (accidentally?) the right thing to get a working update.

Ok I finally figured out the solution of my problem. Of course much easier than first thought.
[HttpPost]
public ActionResult Edit(CourseStudentViewModel model)
{
if (ModelState.IsValid)
{
var course = db.Courses.Find(model.CourseId);
course.Name = model.CourseName;
course.Description = model.CourseDescription;
if(model.Students != null)
{
foreach (var item in model.Students)
{
db.Entry(item).State = System.Data.EntityState.Modified;
}
}
if(course.Id != 0) {
db.Entry(course).State = System.Data.EntityState.Modified;
}
else {
db.Courses.Add(course);
}
db.SaveChanges();
return RedirectToAction("Index");
}
//modelstate not valid, display form
return View(model);
}
Still using the view ive pasted above.

Related

Select dropdown value after post

I was hoping for some guidance on an issue I am having with preserving the value in a dropdownlist after post (razor)
I have a simple page:
#model testContingency.Models.ListByWardDD
#{
ViewBag.Title = "TestDropDowns";
}
<h2>TestDropDowns</h2>
<div>
#Html.DropDownList("HospModel", Model.Hospital, new { #onchange = "ChangeHospital(this.value)" })
#Html.DropDownList("WardModel", Model.Wards)
<script type="text/javascript">
function ChangeHospital(val) {
window.location.href = "/PatientListByWardDD/TestDropDowns?hospID=" + val;
}
</script>
</div>
here's the controller
public ActionResult TestDropDowns(int? hospID)
{
PASInpatientRepository pasRepo = new PASInpatientRepository();
var returnModel = new ListByWardDD();
var HospitalData = pasRepo.GetPatientHospitalsEnum();
returnModel.Hospital = pasRepo.GetHopspitalListItems(HospitalData);
var WardData = pasRepo .GetPatientWardsEnum(hospID);
returnModel.Wards = pasRepo.GetWardListItems(WardData);
ViewBag.HospSearch = hospID;
return View(returnModel);
}
In the controller PASInpatientRepository() communicates with a cache database. It passes back public IEnumerable < SelectListItem > GetHopspitalListItems. It calls stored procedures written within a cache database (same as sql stored procedures in essence). This is all working fine in its own crude way.
The issue I am having is that when I select the dropdownlist #Html.DropDownList("HospModel", Model.Hospital, new { #onchange = "ChangeHospital(this.value)" }) and the controller is called to refresh the Wards dropdown, I want to preserve the value I have selected in the hospital dropdown. I have tried a few different ways, but I admit, I'm a bit stuck. Most examples I found are for strongly typed.
As I mentioned, I'm new to MVC, but any advice on how to solve this issue, or suggestions on improving my code are greatly appreciated.
So I'm not sure what the Hospital property looks like but I'll make the assumption that each one has a unique ID.
Furthermore to bind the posted data to the view model you'll need to use forms in your view. To create the drop down list use the DropDownListFor-Helper. This way the data will be bound back to your Model after submitting the form.
So your view could look something like this
#model testContingency.Models.ListByWardDD
#{
ViewBag.Title = "TestDropDowns";
}
<h2>TestDropDowns</h2>
<div>
#using (Html.BeginForm("TestDropDowns", "YourController", FormMethod.Post))
{
#Html.DropDownListFor(x => x.HospitalID, Model.Hospital)
#Html.DropDownListFor(x => x.WardID, Model.Wards)
<input type="submit" value="send" />
}
</div>
Your ViewModel testContigency.Models.ListByWardDD must have at least the following properties
public class ListByWardDD {
public int HostpitalID { get;set; }
// the value of the SelectListItem-objects should be the hospital ID
public IEnumerable<SelectListItem> Hospital { get;set; }
public int WardID { get;set; }
// the value of the SelectListItem-objects should be the ward ID
public IEnumerable<SelectListItem> Wards { get;set; }
}
Once you post the form (for simplicity I added a button to send the form and left the javascript part out) the method TestDropDowns of your controller (which you need to fill in the BeginForm-Helper) will be called. That method expects expects an object of type ListByWardDD as a parameter and the framework will automatically populate the values for you.
[HttpPost]
public ActionResult TestDropDowns(ListByWardDD viewModel) {
// your code here, viewModel.HospitalID should contain the selected value
}
Note: After submitting the form the properties Hospital and Wards will be empty. If you need to display the form again, you need to repopulate those properties. Otherwise your dropdown lists are empty.
I tried my best to post valid code but I did not compile or test it.

asp.net Core View Component executed but markup not rendered

I have an ASP.NET Core 1.1 web application developed with VS.2017 and I decided to put some of the view functionality in a view component (have done others before).
This view component fetches a Dictionary collection of permissions associated to a user ID and displays them in a nice table. When I put it as part of the page (not VC) it works. But when I use it as a view component the component's HTML is never rendered.
I placed a breakpoint in the view component and it triggers, I see the View(granted) return statement return a populated list so up until there execution is as expected.
Then I placed a breakpoint in the ViewComponent's default.cshtml code section at the top the #{ some code here } and that breakpoint triggers as well, so the view component's Default.cshtml file is found. This Default.cshtml has some markup to render a table, therein within the table I have a #foreach() statement and when do a "Run to cursor" to that precise location -the loop that iterates- it triggers as well so it is iterating through the collection.
But after all that the main view looks as if the view component isn't there, none of the HTML found in the Default.cshtml is rendered even though it was found and executed. What am I missing here? so far my impression has been that VS.2017 (with all its updates) is not very stable.
Default.cshtml
#using ACME.AspNetCore.Permissions.Infrastructure.Authorization
#model Dictionary<Permission, string>
#{
ViewBag.UserName = "xxx";
Model.Add(Permission.Permission1, "test");
}
<h1>Component Output</h1>
<div class="well well-lg">
<table class="table table-hover">
<thead>
<tr>
<th>Permission</th>
<th>Description</th>
<th class="text-center">Status</th>
<th class="text-center">Revoke it!</th>
</tr>
</thead>
<tbody>
#foreach (var dictentry in Model)
{
<tr>
<td>#dictentry.Key.ToString()</td>
<td>#dictentry.Value</td>
<td class="text-center"><span class="glyphicon glyphicon-ok" style="color:green;"></span></td>
<td class="text-center"><a asp-action="RevokePermission" asp-route-id="#ViewBag.UserName" asp-route-pid="#dictentry.Key.ToString()"><span class="glyphicon glyphicon-thumbs-down" style="color:red;"></span></a></td>
</tr>
}
</tbody>
<tfoot><p class="alert alert-success"><span class="glyphicon glyphicon-eye-open"></span> Granted permissions</p></tfoot>
</table>
</div>
GrantedPermissionsViewComponent.cs
[ViewComponent(Name = "GrantedPermissions")]
public class GrantedPermissionsViewComponent : ViewComponent {
private readonly ApplicationDbContext _context;
public GrantedPermissionsViewComponent(ApplicationDbContext context) : base()
{
_context = context;
}
public async Task<IViewComponentResult> InvokeAsync(string emailOrUserId)
{
string id;
Guid UID;
if (Guid.TryParse(emailOrUserId, out UID))
{ // THE PARAMETER WAS A GUID, THUS A USER ID FROM IDENTITY DATABASE
id = emailOrUserId;
}
else
{ // THE PARAMETER IS ASSUMED TO BE AN EMAIL/USERNAME FROM WHICH WE CAN DERIVE THE USER ID
id = _context.Users.Where(u => u.Email == emailOrUserId.Trim()).Select(s => s.Id).FirstOrDefault();
}
Dictionary<Permission, string> granted = GetOwnerPermissions(id);
return View(granted);
}
private Dictionary<Permission, string> GetOwnerPermissions(string userId)
{
Dictionary<Permission, string> granted;
granted = _context.UserPermissions.Where(u => u.ApplicationUserId == userId)
.Select(t => new { t.Permission })
.AsEnumerable() // to clients memory
.Select(o => new KeyValuePair<Permission, string>(o.Permission, o.Permission.Description()))
.ToList()
.ToDictionary(x => x.Key, x => x.Value);
return granted;
}
}
So why on earth is it triggering on the component's code as well as on the component's view (default.cshtml) and yet it does not render the HTML code found therein?
Component invokation in the main view:
#{await Component.InvokeAsync<GrantedPermissionsViewComponent>(
new { emailOrUserId = ViewBag.UserName });
}
NOTE
The InvokeAsync is actually executing synchronously (per warning) because I could not find a way to have GetOwnerPermissions to await on anything... But that is not the problem.
The problem lies in how you are invoking the ViewComponent.
If you use #{ ... } it means you want to execute code and not render to output.
If you use parenthesis instead of brackets, the result gets rendered to output. #( ... )
In your case, you don't even need the parenthesis.
Try invoking it has following:
#await Component.InvokeAsync("GrantedPermissions", new { emailOrUserId = ViewBag.UserName })
More info here
try this, your mileage might vary. :)
#if (User.Identity.IsAuthenticated)
{
<div>User: #User.Identity.Name</div>
#(await Component.InvokeAsync("DefaultNavbar"));
#RenderBody()
}
else
{
<div> show public pages</div>
#RenderBody()
}

There is no ViewData item of type 'IEnumerable<SelectListItem>' that has the key 'BranchQuickChange'

I've been on this error for a while and the ienumerable object is blocking can someone pls help me the error is in the description.
HTML:
#model IEnumerable<DatabaseDAL.Models.WAGTripHdr>
<script type="text/javascript" src="~/Scripts/jquery-1.9.1.min.js"></script>
<script type="text/javascript">
$("select#BranchQuickChange").change(function () {
var branchName = $("select#BranchQuickChange option:selected").text();
alert(branchName);
window.location.href = '#Url.Action("QuickBranchChange", "TripSheets")?branchName=' + branchName;
});
</script>
<div class="row-fluid">
<div class="span4" style="margin-top: 15px">
#if (User.IsInRole("Administrator") || User.IsInRole("SuperUser"))
{
<strong>Quick Switch</strong> #Html.DropDownList("BranchQuickChange",ViewBag.CompanyList as SelectList)
}
</div>
Controller:
{
List<WAGBranch> listWagBranch = WAGBranchRepository.GetAllBranches(CompanyEnum.WAG).OrderBy(i => i.BRName).ToList();
List<string> listCompany = new List<string>();
foreach (WAGBranch branch in listWagBranch)
{
listCompany.Add(branch.BRName); // + " - " + branch.Branch);
}
//listCompany.Insert(0, "WAG HEAD OFFICE - WAG");
if ((string)selected == "") selected = null;
ViewBag.CompanyList = new SelectList(listCompany, selected);
}
Model:
[TableNameAttribute("WAGTripHdr")]
public class WAGTripHdr : SQLSelectUpdateInsertHelper
{
public string DebName { get; set; }
}
Waiting for some advices.
That error usually occurs when the collection passed to DropDownListFor is null. As a fallback, the helper tries to find the options in the ViewBag under a member named after the property, i.e. ViewBag.BranchQuickChange. When it fails to find anything usable there, as well, it gives up and you get the exception you reference.
That said, it appears you are in fact setting ViewBag.CompanyList in your action. Additionally, it is being set to a SelectList instance, so casting it back to SelectList in the view as you're doing should materialize the value. The only thing I can think of is that perhaps a different action than the one you've posted here is being loaded. In particular, if you have GET and POST versions of this action, make sure that both set ViewBag.CompanyList. It's possible you only added that line to one and not the other.

Multiple radio button groups in MVC 4 Razor

I need to have multiple radio button groups in my form like this:
I know it's simply done by specifying the same "name" html attribute for each group.
HOWEVER
MVC doesn't let you specify your own name attribute when using html helper like this:
#Html.RadioButtonFor(i => item.id, item.SelectedID, new { Name = item.OptServiceCatId })
Because it looks at each tag's "name" attribute (not "id") to map/bind the form to the model which the controller receives, etc.
Some said that specifying each with the same "GroupName" attribute will solve the problem, but it didn't work either.
So, is there any way which works ?
EDIT:
Here's my view (simplified):
#model Service_Provider.ViewModels.SelectOptServicesForSubServiceViewModel
#foreach (var cat in Model.OptServices)
{
//A piece of code & html here
#foreach (var item in cat.OptItems.Where(i => i.MultiSelect == false))
{
#Html.RadioButtonFor(i => item.id, item.SelectedID, new { GroupName = item.OptServiceCatId })
<br />
}
}
NOTE:
My model is a List<OptServices>:
public List<OptServices> Cats {get; set;}
And OptServices has a List of OptItems inside:
public class OptServices
{
//a few things
public List<OptItems> Items {get; set;}
}
all you need is to tie the group to a different item in your model
#Html.RadioButtonFor(x => x.Field1, "Milk")
#Html.RadioButtonFor(x => x.Field1, "Butter")
#Html.RadioButtonFor(x => x.Field2, "Water")
#Html.RadioButtonFor(x => x.Field2, "Beer")
Ok here's how I fixed this
My model is a list of categories. Each category contains a list of its subcategories.
with this in mind, every time in the foreach loop, each RadioButton will have its category's ID (which is unique) as its name attribue.
And I also used Html.RadioButton instead of Html.RadioButtonFor.
Here's the final 'working' pseudo-code:
#foreach (var cat in Model.Categories)
{
//A piece of code & html here
#foreach (var item in cat.SubCategories)
{
#Html.RadioButton(item.CategoryID.ToString(), item.ID)
}
}
The result is:
<input name="127" type="radio" value="110">
Please note that I HAVE NOT put all these radio button groups inside a form. And I don't know if this solution will still work properly in a form.
Thanks to all of the people who helped me solve this ;)
I fixed a similar issue building a RadioButtonFor with pairs of text/value from a SelectList. I used a ViewBag to send the SelectList to the View, but you can use data from model too. My web application is a Blog and I have to build a RadioButton with some types of articles when he is writing a new post.
The code below was simplyfied.
List<SelectListItem> items = new List<SelectListItem>();
Dictionary<string, string> dictionary = new Dictionary<string, string>();
dictionary.Add("Texto", "1");
dictionary.Add("Foto", "2");
dictionary.Add("Vídeo", "3");
foreach (KeyValuePair<string, string> pair in objBLL.GetTiposPost())
{
items.Add(new SelectListItem() { Text = pair.Key, Value = pair.Value, Selected = false });
}
ViewBag.TiposPost = new SelectList(items, "Value", "Text");
In the View, I used a foreach to build a radiobutton.
<div class="form-group">
<div class="col-sm-10">
#foreach (var item in (SelectList)ViewBag.TiposPost)
{
#Html.RadioButtonFor(model => model.IDTipoPost, item.Value, false)
<label class="control-label">#item.Text</label>
}
</div>
</div>
Notice that I used RadioButtonFor in order to catch the option value selected by user, in the Controler, after submit the form. I also had to put the item.Text outside the RadioButtonFor in order to show the text options.
Hope it's useful!
I was able to use the name attribute that you described in your example for the loop I am working on and it worked, perhaps because I created unique ids? I'm still considering whether I should switch to an editor template instead as mentioned in the links in another answer.
#Html.RadioButtonFor(modelItem => item.Answers.AnswerYesNo, "true", new {Name = item.Description.QuestionId, id = string.Format("CBY{0}", item.Description.QuestionId), onclick = "setDescriptionVisibility(this)" }) Yes
#Html.RadioButtonFor(modelItem => item.Answers.AnswerYesNo, "false", new { Name = item.Description.QuestionId, id = string.Format("CBN{0}", item.Description.QuestionId), onclick = "setDescriptionVisibility(this)" } ) No
You can use Dictonary to map
Assume Milk,Butter,Chesse are group A (ListA)
Water,Beer,Wine are group B
Dictonary<string,List<string>>) dataMap;
dataMap.add("A",ListA);
dataMap.add("B",ListB);
At View , you can foreach Keys in dataMap and process your action

ASP.NET MVC How to Use two Actionresults with Html.BeginForm?

I'm trying to do the same as this ASP.NET MVC Using two inputs with Html.BeginForm question describes but with enough difference that I don't really know hwo to apply it on my project:
I have a view that has 3 dropdownlists(profilelist, connected salarylist & not connected salarylist)
Looks like this:
<div class="row bgwhite">
#using (Html.BeginForm("GetConnectedSalaries", "KumaAdmin", FormMethod.Get, new { Id = "ProfileListForm" }))
{
<div class="four columns list list1">
#Html.DropDownList("Profiles", (SelectList) ViewBag.Profiles, "--Välj profilgrupp--",
new
{
//onchange = "$('#ProfileListForm')[0].submit();"
// Submits everytime a new element in the list is chosen
onchange = "document.getElementById('ProfileListForm').submit();"
})
</div>
}
#using (Html.BeginForm("Index", "KumaAdmin", FormMethod.Get, new { Id = "SalaryListForm" }))
{
<div class="four columns list list2" style="margin-top:-19px;">
#Html.DropDownList("Salaries", (SelectList) ViewBag.Salaries, "--Kopplade LöneGrupper--")
</div>
}
#using (Html.BeginForm("GetNOTConnectedSalaries", "KumaAdmin", FormMethod.Get, new { Id = "NotConSalaryListForm" }))
{
<div class="four columns list list2" style="margin-top:-19px;">
#Html.DropDownList("Salaries", (SelectList)ViewBag.NotConSalaries, "--Ej Kopplade LöneGrupper--")
<input style="float: left;" type="submit" value="Knyt" />
</div>
}
</div>
as you can see above when i change an element i the profile list i have script code that submits the form and calls the following actionresult that populates my "connected salarylist".
[HttpGet]
public ActionResult GetConnectedSalaries(int Profiles = -1)
{
Model.SalaryGroups = AdminManager.GetConnectedSalaries(Profiles);
ViewBag.Salaries = new SelectList(Model.SalaryGroups, "Id", "SalaryName", "Description");
return (Index());
}
What I wan't to do:
When I chose a element in the profilelist i would like to call 2 actionresults, the one that i have shown above AND a second one that will populare my third list that will contain "not connected salaries".
Second Actionresult:
public ActionResult GetNOTConnectedSalaries(int Profiles = -1)
{
Model.SalaryGroups = AdminManager.GetNOTConnectedSalaries(Profiles);
ViewBag.NotConSalaries = new SelectList(Model.NotConSalaryGroups, "Id", "SalaryName", "Description");
return (Index());
}
I don't want to do this with AJAX/JSON, strictly MVC.
I read the question that i linked above but did not know how to apply it to my project or if it is even possible to do the same.
If more info is needed ask and i will do my best to provide it.
Thank you!
I was so sure that the best way to do this was to have two actionresults that i was totaly blinded to the soloution that i could call both my db methods from the same actionresult and populate both of the lists.
Simple soloution:
[HttpGet]
public ActionResult GetSalaries(int Profiles = -1)
{
Model.SalaryGroups = AdminManager.GetConnectedSalaries(Profiles);
ViewBag.Salaries = new SelectList(Model.SalaryGroups, "Id", "SalaryName", "Description");
Model.NotConSalaryGroups = AdminManager.GetNOTConnectedSalaries(Profiles);
ViewBag.NotConSalaries = new SelectList(Model.NotConSalaryGroups, "Id", "SalaryName", "Description");
return (Index());
}
Sorry if I wasted your time:( but hopefully this will help others that attempt the same.
However if there is a way to do this in two actionresults then I will leave the question as open, would be interesting to see how it is done.