I am trying to pass my model List< Models.Statement > statementList from one action to another but i am receiving null value in the 2nd controller. Please suggest what is wrong here. Even tried with:
return RedirectToAction("WriteInTemplate", new { statementList = statementList });
Please help.
public ActionResult SendPdfStatement(string InvoiceNumber)
{
try
{
InvoiceNumber = InvoiceNumber.Trim();
ObjectParameter[] parameters = new ObjectParameter[1];
parameters[0] = new ObjectParameter("InvoiceNumber", InvoiceNumber);
List<Models.Statement> statementList = new List<Models.Statement>();
statementList = _db.ExecuteFunction<Models.Statement>("uspInvoiceStatement", parameters).ToList<Models.Statement>();
//WriteInTemplate(statementList);
return RedirectToAction("WriteInTemplate", statementList );
}
catch (Exception e)
{
InvoiceSearchTool.Models.udtExceptionTable exception = new udtExceptionTable();
exception.MethodName = "SendPdfStatement";
exception.Exception = e.ToString();
exception.Date = DateTime.Now;
DYNAMICS_EXTEntities db = new DYNAMICS_EXTEntities();
db.AddToudtExceptionTables(exception);
db.SaveChanges();
return View("Error");
}
}
public ActionResult WriteInTemplate(List<Models.Statement> statementList)
{
try
{
string invoiceNumber = statementList.FirstOrDefault().Invoice.ToString().Trim();
...................snip..........
return RedirectToAction("CreateMessageWithAttachment", "email", invoiceNumber);
}
catch (Exception e)
{
InvoiceSearchTool.Models.udtExceptionTable exception = new udtExceptionTable();
exception.MethodName = "WriteInTemplate";
exception.Exception = e.ToString();
exception.Date = DateTime.Now;
DYNAMICS_EXTEntities db = new DYNAMICS_EXTEntities();
db.AddToudtExceptionTables(exception);
db.SaveChanges();
return View("Error");
}
}
Please take a look here to pass your Model
you are not passing "statementList" , instead you are passing new { statementList= statementList} just pass the model and you should be fine .
return RedirectToAction("WriteInTemplate", statementList);
Answer by sino
RedirectToAction() writes a redirect command to the browser, making it start a brand new request to WriteInTemplate(). Your model object is therefore lost.
Is WriteInTemplate() an independent action which will sometimes be responsible for an entire request from a user or a partial request from a view? If not, you should just call it as a regular method instead of using RedirectToAction().
This is because you had spefified wrong route parameters.
while thinking about this.. did you check that the data are not null?
you are using
return RedirectToAction("WriteInTemplate", statementList );
instead you should use
return RedirectToAction("WriteInTemplate","controllerName", new{"statementList"=stetementList});
see reference here
The way you call the RedirectToAction() method may not be your issue.
For me, the solutions presented above did not work because the RedirectToAction() method builds a RouteValueDictionary by using the .ToString() value of each property in the model. This will only work if all the properties in the model are simple properties and it fails if any properties are complex objects, lists, collections, etc.
because this method does not use recursion.
If for example, a model called MymodelOrganization contained a property List Employees, then that property would result in a query string value of
....&Employees=System.Collections.Generic.List'1[System.String]
and binding would fail, and you would end up (as was my case) with ... null
I had this problem, so I created a copy of my model containing only the elements of the form, stripping my Lists and passed that inside RedirectToAction().
Once on the other action method, I was able to re-assemble my Lists and added them to my Model before calling the last return. Good luck. Here is the idea in my code:
[HttpPost]
public ActionResult ItemSubmissionForm(CombinedModelContent membervalues)
{ ...
ItemSubmissionsDBFields aFieldsList = membervalues.FieldsList; //Stripping other objects
return RedirectToAction("ItemSubmissionConfirm", aFieldsList);
}
[HttpGet]
public ActionResult ItemSubmissionConfirm(ItemSubmissionsDBFields aFieldsList)
{ ...
List<SomeArea> SomeAreaitems = new List<SomeArea>();
SomeAreaitems.Add ...
CombinedModelContent copymembervalues = new CombinedModelContent();
copymembervalues.SomeCodeLists = SomeAreaitems;
copymembervalues.FieldsList = aFieldsList;
return View("SomeConfirmPage", copymembervalues);
Related
So I'm struggling to tackle the following problem:
I have a main view called ApiBroker, in this view there are several methods to do something with the input from this view.
For example:
[HttpPost]
public ActionResult AddApi(ApiRedirect model)
{
var data = model;
try
{
List<ApiRedirect> list = dbProducts.ApiRedirects.ToList();
int companyID = dbProducts.Companies.Where(x => x.CompanyName == model.Company.CompanyName).FirstOrDefault().CompanyID;
int mappingID = dbProducts.MappingNames.Where(x => x.Name == model.MappingName.Name).FirstOrDefault().MappingID;
ApiRedirect api = new ApiRedirect();
api.ApiName = model.ApiName;
api.CompanyID = companyID;
api.ApiURL2 = model.ApiURL2;
api.MappingID = mappingID;
api.ResponseType = model.ResponseType;
dbProducts.ApiRedirects.Add(api);
dbProducts.SaveChanges();
return View();
}
catch (Exception ex){
throw ex;
}
}
This view should return the main View(Index) but instead of doing that its trying to return the View "AddApi" which does not exist. Error:
With the code above, the data gets inserted into my database but its returning me a 500 error.
What I've tried:
I've tried returning my View hard coded like this: return View("~/Views/ApiBroker/Index.cshtml"); but this gives me an error in my WebGrid.
I've also tried using "Return View("Index")", however this is me the following error in my WebGrid:
I've also tried "return View("Index",YourModel);", this is giving me the following error:
Hope someone can help!
You can return a particular View if they belong to the same Controller like this:
return View("Index");
If the View belongs to a different Controller, then you would simply have to specify the name of the View and its folder name and call it like this:
return View("../ControllerName/Index");
return RedirectToAction("YourAction");
or
return RedirectToRoute("YourRouteName");
So I've got the following lines of code:
else
{
//if not found, call Gateway Add()
user.Id = await C3SDbContext.UserGateway.NextIdAsync(context);
user.CreatedById = modifier.CreatedById;
user.CreatedBy = modifier.CreatedBy;
user.DateCreated = DateTime.Now;
user.UserType = "G";
System.Diagnostics.Debug.WriteLine(user.UserType);
user.Status = UserStatus.NEW;
System.Diagnostics.Debug.WriteLine(user.UserType);
user.Uic = await C3SDbContext.UicGateway.GetUicByIdAsync(context, user.UicId);
System.Diagnostics.Debug.WriteLine(user.UserType);
user.Role = await C3SDbContext.RoleGateway.GetRoleByIdAsync(context, user.RoleId);
System.Diagnostics.Debug.WriteLine(user.UserType);
if (ModelState.IsValid)
{
userCheck = await C3SDbContext.UserGateway.AddNewGovernmentUserAsync(context, user, modifier);
}
else
{
System.Diagnostics.Debug.WriteLine(user.UserType);
ICollection<ModelState> ListValues = ModelState.Values;
List<object> Errors = new List<object>();
foreach (var item in ModelState.Values)
{
if (item.Errors.Count() > 0)
{
Errors.Add(item.Errors);
}
}
}
}
user is an instance of GovernmentUser.cs, which inherits from User.cs. UserType is a string property of User. When I run this, all the instances of "System.Diagnostic.Debug.WriteLine(user.UserType);" return "G" in the Output window. Heowever, the Errors list returns one item, telling me that UserType is null.
My questions are: what is going on here? How can they both come to different results, when executed at the same type, and how can I get it so that ModelState.IsValid == true?
ModelState checks the data that was posted to you in MVC. I don't know if this is in your controller or not, but if it is, then my guess is that the UserType wouldn't be on the original data that was posted. I don't think you can use that check after setting it server-side. It is a check on the data received from the client. If UserType isn't required on the client side, just remove that rule from the Dto. However, if you are uploading the actual Entity directly here and it is using the Required attribute that EF uses, then I would just save it like normal and let EF handle the valdiation instead of using ModelState.
I'm trying to send parameters with ActionAsPdf using the Rotativa Library (1.6.4), unfortunately, the function is called but the parameter trainee in it is always null.
Here's my code:
List<T_Trainee> trainee= new List<T_Trainee>();
foreach (int f in foo)
{
T_Trainee t = new T_Trainee();
t.email = (string)Session["Mail"];
t.phone = (string)Session["Phone"];
trainee.Add(t);
}
//code to get the PDF
ActionAsPdf pdf = new ActionAsPdf("Index", trainee) { FileName = "Bulletin.pdf" };
Trainee var is a list of object T_Trainee not null -> seen in debug:
//function that return the PDF
public ActionResult Index(List<T_Trainee> trainee)
{
ViewModelFoo vmc = new ViewModelFoo();
vmc.trainee = trainee;
return View(vmc);
}
When the function is call in debug mode, I can clearly see that the parameter "trainee" is null but I still don't understand why.
Can anyone help me? Thanks!
ActionAsPdf seems to be a deprecated function in the last version of Rotativa.
I changed it by ViewAsPdf and now it works. The difference between the two function is that you have to send directly the view model inside the Index method call with ViewAsPdf.
Here's my code, I hope that it will help someone :
Code to call the index and send the viewModel
ViewModelFoo vmc = new ViewModelFoo();
List<T_Trainee> trainees= new List<T_Trainee>();
foreach (int f in foo)
{
T_Trainee t = new T_Trainee();
t.email = (string)Session["Mail"];
t.phone = (string)Session["Phone"];
trainees.Add(t);
}
vmc.trainees = trainees;
//code to get the PDF
ViewAsPdf pdf = new ViewAsPdf("Index", vmc)
{
FileName = "File.pdf",
PageSize = Rotativa.Options.Size.A4,
PageMargins = { Left = 0, Right = 0 }
};
Index that generate the view
public ActionResult Index()
{
return View(vmc);
}
Is foo populated?
Try sample code...
List trainee= new List();
trainee.Add(new T_Trainee {email = "sample#email.com", phone = "555-1212"});
Does that work?
You can also try to bind Action to a model
public ActionResult Index(ViewModelFoo vmc)
The second parameter of ActionAsPdf() is type of RouteValueDictionary, which is a dictionary of key and value. You passed in a custom type thereby it converts it to null. It should work if you pass a RouteValueDictionary instead.
ViewAsPdf() receives an object parameter and treats it as a model for view binding that's why it works.
You can have a look at its source code here:
https://github.com/webgio/Rotativa/blob/master/Rotativa/ActionAsPdf.cs
public class MusicController : Controller
{
User currentUser;
public PartialViewResult UploadMusic()
{
return PartialView("_UploadMusic");
}
[HttpPost]
public ActionResult UploadMusic(List<HttpPostedFileBase> files)
{
EntityDBContext db = new EntityDBContext();
List<Song> uploadedSongs = new List<Song>();
foreach (var file in files)
{
if (file != null)
{
string songName = Path.GetFileName(file.FileName);
byte[] songAsBytes = new byte[file.ContentLength];
using (BinaryReader br = new BinaryReader(file.InputStream))
{
songAsBytes = br.ReadBytes(file.ContentLength);
}
//Save new record in database
Song song = new Song
{
SongName = songName,
SongBytes = songAsBytes
};
uploadedSongs.Add(song);
}
}
string userName = User.Identity.Name;
currentUser = db.Users.Where(x => x.Username == userName).First();
currentUser.UserSongs = uploadedSongs;
return ShowSongs(currentUser.UserSongs);
}
public ActionResult ShowSongs(List<Song> UserSongs)
{
return View("ShowSongs", UserSongs);
}
public ActionResult Publish()
{
EntityDBContext db = new EntityDBContext();
foreach (var song in currentUser.UserSongs)
{
if (song != null)
{
db.Songs.Add(song);
db.SaveChanges();
}
}
return View();
}
}
ShowSongs view:
#model List<Vidafo.Models.Song>
#Html.ActionLink("Publish", "Publish")
The Problem
So I declare currentUser at the top of the controller. I then assign a value to that with this line here currentUser.UserSongs = uploadedSongs; This works fine but when the code goes into Publish() currentUser.UserSongs is null.
I need to have access to currentUser.UserSongs in more than one action method after assigning a value but it seems that it resets to null when it enters another action.
Object state isn't maintained across requests, that's not how web applications work. Every time a request is sent to the server, a new instance of the controller object is created. So any instance-level values are new.
In order to persist information across requests you need to persist it somewhere. For something like a user context, session state is a common choice. You'll probably want to wrap it in a common provider interface so as to not couple your controllers to an HTTP context, but at its core storing in session is simple:
HttpContext.Current.Session["someKey"] = someValue;
(You could even re-fetch from the database with each request. It's slightly less performant, but very simple and robust.)
Don't count out the ASP.NET identity system for this, though. ASP.NET is pretty good at abstracting a lot of this for you. You're already using it here:
string userName = User.Identity.Name;
Then you use that value to get the user from the database. You could extend the identity system to store a custom user object which fits your needs. But that's a larger scope effort outside of this question.
For this you can make use of TempData i.e. store value in TempData dictionary. One problem here is MVC doesn't sore value of variable during postback i.e. during different action of same controller or calling another controller for this you can use temporary varialble TempData as suggested.
How can I do this in another way ?
public ActionResult SomeAction(int id)
{
try
{
var model = GetMyModel(id);
return View(model);
}
catch(Exception e)
{
var notFoundViewModel = new NotFoundViewModel { Some Properties };
return View("~/Views/Shared/NotFound.cshtml", notFoundViewModel);
}
}
Exception will be thrown for url Controller/SomeAction/NotFoundId. I hate to have in project something like: ~/Views/Shared/NotFound.cshtml.
I realize this question is a few years old, but I figured I would add to the accepted answer. Following CodeCaster's recommendation of using the standard "Error.cshtml" as the file (view) to act as your generic error page, I recommend you let the MVC framework do the rest of the work for you.
If you place the Error.cshtml file in the Shared folder in your MVC project, you do not need to explicitly specify the path to the view. You can rewrite your code like the following:
public ActionResult SomeAction(int id)
{
try
{
var model = getMyModel(id);
return View(model);
}
catch(Exception e)
{
var NotFoundViewModel = new NotFoundViewModel { Some Properties };
return View("Error", NotFoundViewModel);
}
}
In fact, I've noticed that if you supply an explicit path and are running the Visual Studio IIS Express on your local machine, it sometimes isn't able to find the file and displays the generic 404 message :(
You can return HttpNotFoundResult object as:
catch(Exception e)
{
return new HttpNotFoundResult();
}
or
catch(Exception e)
{
return HttpNotFound("ooops, there is no page like this :/");
}
Make it a "~/Views/Shared/Error.cshtml" that displays a generic error model with a title and a message?