ASP EF Increment / Decrement Value with + / - buttons - c#

So I'm new to ASP and EF and I am wondering how to do this incredibly basic operation, as well as a few questions to go along with doing it.
Currently I have a table we will call Resource;
class Resource
{
int current;
int min;
int max;
};
Right now I have the default CRUD options for this. What I would like is a + / - button on the main list that will manipulate the current value of each resource and update the value in the DB and on screen.
There are also certain operations I'd like to run such as "AddFive" to a selected group of resources.
So my questions;
How do I do this?
Is this scalable? If someone is constantly hitting the buttons this is obviously going to send a lot of requests to my DB. Is there any way to limit the impact of this?
What are my alternatives?
Thanks.
Edit:
To clarify the question; here are my post functions. How / where do I add these in my view to get a button showing for each resource. I just want the action to fire and refresh the value rather than redirect to a new page.
#Html.ActionLink("+", "Increment", new { id = item.ID })
public void Increment(int? id)
{
if (id != null)
{
Movie movie = db.Movies.Find(id);
if (movie != null)
{
Increment(movie);
}
}
}
[HttpPost, ActionName("Increment")]
[ValidateAntiForgeryToken]
public ActionResult Increment([Bind(Include = "ID,Title,ReleaseDate,Genre,Price")] Resource resource)
{
if ((resource.Current + 1) < (resource.Max))
resource.Current++;
return View(resource);
}

It sounds like the main issue you are having is creating a list of movies on the front end and updating the details for a specific one.
The key here is that you will need to either wrap a form around each item and have that posting to your update controller or use ajax / jquery to call the controller instead.
I have given you an example of the first one. Once the update controller is hit it will redirect to the listing page which will then present the updated list of movies.
Below is a minimal working example of how to wire this up. I've not included any data access code for brevity but have included psuedo code in the comments to show you where to place it.
Please let me know if you have any futher questions.
Controller
public class MoviesController : Controller
{
public ViewResult Index()
{
// Data access and mapping of domain to vm entities here.
var movieListModel = new MovieListModel();
return View(movieListModel);
}
public ActionResult Increment(IncrementMovieCountModel model)
{
// Put breakpoint here and you can check the value are correct
var incrementValue = model.IncrementValue;
var movieId = model.MovieId;
// Update movie using entity framework here
// var movie = db.Movies.Find(id);
// movie.Number = movie.Number + model.IncrementValue;
// db.Movies.Save(movie);
// Now we have updated the movie we can go back to the index to list them out with incremented number
return RedirectToAction("Index");
}
}
View
#model WebApplication1.Models.MovieListModel
#{
ViewBag.Title = "Index";
}
<h2>Some Movies</h2>
<table>
<tr>
<td>Id</td>
<td>Name</td>
<td>Increment Value</td>
<td></td>
</tr>
#foreach (var movie in Model.MovieList)
{
using (Html.BeginForm("Increment", "Movies", FormMethod.Post))
{
<tr>
<td>#movie.Id #Html.Hidden("MovieId", movie.Id)</td>
<td>#movie.Name</td>
<td>#Html.TextBox("IncrementValue", movie.IncrementValue)</td>
<td><input type="submit" name="Update Movie"/></td>
</tr>
}
}
</table>
Models
public class MovieListModel
{
public MovieListModel()
{
MovieList = new List<MovieModel> {new MovieModel{Id=1,Name = "Apocalypse Now",IncrementValue = 3}, new MovieModel {Id = 2,Name = "Three Lions", IncrementValue = 7} };
}
public List<MovieModel> MovieList { get; set; }
}
public class MovieModel
{
public int Id { get; set; }
public string Name { get; set; }
public int IncrementValue { get; set; }
}
public class IncrementMovieCountModel
{
public int IncrementValue { get; set; }
public int MovieId { get; set; }
}

Related

View providing the same value to Controller

I'm trying to write a simple numbers checking program. You enter a correct next digit - it shows you next one, if not - nothing happens. In the Controller I'm trying to: set a value of variable, increment it, pass to specific View (howManyDigits). I know that I can make howManyDigits a session variable but the point here is to understand why the number is still going back to eg. "2" which was entered in View1 just after the first run of the app.
PiController:
namespace PI.Controllers
{
public class PiController : Controller
{
[HttpGet]
public ActionResult View1()
{
return View();
}
[HttpPost]
public ActionResult View2(Pinumber number)
{
if (number.tabPi[number.howManyDigits] != number.numberEntered)
number.howManyDigits++;
return View(number);
}
}
}
View1:
#model PI.Models.Pinumber
#using (Html.BeginForm("View2", "Pi", FormMethod.Post))
{
#Html.TextBoxFor(number => number.howManyDigits,new { autofocus = "autofocus"})
<input type="submit" value="How many numbers do you know?" />
}
In this view I have BeginForm and i'm trying to pass this variable using HiddenFor and Lambda expression back to the same Controller.
View2:
#model PI.Models.Pinumber
<div>
3.#for (int i = 0; i < Model.howManyDigits; i++)
{
#Model.tabPi[i]
}
</div>
#using (Html.BeginForm("View2", "Pi", FormMethod.Post))
{
#Html.HiddenFor(x=> x.howManyDigits)
#Html.TextBoxFor(x => x.numberEntered, new { autofocus = "autofocus" })
}
Model class Pinumber:
namespace PI.Models
{
public class Pinumber
{
public char[] tabPi { get; set; } = new char[100000];
public Pinumber()
{
for (int i = 0; i < 20; i++)
{
tabPi[i] = piNumber[i];
}
}
public int howManyDigits { get; set; }
public char numberEntered { get; set; }
public string piNumber = "141592653589793238462"
Unfortunately it's going back to Controller with an old value which i entered in other View1 which is used just to display the start site with entry number of digits that you already know - [HttpGet].
Things are not working that way.
Look, if you increment an value of some int variable, from 1 to 2 let's say, and you pass it to view. Then you want to increment it from 2 to 3, you need to send that 2 from View(from HTML code) back to the backend, and increment it there, otherwise, value is lost.

Using RenderPartial to assign Model Property values

I am new to MVC platform and trying a simple application to get hold of the framework,
Application:I am designing an admin application which has a form to enter question and multiple options to the database, I have a viewmodel called "QuestionViewModel" which has properties "Quesiton" and a List of OptionsViewModel in it, "OptionViewModel" consists of "Option" and "IsRightAnswer" properties, so on the UI, I have a QuestionView which displays a textbox for Question and I want a question to have 4 options, so I created a PartialView called "QuestionOptionView" which takes "OptionViewModel" as its model,
now I loop about 4 times through the PartialView and display a TextBox for "Option" and "IsRightAsnwer" radiobutton,
The UI displays TetsBox for "Question" and 4 other "TextBoxes" for entering the "Options",
but when I Post this form, the "Options" does not bind to the ViewModel
How can I achieve Model Binding from these partialview data to the main viewmodel??
public class QuestionViewModel
{
[Display(Name = "Enter a question")]
public string Question { get; set; }
public IList<QuestionOptionViewModel> Options { get; set; }
public int MaxOptions { get { return 4; } }
public QuestionViewModel()
{
Options = new List<QuestionOptionViewModel>();
}
}
public class QuestionOptionViewModel
{
public string Option { get; set; }
public bool IsRightAnswer { get; set; }
}
In the viewm I have as below,
#using (Html.BeginRouteForm("savequestion", new {}, FormMethod.Post)){
<div>
#Html.LabelFor(m => m.Question)
#Html.TextBoxFor(m => m.Question)
</div>
<p>Add options for the question</p>
for (int i = 0; i < Model.MaxOptions; i++)
{
{ Html.RenderPartial("QuestionOption", new Babbi_Test_admin.Models.QuestionOptionViewModel()); }
}
<input type="submit" value="Save" />
When the form is submitted I have my post method as,
[HttpPost]
public void SaveQuestion(QuestionViewModel viewModel)
{
}
My viewModel in the post has "Options" as null
This is happening cause you must be binding the QuestionViewModel with the Action of your Controller instead, you need to create a ViewModel that will have the QuestionViewModel OptionViewModel
Error
[HttpPost]
public void SaveQuestion(QuestionViewModel viewModel)
{
}
In the Post Method you are expecting QuestionViewModel then how can you expect to get the OptionviewModel
Another thing your are Renbdering Partial View for QuestionOptionViewModel which Contains Options and RIght Answer so in the loop you will get the Right Answer 4 thing which is incorrect.
Sample
public class QuestionViewModel
{
//Properties
}
public class OptionViewModel
{
//Properties
}
public class QuestionAndOptionViewmodel
{
// QuestionViewModel and OptionViewModel model object
//Constructors
//Methods
}
[HttpPost]
public void SaveQuestion(QuestionAndOptionViewmodel viewModel)
{
//Your Code Here;
}
Try changing your code to:
for (int i = 0; i < Model.MaxOptions; i++)
{
{ Html.RenderPartial("QuestionOption", Model.Options.Item(i)); }
}
Or even better:
foreach(option in Model.Options)
{
Html.RenderPartial("QuestionOption", option);
}
You will need to initialize the option values in your Question Model constructor

ASP MVC4 - Pass List to view via view model

I have a model Person (with among other fields the day of Birth)
and I want to pass a list of all persons, together with the calculated age of each person, to the view
Therefor:
The view model
public class vm_PersonList
{
public Person Person { get; set; }
public int age { get; set; }
}
The controller action:
public ActionResult PersonList()
{
ViewBag.Message = "My List";
var list = new List<vm_PersonList>();
var list_p = new vm_PersonList();
foreach (var p in db.Person)
{
list_p.Person = p;
//the age will be calculated based on p.birthDay, not relevant for the
//current question
list_p.age = 23;
list.Add(list_p);
}
return View(list);
}
The view
#model List<programname.Viewmodels.vm_PersonList>
#foreach (var p in Model)
{
<tr>
<td>
#p.Person.FullName
</td>
<td>
#p.age
</td>
</tr>
}
The Person table contains for example 6 entries.
When debugging the application I see:
At the end of the controller action "list" contains correctly the 6 different Person entries
In the view, the "Model" contains 6 entries, but 6 times the last "database entry".
Does anyone have a suggestion to solve this issue?
You are using the same list_p instance over and over again inside the loop. So you are constantly updating its Person property. And since Person is a reference type you are modifying the same reference in memory. At the last iteration of the loop you are obviously replacing this reference with the last instance of Person which explains why you are seeing the same person in the view.
Try like this, seems lot easier:
public ActionResult PersonList()
{
ViewBag.Message = "My List";
var model = db.Person.Select(p => new vm_PersonList
{
Person = p,
age = 23
}).ToList();
return View(model);
}
You are working on the same instance of vm_PersonList. Move the instantiation of vm_PersonList into the loop
foreach (var p in db.Person)
{
var list_p = new vm_PersonList();
list_p.Person = p;
//the age will be calculated based on p.birthDay, not relevant for the
//current question
list_p.age = 23;
list.Add(list_p);
}
It's an issue with the scope of your list_p instance. Try changing your controller code to:
public ActionResult PersonList()
{
ViewBag.Message = "My List";
var list = db.Person
.Select(p => new vm_PersonList
{
Person = p,
age = 23,
})
.ToList();
return View(list);
}

Trying to pass a parameter but getting a "context" error

I am trying to pass this from my controller into my view (#ViewBag.Chapter7Total):
ViewBag.Chapter7Total = calc.CalculatePrice(quoteData, Chapter7);
But am getting a "doesn't exist in the current context error" in VS.
Basically, I am trying to pass in a second parameter which determines which pricing structure to use between 2. Chapter7 or Chapter13, with the selection determining the second parameter to perform calculations with.
Here are my methods:
class Chapter
{
public decimal PaymentPlan { get; set; }
public decimal Price { get; set; }
}
public decimal decPaymentPlan(QuoteData quoteData, Chapter chapter)
{
if (quoteData.StepFilingInformation.PaymentPlanRadioButton
== StepFilingInformation.PaymentPlan.No)
return PriceQuote.priceNoPaymentPlan;
else
return chapter.PaymentPlan;
}
public decimal Calculate(QuoteData quoteData, Chapter chapter)
{
decimal total = chapter.Price;
total += this.decPaymentPlan(quoteData, chapter);
return total;
}
static Chapter Chapter7 = new Chapter() { Price = 799.00m, PaymentPlan = 100.00m };
Finally, this is my controller:
public ActionResult EMailQuote()
{
Calculations calc = new Calculations();
Chapter chap = new Chapter();
QuoteData quoteData = new QuoteData
{
StepFilingInformation = new Models.StepFilingInformation
{
//just moking user input here temporarily to test out the UI
PaymentPlanRadioButton = Models.StepFilingInformation.PaymentPlan.Yes,
}
};
var total = calc.CalculatePrice(quoteData);
ViewBag.Chapter7Total = calc.CalculatePrice(quoteData, Chapter7);
return View(quoteData);
}
I'm not sure what to do to pass Chapter7. Any thoughts?
UPDATE 1:
This is my ViewModel (QuoteData):
public class QuoteData
{
public PriceQuote priceQuote;
public Calculations calculations;
public StepFilingInformation stepFilingInformation { get; set; }
public QuoteData()
{
PriceQuote = new PriceQuote();
Calculations = new Calculations();
}
}
I'm trying to figure out what you are doing here but I see that most importantly, you are sending quoteData to your View. I'm making a guess here but I figure QuoteData is a custom entity type of yours and not a ViewModel.
To start, I would create a QuoteDataViewModel in your models with all the properties of QuoteData that you need, including
public class QuoteDataViewModel {
... all of your quoteData properties here
public Chapter Chapter7 { get; set; }
}
In your EMailQuote action, something similar to this
public ActionResult EMailQuote() {
...
var model = new QuoteDataViewModel();
var quoteData = new QuoteData();
... // map your quoteData to your model with Automapper or manually like
... // model.SomeProperty = quoteData.SomeProperty;
... // repeat for all properties
model.Chapter7 = Chapter7;
return View(model);
}
If you are posting this data back you would need your Post action to accept the new QuoteDataViewModel
public ActionResult EmailQuote(QuoteDataViewModel model) {
if(ModelState.IsValid) {
....//save data that was entered?
}
return View(model);
}
Your view would then take a QuoteDateViewModel
#model QuoteDataViewModel
This is all just how I would do it personally, I don't quite understand what you have going on, for example, this line:
var total = calc.CalculatePrice(quoteData);
I don't see total ever being used after you create it.
Anyway, that's just a sample of how I'd do it, create a model specific to the view, include any and all properties I need, populate the model in the controller and send it to the view
Update
Based on the OP comment that quoteData is a ViewModel, then just as above, adding the new property to hold the extra data is simple, by adding ...
public decimal QuoteTotal { get; set; }
public Chapter Chapter7 { get; set; }
...to the ViewModel
the controller populates
var total = calc.CalculatePrice(quoteData);
model.QuoteTotal = total;
model.Chapter7 = new Chapter();
model.Chapter7 = Chapter7;
In the View the values can be accessed like:
#Html.DisplayFor(model => model.QuoteTotal)
#Html.DisplayFor(model => model.Chapter7.PaymentPlan)
#Html.DisplayFor(model => model.Chapter7.Price)

MVC3 Checkbox value won't clear

I have a fairly straightforward razor view which needs to be used for a questionnaire, which is used repeatedly until it runs out questions. Checkbox is used for select answers, the problem is when I first submit the form when the next set of questions returned the answers I submit in the first time are retained in the second page. For the simplicity I have reduced the program as follows,
Model,
public class CheckBoxItemDto
{
public int Id { get; set; }
public bool Selected { get; set; }
}
public class CheckBoxModel
{
public CheckBoxModel()
{
Dtos = new List<CheckBoxItemDto>();
}
public IList<CheckBoxItemDto> Dtos { get; set; }
}
Controller,
public class CheckBoxController : Controller
{
public ViewResult Index()
{
CheckBoxModel model = new CheckBoxModel();
for (int i = 0; i < 5; i++)
{
model.Dtos.Add(new CheckBoxItemDto(){Id = i,Selected = true});
}
return View(model);
}
[HttpPost]
public ViewResult Index(CheckBoxModel mdl)
{
CheckBoxModel model = new CheckBoxModel();
for (int i = 5; i < 10; i++)
{
model.Dtos.Add(new CheckBoxItemDto() { Id = i, Selected = i % 2 == 0 });
}
return View(model);
}
}
View,
#model CheckBoxTest.Models.CheckBoxModel
#{
ViewBag.Title = "ViewPage1";
}
<h2>ViewPage1</h2>
#using (Html.BeginForm())
{
for (int i = 0; i < Model.Dtos.Count; i++)
{
#Html.DisplayFor(m => m.Dtos[i].Id)
#Html.DisplayFor(m => m.Dtos[i].Selected)
#Html.CheckBoxFor(m => m.Dtos[i].Selected)
<br/>
}
<input type="submit" value="Index" />
}
In the view after post, DisplayFor and CheckBoxFor column values are different. But It should be the same.
By the sound of it your ModelState is still holding its previous values when you return the view. Try clearing the ModelState in your post action:
ModelState.Clear();
CheckBoxModel model = new CheckBoxModel();
...
In your Index action method that gets run for a POST, ASP.Net MVC is expecting the resulting View to display validation errors. Therefore, the HTML Helper method CheckBoxFor looks in the ModelState before the Model, so it can display the "invalid" value to the user.
If you're not using the View from a POST action to display errors, you can clear the ModelState as Dangerous suggests. Or, you could use the "Post-Redirect-Get" pattern: in your POST action method, display the same View if there are errors, or else redirect to a GET view that displays something else.

Categories