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.
Related
this variable works fine if used by one user, but when used by two or more users then the "static" variable will be read by the next user, the first user instance when filling the gridview there are 5 rows of data and I try to access through other browser when entering the page, gridview on the second user already filled 5 rows of data in input by the first user. then how the solution to this problem? please see my code and give me an solutions. thanks.
static List<ServicesModels> _gridPackageDetail = new List<ServicesModels>();
private void AddListAction(string alfa, string beta)
{
ServicesModels data = new ServicesModels()
{
id_service_detail = Guid.NewGuid(),
scope_name = alfa,
detail_name= beta
};
_gridPackageDetail.Add(data);
}
public ActionResult GridPackageDetail()
{
ViewBag.DataListPackage = _gridPackageDetail.OrderBy(a => a.scope_name).ToList();
return PartialView();
}
my code in mvc3 controller.
The code is working fine, because this is what intended by "static", to have the same data for multi users. In your case you need to create a list or dictionary or multi-dimensional array (any data structure you are comfortABLE with) and save the data per use in it, and then retrieve the data when needed based on the user id.
static List<ServicesModels> _gridPackageDetail = new List<ServicesModels>();
private void AddListAction(string alfa, string beta)
{
ServicesModels data = new ServicesModels()
{
id_service_detail = Guid.NewGuid(),
scope_name = alfa,
detail_name= beta,
user_id = getTheID()// Get the id of the user
};
_gridPackageDetail.Add(data);
}
public ActionResult GridPackageDetail()
{
ViewBag.DataListPackage = _gridPackageDetail.OrderBy(a => a.scope_name && user_id ==getTheID()).ToList();
return PartialView();
}
replace getTheID() by your way of getting the id of the user.
This is used if you want to keep the data of all users. else you should remove the static keyword.
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 have a controller in ASP.NET like this:
public class FileUploadController : Controller
{
// ...
static List<ThreePartKey> uploadedFiles = new List<ThreePartKey> ();
// ...
public ActionResult Index ( )
{
// ...
}
[HttpPost]
public ActionResult Index (HttpPostedFileBase file,
string selectedOrgName,
string selectedCatName)
{
// ...
uploadedFiles.Add(new ThreePartKey {
orgname = selectedOrgName,
catname = selectedCatName,
filename = fileNameNoExtension });
ViewBag.uploadedFiles = uploadedFiles;
return View();
}
}
where the second Index function uploads a file that the user chooses and adds its information to a list of uploaded files. That list information is used to build out an HTML table. The reason I ended up prefixing List<ThreePartKey> uploadedFiles with static is because without it my list would only have the last uploaded file each time I invoked Index.
How long does that list stay in memory? I was hoping for it to correspond to user sessions, but I'm not sure.
Static variables are global to an AppDomain and last the life of the AppDomain. The value wouldn't be user-specific and would basically stick around until the web app restarts in the case of ASP.NET.
It's also worth noting that static variables aren't necessarily thread safe, so care should be taken when manipulating the variable. In your case, the List<ThreePartKey> is NOT inherently thread safe, so you should accommodate for that in your code (unless you change the implementation to use a session variable or something).
I'm looking to see if there is a way to eliminate one of the two calls that gets made to my method to google maps to calculate long/lat coordinates.
Here is my method.
public static GeocoderCoordinates GetCoordinates(string region)
{
WebRequest request = WebRequest.Create("http://maps.googleapis.com/maps/api/geocode/xml?sensor=false&address=" + HttpUtility.UrlEncode(region));
using (WebResponse response = request.GetResponse())
{
using (Stream stream = response.GetResponseStream())
{
XDocument document = XDocument.Load(new StreamReader(stream));
XElement longitudeElement = document.Descendants("lng").FirstOrDefault();
XElement latitudeElement = document.Descendants("lat").FirstOrDefault();
if (longitudeElement != null && latitudeElement != null)
{
return new GeocoderCoordinates
{
Longitude = Double.Parse(longitudeElement.Value, CultureInfo.InvariantCulture),
Latitude = Double.Parse(latitudeElement.Value, CultureInfo.InvariantCulture)
};
}
}
}
return null;
}
The first time I call this method it's for validation.
internal class ValidateLocationAttribute : ValidationAttribute
{
public override bool IsValid(object value)
{
var location = value as string;
GeocoderCoordinates coordinates = Geocoding.GetCoordinates(location);
if (coordinates == null)
return false;
return true;
}
}
and if there is no location that gets found it returns null - validation fails.
The second time it gets called is in the controller to set longitude/latitude coordinates within my entity.
[HttpPost]
public ActionResult Edit(EditStudentViewModel viewModel)
{
if (ModelState.IsValid)
{
Student student = studentRepository.Find(User.Identity.GetUserId());
if (student == null)
{
var newStudent = new Student
{
AspNetUserRefId = viewModel.AspNetUserRefId,
CatchPhrase = viewModel.CatchPhrase,
StartedPracticing = Convert.ToInt16(viewModel.SelectedYearId),
LocationPoints = Geocoding.GetDbGeography(viewModel.Location),
Location = viewModel.Location,
SO I'm running through this method twice just to insert/update a student. It seems a little redundant.
Isn't there a way to trigger/set validation state while the code in the controller is running, so I don't have to call this method twice (once for validation and once to set the actual values) when the user submits the form?
I thought about caching but don't think it's a good idea, unless someone can point out something.
If you think applying validation upfront using an attribute on the text box serve value to the user (early feedback), keep things as it is. Two calls is not too bad at all considering the value and cleanliness of the solution.
Second option is you can remove the attribute, and perform the validation in the controller action. If validation fails, display same form with all the same data but error message for the text box value (location). User will need to choose another location and then submit.
It is a trade off.
Important Tip: You can optimize your solution by storing region names in your DB and going to google API only if the region name does not present in your DB.
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);