MVC Best approaching reading data - c#

I was wondering what's the best approaching to read data in ASP.NET.
Method 1:
In the controller:
public ActionResult CatPerfomanceReportCreate(string dateStart, string dateEnd)
{
ViewBag.DateStart = dateStart;
ViewBag.DateEnd = dateEnd;
return View();
}
and the View:
#{
var readData = MyDataClass.GetData(ViewBag.DateStart, ViewBag.DateEnd)
}
..
..
..
<table>,,,,,<table>
Method 2:
In the controller:
public ActionResult CatPerfomanceReportCreate(string dateStart, string dateEnd)
{
ViewBag.Data = MyDataClass.GetData(dateStart, dateEnd);
return View();
}
and the View:
#{
List<MyDataClass> data = ViewBag.Data;
}
..
..
..
<table>,,,,,<table>
Method 1 read the heavy data in the View and Method 2 in the controller.
Thanks

Neither.
Method2 is slightly better than Method1 because it comply with the Dumb View method. Both of those method make use of the ViewBag dynamic which can but not intended to save information about your information you want to present to the user. Strongly Type elements in most cases are much preferred than dynamics and giving you the full force of the compiler behind it.
Let me suggest Method3 that is much better suited method for MVC. It is creating a Model of the data you want to use in your View and use it.
Model:
public class CatPerfomanceReport
{
public string DateStart { get; set; }
public string DateEnd { get; set; }
}
Controller:
public ActionResult CatPerfomanceReportCreate(string dateStart, string dateEnd)
{
var model = new CatPerfomanceReport
{
DateStart = dateStart,
DateEnd = dateEnd
};
return View(model);
}
Partial of View:
#model CatPerfomanceReport
<h1>Start: #Model.DateStart</h1>
<h1>End: #Model.DateEnd</h1>

Related

Passing a parameter back to a view after performing a form action?

I have a view that loads a record with a certain record number. Once the page is loaded, it gives the user an opportunity to login for additional information. Once the login logic is performed, I need to return to that same view with the same record number intact. I am passing the record number to the action using a hidden input in the form. What I can't seem to figure out is how to return to that same view and provide it with that record #. The code I am trying below is not working. I know this is MVC 101 stuff but a hint in the right direction would be appreciated, or feel free to scrap my method and suggest something better!
Form in view:
<form action="/MyView/Authenticate/#item.ID" method="post" enctype="multipart/form-data">
<input name="form_id" type="hidden" value="#item.ID">
.....
Form action:
[HttpPost]
public ActionResult Authenticate()
{
int myid = Convert.ToInt16(Request["form_id"]);
.....
return View("Index", new { id = myid } );
}
EDIT:
It turns out that the correct view is being returned, but it is expecting a model item type of "JobSummaryModel" per the Index action result below. So the question I actually need answered is, how do I pass both the record id and this view model to it?
public ActionResult Index(int id = 0)
{
List<JobSummaryModel> jdata;
ViewBag.IsResults = false;
if (id != 0)
{
ViewBag.IsResults = true;
}
jdata = db.Jobs.Where(c => c.ID == id).Select(c => new JobSummaryModel() { ID = c.ID, Name = c.Name, City = c.City, PostalCode = c.PostalCode, JobDescription = c.PositionDescription }).ToList();
return View(jdata);
}
EDIT:
Thanks Reddy, your suggestions worked! My only remaining issue is that when I return to my Index view from the Authenticate action, I do not seem to have my "jdata". Is my Index action result not being rerun when I return the Index view via my Authenticate action? I am coming from a web forms background where, in an instance like this, the Load/Init events would automatically run when a form is loaded. Do I need to bind my "jdata" in the Authenticate action and include it in the viewmodel?
EDIT: Resolved. Changed my "return View" to a "return RedirectToAction" to resolve my final issue. Thanks everyone!
Answer For your after Edit:
All you want to pass to view is a int Id and your List<JobSummaryModel> jdata right?
So create a ViewModel JObSummaryModelHelper
Public class JObSummaryModelHelper
{
public int Id {get;set;}
public List<JobSummaryModel> jdata {get;set;}
}
Now in your controller
public ActionResult Index(int id = 0)
{
JObSummaryModelHelper jobDetails = new JObSummaryModelHelper();
jobDetails.Id = id;
ViewBag.IsResults = false;
if (id != 0)
{
ViewBag.IsResults = true;
}
jobDetails .jdata = db.Jobs.Where(c => c.ID == id).Select(c => new JobSummaryModel() { ID = c.ID, Name = c.Name, City = c.City, PostalCode = c.PostalCode, JobDescription = c.PositionDescription }).ToList();
return View(jobDetails );
}
Now make sure your view is set to expect this new viewmodel
#model JObSummaryModelHelper
carry on with your manipulation......
You are better off creating a ViewModel for this like so:
Create a View Model class i.e.
public class AuthViewModel
{
public int MyId { get; set; }
}
In your View put the following directive at the top:
#model AuthViewModel
In your initial [HttpGet] method return the view model:
[HttpGet]
public ActionResult Authenticate()
{
var model = new AuthViewModel { MyId = 123 };
return View("Index", model );
}
It's best to use Html helpers in your view, so you can change it to this:
#using(Html.BeginForm()
{
#Html.HiddenFor(m => m.MyId)
...
}
The above uses naming conventions to post back to the action that you are on.
Then return it to your view like this:
[HttpPost]
public ActionResult Authenticate(AuthViewModel model)
{
int myid = model.MyId;
return View("Index", model );
}
Then you can output using this razor syntax #Model.MyId
It's really worth doing some tutorials to learn the conventions, a small amount of time invested in this will save you a lot of time in the future.
Instead of
return View("Index", new { id = myid } );
could you do
return Index(myid);

Create (not read) field values into a new view in C# MVC

I've looked, tried several different solutions and haven't found anything that works (at least, not something with an example close enough to what I want for me to follow). I'm sure I'm missing something that would be a simple thing to a more experienced coder. Help?
I have a Model called Residents. It includes ResidentID, PFName, PLName. I have a controller for Residents. I have CRUD views for Residents. All working just fine.
I have a Model called Logs. It includes LogID, ResidentID, Comments. I have a controller for Logs. I have CRUD views for Logs. All working just fine.
I can display all the log entries for a Resident. Works fine. After a Log entry has been created, I can display the PFName using the method
#Html.DisplayFor(model => model.Resident.PFName)
Next, I want to Create a new log entry for a selected Resident.
That's where I'm having the problem. I would like the "Create" view (for the Log) to display the ResidentFName and ResidentLName of the selected resident, not the ResidentID.
A this point, from the Details view for a Resident, I have a CreateLog link.
#Html.ActionLink("New Log Entry", "../Log/Create", new { #ResidentID = Model.ResidentID})
This (likely not the best way) gives me a URL with the value of the selected ID
http://localhost:999/Log/Create?ResidentID=1
The value for the ResidentID is correct; it changes depending on which Resident is selected.
This value is correctly entered
#Html.TextBoxFor(model => model.ResidentID)
on the new CreateLog page using the Log Controller Create action.
public ActionResult Create(int ResidentID)
I plan to hide the ResidentID TextBox so the user doesn't see it. It seems I have to make it available in the form to be able create a new log entry.
The CreateLog form currently works as I have it now. I can create a log entry and verify that entry has been correctly recorded for the Resident.
But, I would like the form to display the PFName and PLName for the Resident so the user has visible feedback for which Resident was selected.
I believe that the related data (PFName and PLName) I want has to be passed to the CreateLog form .... somehow. I can't get it from the form.
Since there's only the unsaved entry for ResidentID, I can't use the value from the CreateLog form it to display related data. As mentioned, for the Lists, there is no such problem. It's only for CreateLog.
I've tried adding the data to the URL. Not working. I've tried setting the strings in the Controller (and the URL). Not working. I've looked at setting a cookie, but haven't ever done that so not sure what to set or where to put it or how to get the values from it. I've looked at setting a variable in the controller ... (have that working to display drop down lists, but a list to select from is not what I need -- I want the matching values from the related table).
Log.LogID(PK, Identity)
Log.ResidentID(FK)
Resident.PFName
Resident.PLName
I can directly create a view with these tables/fields in my SQLDB and update it.
Assuming a view model which looks something like this:
public class CreateLogViewModel
{
public int ResidentID { get; set; }
public string PFName { get; set; }
public string PLName { get; set; }
public string SomeLogCreationProperty { get; set; }
// other properties
}
Your controller could look something like this:
public ActionResult Create(int ResidentID)
{
var model = db.Residents.Where(r => r.ResidentID == ResidentID)
.Select(r => new CreateLogViewModel
{
ResidentID = r.ResidentID,
PFName = r.PFName,
PLName = r.PLName
// other properties
});
return View(model);
}
Then the view:
#model CreateLogViewModel
#using (Html.BeginForm())
{
#Html.HiddenFor(m => m.ResidentID)
#Html.HiddenFor(m => m.PFName)
#Html.HiddenFor(m => m.PLName)
#Html.EditorFor(m => m.SomeLogCreationProperty)
// other properties
<input type="submit" />
}
This would then POST back to:
[HttpPost]
public ActionResult Create(CreateLogViewModel model)
{
if (ModelState.IsValid)
{
return RedirectToAction("Index");
}
// Redisplay the form with errors
return View(model);
}
Expanding on John H and StuartLC answers, you need to use ViewModels and the following workflow:
Database->(load)->Model->Controller->(convert)->ViewModel->View
and
View->ViewModel->Controller->(convert)->Model->(save)->Database
So lets says you have the following models:
namespace Models
{
public class Residents
{
public int ResidentID { get; set; }
public string PFName { get; set; }
public string PLName { get; set; }
//...
}
public class Logs
{
public int LogID { get; set; }
public int ResidentID { get; set; }
public string Comments { get; set; }
//...
}
}
You need a ViewModel that combines the data you need for display and input in your Log\CreateView:
namespace ViewModels
{
public class ResidentLog
{
public int ResidentID { get; set; }
public string PFName { get; set; }
public string PLName { get; set; }
public string Comments { get; set; }
//...
}
}
Then inside the controller:
public class LogController : Controller
{
[HttpGet]
public ActionResult Create(int ResidentID)
{
// Run in debug and make sure the residentID is the right one
// and the resident exists in the database
var resident = database.Residents.Find(residentID);
var model = new ViewModels.ResidentLog
{
ResidentID = resident.ResidentID,
PFName = resident.PFName,
PLName = resident.PLName,
Comments = string.Empty,
// ...
};
// Run in debug and make sure model is not null and of type ResidentLog
// and has the PFName and PLName
return View(model);
}
[HttpPost]
public ActionResult Create(ViewModels.ResidentLog model)
{
if (!ModelState.IsValid)
return View(model);
var log = new Models.Logs
{
// Assumes LogID gets assigned by database?
ResidentID = model.ResidentID,
Comments = model.Comments,
};
// Run in debug and make sure log has all required fields to save
database.Logs.Add(log);
database.SaveChanges();
return RedirectToAction("Index"); // Or anywhere you want to redirect
}
}
Then your Log\CreateView:
#model ViewModels.ResidentLog
<!-- Display the values needed -->
<div>#Model.ResidentID - #Model.PFName - #Model.PLName</div>
#using (var form = Html.BeginForm(...))
{
<!-- This saves the values for the post, but in fact only ResidentID is actually used in the controller -->
#Html.HiddenFor(m => m.ResidentID)
#Html.HiddenFor(m => m.PFName)
#Html.HiddenFor(m => m.PLName)
#Html.EditorFor(m => m.Comments)
<input type="submit" />
}
You need to provide the additional information to the view.
This can be done in at least 2 ways
Use the ViewBag dynamic as a quick and dirty cheap and cheerful container to pass everything the view needs from the controller.
(preferred) Use a custom ViewModel with a tailor made class which holds everything the view needs. This is generally preferred as it is statically typed.
(I'm assuming that resident is already persisted in the database by the time the Log controller is called - you might need to fetch it elsewhere)
So, in your log controller, here's an example of using ViewBag:
[HttpGet]
public ActionResult Create(int residentID)
{
ViewBag.Resident = Db.Residents.Find(residentId);
return View();
}
You can then show the resident properties on the view by utilizing the ViewBag.
Edit
Yes, by persisted I meant in the Db - apologies about using unclear jargon.
Here's another example of ViewBag approach (the idea is to create a new Comment for another object):
Doing this the cheap + cheesy ViewModel way - in the HTTPGet Controller Create method:
public ActionResult Create(string objectType, int objectId)
{
// This is equivalent to youn fetching your resident and storing in ViewBag
ViewModel.Object = FetchSomeObject(objectType, objectId);
return View();
}
And in the View I use this (The ViewBag is accessible to Controller and View):
<title>#string.Format("Add new Comment for {0} {1}", ViewBag.Object.ObjectType, ViewBag.Object.Name);</title>
As you say, you will also need to do add a hidden for the ResidentId in your create log form
As per #JohnH's answer (+1), the BETTER way to do this (than using the magic ViewBag dynamic) is to create a custom ViewModel specifically for this screen. The ViewModel can either be reused both ways (GET: Controller => View and POST : Browser => Controller, or you even have separate ViewModels for the Get and Post legs.
With much thanks to all, I have it working. The final piece was telling the controller to return the model (nl). Here's the full spec for what's working:
I have created a ViewModel that includes
public class NewLog
{
public int ResidentID { get; set; }
public string PFName { get; set; }
public string PLName { get; set; }
public string Comment { get; set; }
// other properties
}
In the LogController,
public ActionResult Create(int ResidentID)
{
var resident = db.Residents.Find(ResidentID);
var nl = new NewLog
{
ResidentID = ResidentID,
PFName = resident.PFName,
PLName = resident.PLName,
Comment = string.Empty,
};
return View(nl);
}
In the Create.cshtml page,
#model My.Models.NewLog
The required ResidentID to be recorded with the new Log Entry
#Html.TextBoxFor(model => model.ResidentID, new {#Type = "Hidden"})
And the related, user-friendly display boxes for the person's name
#Html.DisplayFor(model => model.PFName)
#Html.DisplayFor(model => model.PLName)
And in the URL which is used to access the create page,
#Html.ActionLink("New Log Entry", "../Log/Create", new { #ResidentID = item.ResidentID, item.PFName, item.PLName})

MVC List Null on Postback

My Controller populates my Model with a list with strings that appear in a DropDownList in my View. When the view is posted back to my Controller, that list is suddenly null. Why is it null, and what happened to the list of strings I created?
The list was properly populated and shows up in the View. The remainder of the form elements DO properly post back. For example, selectedName has whatever name the user clicked on. The only thing that is not posting back is nameList.
Here is the relevant part of my model,
public class MyModel
{
[Display(Name = "Selected")]
public string selectedName{ get; set; }
[Display(Name = "Names")]
public List<string> nameList{ get; set; }
}
the relevant Get and Post parts of my Controller,
public class MyController: Controller
{
[HttpGet]
public ActionResult Index()
{
List<string> nameList= getNames();
MyModel model = new MyModel()
model.nameList= nameList;
// Now, model.nameList has a bunch of stuff in it
return View(model);
}
[HttpPost]
public ActionResult Index(MyModel model)
{
if(model.nameList== null)
{
cry();
postOnStackOverflow();
}
return View(model);
}
}
and the relevant part of my View (which is encapsulated inside of a form).
<p>
#Html.LabelFor(c => c.nameList):
#Html.DropDownListFor(c => c.selectedName, new SelectList(Model.nameList), new { onchange = "this.form.submit();" })
</p>
Only the value of the drop down list is posted when you post the form. I assume that your control in question is on a form.
I am not sure why you want to always return to the view you posted from, but you need to repopulate the list:
[HttpPost]
public ActionResult Index(MyModel model)
{
List<string> names = getNames();
model.nameList = names;
return View(model);
}
That is the expected behaviour considering what you have in your view. You need to reload the namelist collection property incase you are returning model to the same view again.
[HttpPost]
public ActionResult Index(MyModel model)
{
if(ModelState.IsValid)
{
// Save and redirect
}
//reload the collection again and return the model to the view
model.nameList=getNames();
return View(model);
}

ASP.NET MVC Passing DataTime to View a

I have a asp.net MVC4 web project it shows a list of production data for that day. I have added a datetime picker which allows the user to select a date that they want to show information for.
The problem i am having is i am not sure how to go about passing the information back to the view from the method i have inside the controller.
I have the date passing back to the controller. Inside the controller i am doing a LINQ statement that allows me to select only the production data for that day.
[HttpPost]
public ActionResult GetProductionDateInfo(string dp)
{
DateTime SelectedDate = Convert.ToDateTime(dp);
DateTime SelectedDateDayShiftStart = SelectedDate.AddHours(7);
DateTime SelectedDateDayShiftEnd = SelectedDate.AddHours(19);
var ProductionData =
from n in db.tbl_dppITHr
where n.ProductionHour >= SelectedDateDayShiftStart
where n.ProductionHour <= SelectedDateDayShiftEnd
select n;
return View();
I am looking to get the Var ProductionData passed back to the view so that display it inside a table.
You can return ProductionData directly to your View.
return View(productionData)
And then in your View you could have #model IEnumerable<Type>
However, a better practice would be to create a strongly typed ViewModel to hold the ProductionData and then return the following:
var model = new ProductionDataViewModel();
model.Load();
return View(model);
Where model a definition as follows:
public class ProductionDataViewModel {
public List<ProductionDataType> ProductionData { get; set; }
public void Load() {
ProductionData = from n in db.tbl_dppITHr
where n.ProductionHour >= SelectedDateDayShiftStart
where n.ProductionHour <= SelectedDateDayShiftEnd
select n;
}
}
Then in your view use the new strongly typed ViewModel:
#model ProductionDataViewModel
Use a model, something like:
public class ProductionDataModel
{
//put your properties in here
public List<ProductionData> Data { get; set; }
}
Then create/return it in your ActionResult:
var ProductionData =
from n in db.tbl_dppITHr
where n.ProductionHour >= SelectedDateDayShiftStart
where n.ProductionHour <= SelectedDateDayShiftEnd
select new ProductionData
{
//set properties here
};
var model = new ProductionDataModel
{
Data = ProductionData
};
return View(model);
Then in your view, set your model at the top:
#model ProductionDataModel
Your ProductionData variable should now be of type IEnumerbable<tbl_dppITHrRow>.
You can pass in the model from your controller using this code at the bottom of your action:
return View(ProductionData);
In your view, you can make this your model type by placing the following Razor code in your view's .cshtml file:
#model IEnumerbable<tbl_dppITHrRow>
Then, you can use your model in your view code:
#foreach(var row in Model) {
<div>#row.Value</div>
}
The problem here is that you are returning nothing to your view here return View(); this view just render view and no data will be passed to it.
if ProductionData is getting values then
return return View(ProductionData);
You can then use the values passed in the view.

Calling a method in the controller

I'm a newbie about ASP.NET MVC 3, but I have a simple question.
Is it possible to call a Controller method from an CSHTML (Razor) page?
Example:
xxxControl.cs:
public String Bla(TestModel pModel)
{
return ...
}
index.cshtml:
#Bla(Model) <-- Error
Thanks.
Update:
Thanks #Nathan. This isn't a good idea to do this on this way.
The goal is: I need some formatting string for a field of the Model. But where I put the code that return a formatting String in the case?
It is considered bad practice for a view to call methods located on a controller. Usually it is a controller action which populates a model and passes this model to the view. If you needed some formatting on this model you could write an HTML helper.
public static class HtmlExtensions
{
public static IHtmlString Bla(this HtmlHelper<TestModel> htmlHelper)
{
TestModel model = htmlHelper.ViewData.Model;
var value = string.Format("bla bla {0}", model.SomeProperty);
return MvcHtmlString.Create(value);
}
}
and in your view:
#Html.Bla()
That would make unit-testing your mvc site very difficult.
Are you needing a partial view maybe? (what are you actually trying to do?)
Yes it's possible.
#using Nop.Web.Controllers;
#
var _CatalogController = EngineContext.Current.Resolve<CatalogController>();
var _model = new ProductModel();
_model = _CatalogController.PrepareProductOverviewModel(p, true, true);
}
Set the method to public if it is private.
Even the services you can call in the same manner.
var _productService = EngineContext.Current.Resolve<IProductService>();
if (Model.SubCategories.Count > 0)
{
foreach (var SubCategories in Model.SubCategories)
{
int subcategoryid = SubCategories.Id;<br>
IPagedList<Product> _products = _productService.SearchProducts(subcategoryid,0, null, null, null, 0, string.Empty, false, 0,null,ProductSortingEnum.Position, 0, 4);
}
i++
}
Simply do it like this:
xxxControl.cs action method:
public ActionResult YourView(TestModel pModel) {
//pMomdel code here
ViewBag.BlaResult = Bla(pModel);
return View(pModel);
}
index.cshtml:
#ViewBag.BlaResult

Categories

Resources