Sharing model objects between controller actions in MVC3 - asp.net-mvc-3

I have two actions in a controller. One that displays a form for file upload and another one that displays the results of the upload.
I have created a POCO called FileInfo i.e
public class FileInfo
{
public string Name { get; set; }
public int Length { get; set; }
public string FileType { get; set; }
public string ErrorMessage { get; set; }
}
When I submit the form, the Upload action creates and populates the FileInfo object and then redirects to the second action called results. I want to be able to use the same file info object in the results action.
I am able to get around this using TemPData[], but it is limited since it only holds object data for a single request. I presume there must be a better way to share abjects between controller actions.Any help is appreciated!
// Upload Action
List<FileInfo> fileInfo= new List<FileInfo>();
//populate the fileInfo object using fi.Add()
if ((status.ToString() == "OK"))
{
TempData["Info"] = fileInfo;
return RedirectToAction("Results");
}
else
{
return RedirectToAction("Index");
}
//Results action.
public ActionResult Results()
{
List<FileInfo> fi = TempData["Info"] as List<FileInfo>;
if (fi != null)
{
return View(fi);
}
else
{
return View("Index");
}
}

If you need something to stick around longer then one subsequent request, you will have to put it in Session or in persistent storage (e.g. database).

Related

WebApi - Passing an Array of Values

I need to build an API using ASP.NET Web API (version 4.5.2). To get started, I'm just trying to create a basic endpoint that adds some numbers. In an attempt to do this, I've created:
[RoutePrefix("api/test")]
public class MyController : ApiController
{
[HttpGet]
public IEnumerable<int> Calulate(decimal[] op1, decimal[] op2)
{
var results = new List<Calculation>();
for (var i=0; i<op1.Length; i++)
{
var calculation = new Calculation();
calculation.Operand1 = op1[i];
calculation.Operand2 = op2[i];
calculation.Calculate();
results.Add(calculation);
}
return results;
}
public class Calculation
{
public int Operand1 { get; set; }
public int Operand2 { get; set; }
public int Result { get; set; }
public void Calculate()
{
this.Result = this.Operand1 + this.Operand2;
}
}
}
I am now trying to hit this endpoint via the Postman Chrome app. When I run it via Postman, I'm getting an error. Here is what I'm doing:
In Postman, I've put "http://localhost:50668/api/test/calculate" in the URL field next to the "GET" drop down. I then click "Send". I'm receiving the following error:
{
"Message": "An error has occurred.",
"ExceptionMessage": "Can't bind multiple parameters ('op1' and 'op2') to the request's content.",
"ExceptionType": "System.InvalidOperationException",
"StackTrace": "..."
}
I think (I don't know) the cause is because I'm not passing the values to the API from Postman correctly. However, I'm not sure how to do that. How do I pass an array of values to an API?
Short answer
To send arrays of decimals, WebApi expects url signature like:
GET http://localhost:50668/api/test/calculate?Operand1=1.0&Operand1=2.0&Operand2=3.0&Operand2=4.0
That url will send [1.0,2.0] as Operand1 and [3.0,4.0] as Operand2.
Long answer
By calling your api using GET http://localhost:50668/api/test/calculate, you actually send nothing to your server. (aside of headers content)
If you want to send data to your server, you have (at least) 2 options:
Option 2: Use GET method if operation is idempotent
Like William Xifaras already pointed out, specify that your inputs will come from the URL so WebApi interprets properly. To do so, use [FromUri].
[HttpGet]
[Route("calculate")]
public List<Calculation> CalculateWithGet([FromUri]decimal[] Operand1, [FromUri]decimal[] Operand2)
{
var results = new List<Calculation>();
for (var i = 0; i < Operand1.Length; i++)
{
var calculation = new Calculation();
calculation.Operand1 = Operand1[i];
calculation.Operand2 = Operand2[i];
calculation.Calculate();
results.Add(calculation);
}
return results;
}
public class Calculation
{
public decimal Operand1 { get; set; }
public decimal Operand2 { get; set; }
public decimal Result { get; set; }
public void Calculate()
{
Result = this.Operand1 + this.Operand2;
}
}
With a REST client, it should look like:
With GET, data is sent via the URL
Note that if you use GET Method, the server will expect to receive inputs from the URL. You should therefore send queries like:
GET http://localhost:50668/api/test/calculate?op1=1.0&op1=2.0&op2=3.0&op2=4.0
Use POST method if operation is not idempotent
Since the operation does some server side calculation, I pretend it may not always be idempotent. If it is the case, POST might be more appropriate.
[HttpPost]
[Route("calculate")]
public List<Calculation> CalculateWithPost(CalculationInputs inputs)
{
var results = new List<Calculation>();
for (var i = 0; i < inputs.Operand2.Length; i++)
{
var calculation = new Calculation();
calculation.Operand1 = inputs.Operand1[i];
calculation.Operand2 = inputs.Operand2[i];
calculation.Calculate();
results.Add(calculation);
}
return results;
}
public class CalculationInputs
{
public decimal[] Operand1 { get; set; }
public decimal[] Operand2 { get; set; }
}
public class Calculation
{
public decimal Operand1 { get; set; }
public decimal Operand2 { get; set; }
public decimal Result { get; set; }
public void Calculate()
{
Result = this.Operand1 + this.Operand2;
}
}
With POST, data is sent via the body
With that structure, the server expects to receive inputs from the request body. WebApi will deserialize the body if it matches the signature of your function.
With a REST client, it should look like:
Sidenote
The nuget package used to get the SwaggerUI generated (printscreens) can be find here. Very useful to run adhoc tests on WebApis.
Add from [FromUri] before the parameter.
public IEnumerable<int> Calulate([FromUri] decimal[] op1, [FromUri] decimal[] op2)
To force Web API to read a complex type from the URI, add the
[FromUri] attribute to the parameter
http://www.asp.net/web-api/overview/formats-and-model-binding/parameter-binding-in-aspnet-web-api
I think you can pass as a JSON array
http://localhost:50668/api/test/calculate?op1=[1,2,3]&op2=[4,5,6]
Hope this helps

MVC3 entity update issue

We have two models Person and Address. We have created one combined model of these two models as shown below.
public class Trust_Person_Master
{
public Person_Master PersonMaster { get; set; }
public Address_Master AddressMaster { get; set; }
public Trust_Person_Master()
{
}
public Trust_Person_Master(Person_Master personMaster, Address_Master addressMaster)
{
PersonMaster = personMaster;
AddressMaster = addressMaster;
}
}
Now, we use this Trust_Person_Master model, to save person and address.
So following is the method to save and edit.
[HttpPost, Ajax(true)]
public JsonResult SaveTrust(Trust_Person_Master entity)
{
int nCurrPersonId = entity.PersonMaster.Person_ID;
if (entity.PersonMaster.Person_ID > 0)
{
var update = db.Person.Find(entity.PersonMaster.Person_ID);
if (ModelState.IsValid)
{
TryUpdateModel(update);
}
}
else
{
db.Person.Add(entity.PersonMaster);
}
db.SaveChanges();
HttpContext.Application["TrustPersonSearch"] = null;
return Json(new { person_id = entity.PersonMaster.Person_ID, location_id = entity.PersonMaster.Location_ID });
}
But my problem is that when I edit person, i.e. just field of person master say first name, then that it executes the code as required without giving any error. But does not reflect the changes.
You need to show the TryUpdateModel() method. How are you updating the model ?
By looking at the code you have posted everything seems ok and should not give any problems, however I would like to see the way you are updating your entity, the problem seems to lie there.
Debug as much as possible and come back here with your findings.

context.SaveChanges() works, but database is never updated? (MVC 3)

I've written a form in ASP.NET MVC3, and I can't get the entry to save the changes I make in the database, but while debugging, I noticed that the changes were reflected in the data context. I am experiencing no errors running this code. Let me know if you need more. Thanks!
Controller
[HttpPost]
public ActionResult Edit(Tool tool, FormCollection collection)
{
if (collection["Tool.Person.PersonID"] != "")
{
tool.Person= context.People.Find(
Convert.ToInt32(collection["Tool.Person.PersonID"])
);
}
if (collection["Tool.Company.CompanyID"] != "")
{
tool.Company = context.Companies.Find(
Convert.ToInt32(collection["Tool.Company.CompanyID"])
);
}
if (ModelState.IsValid)
{
context.Entry(tool).State = EntityState.Modified;
context.SaveChanges();
return RedirectToAction("Index");
}
return View(tool);
}
The first two if statements are checking to see if the user inputted a person or company, and the information is passed via the FormCollection. PersonID and CompanyID are primary keys for Person and Company, respectively. I went through the method line by line multiple times and achieve the same result - after context.SaveChanges();, the context reflects the changes, but the database entries remain null for both Person_PersonID and Company_CompanyID.
Try using a view model and accessing the database after the user submits the form.
This should get you well on your way.
ViewModel
using System.ComponentModel.DataAnnotations;
namespace Project.ViewModels
{
public class _tools
{
[Required(ErrorMessage="ToolID is required")]
public int32 ToolID{ get; set; } //whatever ID you use to retrieve the Tool from the database.
[Required(ErrorMessage="PersonID is required")]
public int32 PersonID{ get; set; }
[Required(ErrorMessage="CompanyID is required")]
public int32 CompanyID{ get; set; }
}
}
Controller Post
[HttpPost]
public ActionResult Edit(_tool viewModel)
{
if (ModelState.IsValid)
{
Tool tool = db.GetTool(viewModel.ToolID) //whatever method you use to get a current version of the row. You already do this before you send the data to the client, so just copy that code
tool.Person = viewModel.PersonID
tool.Company = viewModel.CompanyID
context.Entry(tool).State = EntityState.Modified;
context.SaveChanges();
return RedirectToAction("Index");
}
return View(tool);
}
View
#model = _tool
#using(Html.BeginForm("Edit", "ControllerNameHere", FormMethod.Post, null))
{
#Html.HiddenFor(model => model.ToolID)
#*Also add whatever inputs you use to get PersonID and CompanyID from the user.
Make sure to either use the #Html helpers or to give them names that correspond.
i.e. name one input PersonID and the other CompanyID*#
<input type="submit" value="Edit">
}

Two render bodies in layout page?

I understand that only 1 RenderBody can exist in the MVC3 layout page however I want to attempt to create another. Maybe I'm looking at it the wrong way... Ideally I want to add a testimonial section that pulls in from the DB and display 1 testimonial at a time and a different 1 for each page refresh or new page. What is the best way to go about this?
Controller
CategoryDBContext db = new CategoryDBContext();
public ActionResult Testimonial(int id)
{
TestimonialModel model = db.Testimonials.Find(id);
return View(model);
}
Model
public class TestimonialModel
{
public int ID { get; set; }
public int CategoryID { get; set; }
public string Data { get; set; }
}
public class CategoryDBContext : DbContext
{
public DbSet<TestimonialModel> Testimonials { get; set; }
}
The View is in a folder called CategoryData.
You need to be use:
Layout:
#RenderSection("Testimonial", false) #*false means that this section is not required*#
and in you View
#section Testimonial{
}
I would use #Html.Action()
Here is a great blog post about using them: https://www.c-sharpcorner.com/article/html-action-and-html-renderaction-in-Asp-Net-mvc/
This would allow you to have a TestimonialController that can take in values, query for data and return a partial view.

MVC3 shared-search model confusion

(couldn't think of a better title, sorry)
So I've got my layout page, on this page there is a searchbar + options. Choosing whatever, should take you through to the search page, with the results etc. Fairly standard. What I've done to get this working is to create a MasterModel class, with a SearchDataModel class member on it. This SearchDataModel contains the various parameters for the search (search term, what fields to search on etc).
I've then strongly typed my layout page to the MasterModel class, and using a Html.BeginForm... I've constructed the search form for it. However all the checkboxes relating to the fields aren't checked by default, even though the default value for all the fields is true (via a private getter/setter setup).
Yet when I submit the form to the SearchController, all the checkboxes are set to true. So I'm a bit confused as to why it knows they should be true, yet not set the checkboxes to be checked?
Putting breakpoints in key places seems to show that the model isn't insantiated on the get requests, only the post to the Search controller?
I may be going about this all wrong, so if so, pointers as to the right way always appreciated.
public class MasterModel {
public SearchDataModel SearchModel { get; set; }
}
public class SearchDataModel{
private bool _OnTags = true;
private bool _OnManufacturers = true;
private bool _OnCountries = true;
[Display(Name= "Tags")]
public bool OnTags {
get { return _OnTags; }
set { _OnTags = value; }
}
[Display(Name= "Manufacturers")]
public bool OnManufacturers {
get { return _OnManufacturers; }
set { _OnManufacturers = value; }
}
[Display(Name= "Countries")]
public bool OnCountries {
get { return _OnCountries; }
set { _OnCountries = value; }
}
[Required]
[Display(Name="Search Term:")]
public string SearchTerm { get; set; }
}
Then in the _layout page:
#Html.CheckBoxFor(m => m.SearchModel.OnTags, new { #class="ddlCheckbox", #id="inpCheckboxTag" })
#Html.LabelFor(m =>m.SearchModel.OnTags)
Make sure you return a MasterModel with initialized SearchModel from your views:
public ActionResult Index()
{
var model = new MasterModel
{
SearchModel = new SearchDataModel()
};
return View(model);
}
Another possibility to implement this functionality than strongly typing your master layout to a view model is yo use Html.Action as shown by Phil Haack in his blog post.

Resources