Where should calculations be done in MVC? - c#

I need to perform some conversion calculations on data I am pulling from an SQL server. It seems like the best place to do it is in the view while I am looping through the collection, but I am not sure if that is best practice or not. Also, I am not sure how to go about doing it in the view.
I am still relatively new to this and slowly learning, so I am not even sure of the syntax to accomplish what I am trying to do.
As always any and all help is greatly appreciated.
<div class="panel-group" id="accordion" role="tablist" aria-multiselectable="true">
#foreach (var item in Model.OR_OP)
{
<div class="panel panel-default">
#Html.Raw("<div class=\"panel-heading\" role=\"tab\" id=\"heading")#Html.DisplayFor(modelitem => item.NAME)#Html.Raw("\">")
<h4 class="panel-title">
#Html.Raw("<a data-toggle=\"collapse\" data-parent=\"#accordion\" href=\"#collapse")#Html.DisplayFor(modelitem => item.NAME)#Html.Raw("\" aria-expanded=\"true\" aria-controls=\"collapse")#Html.DisplayFor(modelitem => item.NAME)#Html.Raw("\">")
#Html.DisplayFor(modelitem => item.NAME)
-
#Html.DisplayFor(modelitem => item.DESCR)
#Html.Raw("</a>")
</h4>
<div>
<div class="btn-custom-start">Start</div>
</div>
<div>
<div class="btn-custom-stop">Stop</div>
</div>
#Html.Raw("</div>")
#Html.Raw("<div id=\"collapse")#Html.DisplayFor(modelitem => item.NAME)#Html.Raw("\" class=\"panel-collapse collapse collapse\" role=\"tabpanel\" aria-labelledby=\"heading")#Html.DisplayFor(modelitem => item.NAME)#Html.Raw("\">")
<div class="panel-body">
#Html.DisplayFor(modelitem => item.PWPLACE)
#Html.DisplayFor(modelitem => item.PPARTS)
#Math(item.PTE.Value/60) <------ this is where I would like to do the calculation.
#Html.DisplayFor(modelitem => item.PTE)
#Html.DisplayFor(modelitem => item.PTR)
</div>
#Html.Raw("</div> ")
</div>
}
</div>

Any logic type operations should really be done in the Controller. Then the result should be saved to a View Model by Controller. That Model should be used in the View so you can display the result of some sort of operation you performed on data. The idea is that the Controller changes the View Model and if appropriate the Entity Model.
The View Model in the Controller should be set based on your Entity Model if you have one. If you are using DDD, then a lot of the Controller's logic should sit in the Domain Models, the logic which is appropriate to the domain of course, e.g. Calculations.
So looks like you are using Entity Framework. If you read on the page that you linked at the top:
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated from a template.
//
// Manual changes to this file may cause unexpected behavior in your application.
// Manual changes to this file will be overwritten if the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
I think your models get generated from the Q-MakModel.Context.tt file. Make sure that the Context file contains those two properties, because every time EF regenerates the model, it will remove those properties if they are not present in the template. Another thing to consider, I wouldn't use an Entity Model in the UI View. I would use a custom View Model
Then in the Controller map the View Modelto the Entity Model and do a save. When you load the View, because of MVC the Controller loads first so you map the Entity Model to the View Model and return that to the view.
This will protect your Entity so you don't save garbage in the database.
In the View Model I would only put things which are relevant for that View.
I have added this into an answer just because it's too long to comment above.
Let me know how you get on, it's definitely EF deleting those 2 properties.

In this case you can do calculation in Model. I recommend you add a property in your Model class something like:
public PropertyName
{
get
{
return #Math(this.PTE.Value/60);
}
}

Related

Making an Editor Form for a complex object containing a list of complex objects

I'm working on a MVC project where I am trying to review a list of complex objects that has been processed by the controllers before submitting it to the database. Crucially to the problem are two components.
first, the view displays each of the objects with a checkbox to 'Keep This' (ie: this object is good for adding)
Second, the complex objects all come from a source - but there can be multiple sources for each list of complex objects. I also need a checkbox for each source that only is displayed once per source, that indicates a different flag (not keep, but if the source is complete or only partial)
Because this is about procedural generated maps, two things are:
List of Map Tiles (the list of complex objects) - with a checkbox for each if we Keep it to add to the database, or ignore it.
Map Identifier (The source) - with a checkbox for each run, that indicates if we know these are all the tiles for this run, or if its only a few.
How I have worked through this problem so far:
In my view, I used an EditorForModel when I wasn't worrying about differentiating the sources. This worked great for getting the checkboxes to come back in the Post
<form asp-controller="Process" asp-action="Keep" method="post">
<input type="submit" value="Submit Data" id="btnSubmit" />
<hr />
#Html.EditorForModel()
<input type="submit" value="Submit Data" id="btnSubmit" />
<hr />
</form>
with a template for the original model, which was just each tile of the map. This did not differentiate between Maps - I just passed all the tiles, no matter what map they were from, in a single list to the View. This worked great, and my Post Action separated out the the different maps for processing each map + its tiles individually.
The view was basically:
- Tile 1 CheckBox: Keep[]?
- Tile 2 CheckBox: Keep[]?
- Tile 3 CheckBox: Keep[]?
... and so on
with no care for what map they came from. The Map information was still there (it was on each tile object) I just processed it in the Post action to properly separate them into their maps.
But now, I want to add the ability to say if the list of tiles for a given map is complete or not. A single checkbox is my goal - something looking like this:
Map Identifier map1 CheckBox: Complete? []
- Tile 1 for map1 CheckBox: Keep[]?
- Tile 2 for map1 CheckBox: Keep[]?
- ... and so on
Map Identifier map2 CheckBox: Complete? []
-Tile 1 for map2 CheckBox: Keep[]?
- ... ect
I do not know how many tiles or maps there will be per process attempt.
So what I tried to do was create a new ViewModel that was:
public class MultipleMapIdentifiers
{
[Display(Name = "Map: ")]
public string MapIdentifier { get; set; }
public List<TileInformation> TileInformationList{ get; set; }
[Display(Name = "Full Run? ")]
public bool FullRun { get; set; }
}
I then, before returning the Review view, simply went through all the submitted tiles, got the distinct MapIdentifiers, and then looped through the tiles to find the ones with the same mapID and put them in a list together in this view object.
Then I created new views, with new Editor Templates.
At first, I just used a #foreach loop in the MultipleMapIdentifiers EditorTemplate to show each of the tiles. Shows them great. Returns a null list when the Post is called.
So second, I tried to call a Partial view for each item instead - still a Foreach loop in the MultipleMapsIdentifiers EditorTemplate, but it calls a template for the TileInformation that I had hopped would work properly for editor checkboxes. Remember - each TileInformation has it's own check box if we want to keep this tile or discard it as a bad/duplicate tile.
This too only returns a null List of Tiles to the Post Action.
Is there a way to do this, what I am hoping for? How can I get the list, with their checkboxes, returned intact to the Post Action so I can remove which ones we don't want to keep before adding to the database?
In case anyone is searching and lands here:
I ended adding a couple of [NotMapped] properties to the general model, including a bool first. If the first flag is true, then it displays the map identifier string and anything else, otherwise it just uses a hidden field in razor to push the data through for the rest of the model versions.
#if (Model.First)
{
<div class="row mb-3 bg-info rounded">
<div class="col-sm-2 font-weight-bold border-bottom border-light mb-2 align-items-center">
#Html.DisplayNameFor(a => a.MapIdentifier)
</div>
<div class="col-sm-7 font-italic text-truncate border-bottom border-light mb-2 align-items-center">
<div class="display-field">
#Html.DisplayFor(a => a.MapIdentifier)
</div>
<div class="editor-field">
#Html.HiddenFor(a => a.MapIdentifier)
#Html.ValidationMessageFor(a => a.MapIdentifier)
</div>
</div>
<div class="col-sm-3 border-bottom border-light mb-2">
<p class="text-center"><span>#Html.CheckBoxFor(a => a.FullRun)</span> <b>Was this a Complete Run?</b></p>
<p class="text-center"><span>#Html.CheckBoxFor(a => a.MapPointsRecorded)</span> <b>Recording Map Points?</b></p>
</div>
<div class="editor-field">
#Html.HiddenFor(a => a.First)
#Html.ValidationMessageFor(a => a.First)
</div>
</div>
}
else
{
<div class="editor-field">
#Html.HiddenFor(a => a.MapIdentifier)
#Html.ValidationMessageFor(a => a.MapIdentifier)
</div>
}
probably not the most elegant of solutions, but it works for now.

The Create View is redirecting to itself

I've been trying to Auto Increment my ID (BookId). All views are working(Edit,Delete,Details,Index), but the Create one is not working very well because it redirect to itself every time I try to add a new book.
In my Controller I have the following:
public ActionResult Create()
{
return View();
}
//
// POST: /Book/Create
[HttpPost]
public ActionResult Create(tbBooks tbbooks)
{
if (ModelState.IsValid)
{
db.tbBooks.Add(tbbooks);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(tbbooks);
}
And in the Create.cshtml:
#using (Html.BeginForm("Create","Book")){
#Html.ValidationSummary(true)
<fieldset>
<legend>tbBooks</legend>
<div class="editor-label">
#Html.HiddenFor(model => model.BookId)
</div>
<div class="editor-field">
#Html.HiddenFor(model => model.BookId)
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
I've already created the database with the table and the column "BookId" is set with the Identity Specification = Yes.
enter image description here
Any help you can give me I'll appreciate,
Regards.
Well, a lot of things are going on here:
First, you'd better specify in the Create method if it is a [HttpGet] (in the first method).
Second, your view has:
#Html.HiddenFor(model => model.BookId)
This doesn't make any sense, once your are not passing any value from the [HttpGet] method to your view.
Third, it's not usual to place a label for a hidden field. You usually don't want to show hidden fields.
Also, for the structure in your database, you'd probably need filling out the fields name, price, etc in your view. If your ID is auto-incremented, you probably don't need to handle it yourself, database will do that for you.
Finally, when you submit your form, what's probably happenning is that the if command below is false (you can place a breakpoint and check):
if (ModelState.IsValid)
So the next command executed is:
return View(tbbooks);
Which makes you return to the View itself. I'm surprised you are not getting any errors. I suggest you take a look in some tutorials and actually use the code scaffolded by Visual Studio, which already contains some great indicators on how things work.
Your ModelState.IsValid property is returning false and hence it is executing the Return View(tbbooks) line. That means your form did not provide all the required values / some values were not acceptable as per the data annotation definition.
To know which valiations failed, In your razor view,
Change #Html.ValidationSummary(true) to #Html.ValidationSummary()
and submit the Add form again. You will see the validation errors
The boolean parameter you passed to ValidationSummary overload tells the system to exclude property Errors. that is why you could not see them.
public static MvcHtmlString ValidationSummary(
this HtmlHelper htmlHelper,
bool excludePropertyErrors
)

How to prevent lazy loading in partial views, custom Display/Editor Templates?

In my ASP.NET MVC 4 web application, I have utilized partial views and custom display/editor templates to modularize the code. One example is a User.cshtml DisplayTemplate which takes User (an Entity object) and prints out their name and an icon to popup their directory info.
Views/Shared/DisplayTemplates/User.cshtml
#model MyApp.Domain.Entities.User
#if (Model != null) {
#Html.DisplayFor(m => m.DisplayName)
<span class="view-contact icon ui-icon ui-icon-contact" title="View Contact"></span>
#Html.HiddenFor(m => m.Identity)
}
I'm having the opposite problem most people seem have when I searched on the topic. I've noticed that when I use this template, lazy loading is triggered and so a query is sent to the DB to grab the data, but I don't want this to happen if I've already preloaded the data, especially in the case where I show a listing of users. In that case I made sure to use .Include("User") in my query and the info displays without issue or additional querying when I essentially write out the template's code in the view:
Simplified excerpt from Views/MyController/List.cshtml
...
#foreach (var item in Model) {
<tr>
<td class="alignl">
#item.User.DisplayName
<span class="view-contact icon ui-icon ui-icon-contact" title="View Contact"></span>
#Html.HiddenFor(m => item.User.Identity)
</td>
</tr>
}
...
If I replace those three lines with the call to template, each line queries the db.
#foreach (var item in Model) {
<tr>
<td class="alignl">
#Html.DisplayFor(i => item.User)
</td>
</tr>
}
How do I utilize this template without triggering a unnecessary query?
Based on #GertArnold 's suggestion I looked a little closer at the main query that grabs the collection of users and tried to see if I could disable lazy loading. Because I was using a generic repository pattern and unit of work, I wasn't able to specifically disable it just for that call so I ended up using the context directly and everything behaved properly without disabling needed -- this breaks my pattern, but I now have the benefit of being able to use projection and save my application from eagerly loading unnecessary data columns.
If anyone can point me down the right path for implementing projection in a generic repository, I'd be greatful, but I can live with this alternative solution for now.
This msdn article also helped: https://msdn.microsoft.com/en-us/data/jj574232.aspx

MVC EF - EditorFor adds changes to model, CheckboxFor does not

I'm developing a webapplication in MVC and encountered a problem.
When using
#Html.CheckBoxFor(m => m[i].IsChecked.Value)
and posting back to the controller. The objects does not get modified.
When i try using EditorFor the object gets modified but the checkbox is now a DropDownList. The solution for that issue is using CheckBoxFor so i am pretty stuck.
This is the part that handles it.
#model List<Models.intake_Answers>
#{var i =0;}
//Viewbag is used to work with 2 models.
#foreach (Models.intake item in ViewBag.Questions)
{
<tr>
<td class="question">#Resources.ResourceManager.GetString(item.Question_localized.ToString())</td>
//IMPORTANT PART
<td class="answer">#Html.EditorFor(m => m[i].IsChecked, new { #Value = true })</td>
#{i++;}
</tr>
}
Thanks In Advance
If EditorFor is working then maybe all you need is to create a custom EditorTemplate to display however you want if you don't like the default dropdown. One of the EditorFor overrides will also let you pass in a template name if the name doesn't match the object type as the examples show.
Here is another EditorTemplate example.

Generating views at runtime from a database table

I have a technical question as to how I can generate Multiviews (views control) using MVC framework where the views are getting generated dynamically (get details from the DB).
As per asp.net the generation of the views (control) will need to be placed in PreInit or Load events of the page. Need some technical guidance on how to go ahead.
Or is it good practice to use again the question is how to. Any other alternate solution is also welcome..
You should probably try something like this:
Generating ASP.NET MVC View Controls According to XML Configurations
The data source is XML, but that doesn't matter; you should be able to adapt the technique to a database table containing the view metadata.
The "meta-view" code for an editor form would look something like this:
#model DynamicControlsCreation.ViewModels.DefaultControlsViewModel
<p>
#using (Html.BeginForm())
{
for (int i = 0; i < Model.Controls.Count; i++)
{
<div>
#Html.HiddenFor(x => x.Controls[i].Type)
#Html.HiddenFor(x => x.Controls[i].Name)
#Html.EditorFor(x => x.Controls[i])
</div>
}
<input type="submit" value="Submit" />
}
</p>
Note that you don't get much "shape" when you generate a view in this manner; it's mostly useful for things like configuration forms. Although you can put metadata in the database like the locations of the controls on the page, by the time you do all that, you're probably better off just making conventional views.

Categories

Resources