Hi I cant find the way to pass data between Action methods within one controller. The simplified code example is as follows:
public class HomeController : Controller
{
int latestID;
// GET: Home
public ActionResult Index()
{
latestID = 50000;
return View();
}
// GET: Generate
public ActionResult Generate()
{
// using the latestID here
return View();
}
}
Index loads when the app starts and latestID is set to 50000. But when I click button mapped to API GET request Generate, the latestID is suddenly null. I tried to implement TempData and Session but with no luck. Any help would be greatly appreciated!
I feel there are 2 possibilities here i.e. the value to be retrieved is for a user session or its global across all the users using the website.
If the value is user specific then it can be managed through session stage management like Viewbag. Or if this value is expected to be maintained on the server side alone then it needs to be retrieved again through some persistence mechanism like database or memory cache.
If the value is common across all the users using the Website then it can be achieved through the Dependency Injection of a singleton object (this can be a database manager or cache manager again or a simple in memory object). The static objects can be used as well but it wouldn't be ideal as the application wouldn't support horizontal scalability across instances.
Related
Im new to asp.net core and I was trying to develop an online SQL database manager, that will work on any SQL database, after passing: ServerAddress, Login, Password and DatabaseType (my ConnectionInformation model).
Something like SSMS but online.
I want to pass my ConnectionInformation model from Login controller to Database controller.
Redirecting to action uses query string which exposes all of my data.
TempData only accepts strings and converting my model to json isn't the most elegant way to solve this problem.
Login Controller:
public class LoginController : Controller
{
private readonly ILoginLogic _loginLogic;
public LoginController(ILoginLogic loginLogic)
{
_loginLogic = loginLogic;
}
[HttpGet]
public IActionResult Index()
{
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Index(ConnectionInformationViewModel connectionViewModel)
{
if (!ModelState.IsValid)
return View();
ConnectionInformation connection = Mapper.Mapper.ConnectionInformationMapper(connectionViewModel);
var connectionSuccess = _loginLogic.ConnectToDatabase(connection);
if (connectionSuccess)
return RedirectToAction("Index", "Database", connection);
else
return View(); // TODO: Return view with error or handle it in js
}
}
Database Controller:
public class DatabaseController : Controller
{
private readonly IDatabaseLogic _databaseLogic;
public DatabaseController(IDatabaseLogic databaseLogic)
{
_databaseLogic = databaseLogic;
}
public IActionResult Index(ConnectionInformation connection)
{
var databases = _databaseLogic.GetDatabases(connection);
return View(databases);
}
}
Prehaps my approach is totally wrong. My main goal is to check if I can connect to database, and if I can I want to perform query operations on that Database untill user logs out.
Well, simply, you have to persist the information some way. In that regard, there's a number of options. You could persist it in local storage on the client and actually pass it back with each further request. That works better for SPA-style apps, where you're pretty much doing everything via AJAX, though. Another technically client-side storage mechanism would be setting a session cookie with the posted connection information. Here I'm talking about explicitly setting and reading from a cookie with a "session" lifetime, not using sessions.
Or you can can actually use a true session, i.e. Session. TempData is basically just Session anyways, but here it would be inappropriate as you'd then need to ensure that the TempData is kept every time it's accessed or it won't survive the next request. If you're doing that, then you might as well just use Session and not have to worry about it.
Serialization is pretty much required no matter what you do. There's no way to persist an actual C# object instance, so you're either going to have to write it to a relational store like a database or serialize it to JSON.
One alternate solution, which avoids having to persist the actual connection information is to basically create your own connection pool. This will require a singleton-scoped class with a ConcurrentDictionary ivar and likely the use of SemaphoreSlim to lock during reads and writes of that dictionary so that you don't create and orphan connections. Then, you'd just need to assign the key to their particular connection in the dictionary client somehow, such as via Session or a cookie. This is actually a little more secure as well, as you're not persisting the database connection info past the initial post, but you might end up exhausting the available server connections if there's too many simultaneous users. Of course, that could potentially be an issue regardless. You'll also need some policy for eviction of connections. It's not the easiest setup.
Personally, I'd just stick with using Session on this one. It's secure enough, as long as you take the standard session hijacking prevention measures, and it's simple to implement. If you are building a SPA-style app, then I'd stick with local storage, as that's going to be imminently better keeping the info client-side to begin with, but it requires a bit more plumbing that way.
I am using Session variables to store information. It works fine until some file like web.config or IIS itself is changed from outside. In my case, it usually happens on doing Jenkins.
When that happens all the information stored in the session gets lost as a result I get logged out forcibly.
In order to cope this I am now planning to use Request.Form to hold all the required information instead. Therefore I want to ask the forum if Request.Form is safe in order to use it like Session Variables.
MVC is Model View Controller right? Not using session is not a good idea, neither request.form. The best approach to write MVC application is creating a Model according to your needs and 'posting' or 'getting' from your controller. It's even more testable, maintainable and extensible then sessions and request.from.
Just for giving an example;
public class YourController : Controller
{
private readonly IEmailMessengerService _emailMessageService;
private readonly IUnitOfWorkFactory _unitOfWorkFactory;
// dependency injection
public YourController
(IUnitOfWorkFactory unitOfWorkFactory,
IEmailMessengerService emailMessageService)
{
_unitOfWorkFactory = unitOfWorkFactory;
_emailMessageService = emailMessageService;
}
// GET: YourController
public ActionResult Index()
{
return View();
}
[HttpPost]
public ActionResult Index(YourViewModel model)
{
#region backend validation
if (
!GoogleReCAPTCHAHelper.Check(Request["g-recaptcha-response"],
ConfigurationManager.AppSettings["GoogleReCAPTCHASecret"]))
{
ModelState.AddModelError(string.Empty, "You have to confirm that you are not robot!");
return View(model);
}
// do your validation
#endregion
// do your needs here.
}
}
The question is, do you really need to use Session? If you really want to store a global data, you can consider cookies, localStorage or sessionStroge that store at the client side.
On my asp.net site I'm using an third party component for every user to provide data und subsume actions. Unfortunatly the component initialization is pretty slow (about 1-2 seconds) - way too long to initialize every page request.
So I'm trying to store this object in my application. But in any way it is null after the next page request. Simple objects are working as expected, of course.
I have tried so far
Static variables
HttpContext.Session
HttpContext.Cache
TempData
In all cases my application forgets everything, but I don't get any error.
Is there any way to achieve keeping those objects in memory?
Here is my minimal example. When I exchange the values to string everything works fine. With this object, I get null in Login method.
public class HomeController : Controller
{
public static IFrameworkItem Test;
// GET: Home
public ActionResult Index()
{
Test = FrameworkItem.Create("admin", "admin");
return View();
}
public ActionResult CreateItem(string username, string password)
{
var test = HomeController.Test;
return null;
}
}
Well, after many more hours of searching I found that this framework does logging on a file, which was not in App_Data. So I guess the ASP.NET website was restarted every time a file change was noticed.
A real bad issue, but now it is solved by forbidding this behavior.
I have 8 pages labeled Step0-Step7 that are used to save data incrementally to a model called dr405. After Step7, I need to display an Upload.cshtml that creates a folder based on DR405Profile.CurrentUser.TangiblePRopertyID from my custom Profile provider. So, as of right now I'm not posting anything from from Step7 to the Upload.cshtml. After Upload.cshtml I display an UploadSummary.cshtml that simply list the files located in the directory based on DR405Profile.CurrentUser.TangiblePRopertyID. Now, I have to take the user to a review page that displays the DB persisted data for the dr405 model. Does this mean I have to pass my model through Upload and UploadSummary as well even though those pages don't interact with the model?
My plan is to pass the ID as a hidden parameter from
step7 -> Upload -> UploadSummary -> Review(id) <--post accepts ID as parameter. I'm not sure if this is the best way.
Important point
I want to understand if I can do the same with the model
Step7(model) --> Upload(model) -->UploadSummary(model) -->Review(id Or Model)
public ActionResult Review(string id)
{
var service = new DR405Service();
var dr405 = db.dr405s.FirstOrDefault(d => d.TangiblePropertyId == id);
return View(dr405);
}
public ActionResult UploadSummary()
{
var saveLocation = Path.Combine(Server.MapPath("\\"), "returns\\" + DR405Profile.CurrentUser.TangiblePropertyId);
ViewData["files"] = Directory.GetFiles(saveLocation).ToList() ;
return View();
}
[HttpPost]
public ActionResult Upload(HttpPostedFileBase uploadfile)
{
var saveLocation = Path.Combine(Server.MapPath("\\"), "returns\\" + DR405Profile.CurrentUser.TangiblePropertyId);
System.IO.Directory.CreateDirectory(saveLocation);
uploadfile.SaveAs(Path.Combine(saveLocation, Path.GetFileName(uploadfile.FileName)));
ViewData["UploadStatus"] = String.Format("File name: {0}, {1}Kb Uploaded Successfully.", uploadfile.FileName, (int)uploadfile.ContentLength / 1024);
return View();
}
You have a few options to persist the data across requests.
You can use the MVC TempData feature. You can use the Peek/Keep feature of TempData to keep it around until you need to dispose of it (as by default once the data is accessed it is deleted).
Session would also work, but isn't recommended due to it being harder to test in a unit test.
This is perfectly fine. Taking in the ID of a database entry is standard practice. It requires less bandwidth, allows MVC to resolve the correct route/controller/action faster, and doesn't give bad guys as much surface are for sending malicious data to your server.
Hi,
I have a action that looks like this :
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Register(AdRegister adRegister, IEnumerable<HttpPostedFileBase> files)
The AdRegister is a complex class and I need to pass this in to a redirect method further down in the Register action, like this :
return this.RedirectToAction("Validate", adRegister);
The Validate action looks like this :
public ActionResult Validate(AdRegister adRegister)
I do know that I can pass simple parameters but in this case itĀ“s a complex object. This example do not work, the adRegisterĀ“s properties will be null.
Is this posible and if so, how?
BestRegards
More Information : Register action will take the adRegister and do som magic on it, then It will be sent to the Validate action. The Validate action will return a validation page to the user. When the user hit the grant button the adRgister will be filled from the form and then sent to the vValidate post where it will be saved. I have looked in to place the adRegister in cache or database temporarily but it will be better if I could simple pass it to the next action.
One possibility would be to pass the simple properties in the query string:
return RedirectToAction(
"Validate",
new {
foo = adRegister.Foo,
bar = adRegister.Bar,
... and so on for all the properties you want to send
}
);
Another possibility is to store it in TempData (for the lifetime of the redirect) or Session (for the lifetime of the ASP.NET session):
TempData["adRegister"] = adRegister;
return RedirectToAction("Validate");
and then retrieve it from TempData:
public ActionResult Validate()
{
adRegister = TempData["adRegister"] as AdRegister;
...
}
Yet another possibility (and the one I would recommend you) is to persist this object in the POST method in your datastore:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Register(AdRegister adRegister, IEnumerable<HttpPostedFileBase> files)
{
...
string id = Repository.Save(adRegister);
return RedirectToAction("Validate", new { id = adRegister.Id });
}
and then fetch it from the data store after you redirect:
public ActionResult Validate(string id)
{
AdRegister adRegister = Repository.Get(id);
...
}
an idea would probably create a session variable and pass around a Key that references that session variable if the object is required acorss a few views?
ASP.NET MVC's tempdata should be perfect for this.
That said, TempData or Session is one option, but has some downsides like being quite violate and oftentimes murky or difficult to debug. What might be preferable is to "stash" the temporary value in a persistent store, such as the user's profile or your own database, then pass a key through the validate method which can then load the data from said store. This also opens up the possibility of recovering abandoned carts and such.