ASP MVC4 + NHibernate objects in Session - c#

I need to save a hibernate object into session and then retrieve one of it's foreign key properties like this:
public ActionResult Login(LoginModel model, string returnUrl) {
User usr = _userRepository.GetById(Convert.ToInt32(ModelState["User"].Value.AttemptedValue));
Session["user"] = usr;
}
public ActionResult Index() {
Customer customerActive = Session["user"].Customer.Active;
// this line throws an error:
// Initializing[Myproj.Models.Customer#3]-Could not initialize proxy - no Session.
}
As User.Customer is a foreign key and NHIbernate lazy loads it, the call fails. How could I prevent this "No session" failure?

If you want to continue with existing approach you would want to make sure that Customer is initialized before it was put into the session, e.g.
var userId = Convert.ToInt32(ModelState["User"].Value.AttemptedValue);
User usr = _userRepository.GetById(userId);
NHibernateUtil.Initialize(usr.Customer);
Session["user"] = usr;
but...
as commentators have hinted there are probably various better approaches as the session is not a great place to store what might become large and complex objects which have to be serialized and stored remotely if you are in a web farm.
If passing the userId around and loading from the database each time is a performance hit for you there are several things you can do, e.g.
You could put some caching in front of the database call
Store some basic user data within a cookie (or local storage) to save any db hit
-

Related

How to pass fragile data e.g connection string from one controller to another

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.

variable set null after method

I'm building a web site, and I need to show a list of customers. When a customer is selected, to show his items, I send the id of the customer from the view with this:
<th>
#using (Html.BeginForm("SetID", "Client", FormMethod.Post, new
{ id = item.id.ToString() }))
{
#Html.Hidden("id", item.id.ToString());
<input type="submit" value="see items" />
}
</th>
I receive the id in the controller and save it to make the query and show the values this way.
private string customer_id
[HttpPost]
public ActionResult SetCustomer(string id) {
Save(id);
return RedirectToAction("Index");
}
private void Save(string id) {
this.customer_id = id;
}
But when I get redirected to the Index view, the variable "customer_id", is null. Is there something I'm missing?
Because you're not persisting the value anywhere.
HTTP is stateless. What this means in ASP.NET is that each HTTP requests results in a new instance of the controller being requested. So the following sequence of events is happening here:
User makes a request to SetCustomer, creating a new instance of the class
Value is stored in a class-level variable
Request is responded to and completed, the instance of the class is destroyed
User makes a request to Index, creating a new instance of the class
Since it's a new instance, no value is set. There are a variety of places you can store data, it just has to be in a context that both requests can access. Examples, in no particular order, include:
Database
Session
URL query string
Cookie
etc.
Basically, you have to write the value to some location which persists between requests. In-memory variables don't do that in web applications.
A simple example here could be to include the value on the query string in the redirect. In ASP.NET MVC, that might look something like this:
return RedirectToAction("Index", new { customer_id = id });
What this would do is include on a URL parameter a customer_id value when the user is redirected. So your Index action you could accept that parameter:
ActionResult Index(int? customer_id = null)
{
// use the customer id if one is provided
}
In this case I assumed that the value should be nullable in case the Index is ever requested without a value. But what you prefer to do for that is up to you. Basically this action now has an optional parameter, which you would use however you're currently trying to use it.
The benefit of this is that it maintains the intended statelessness of web applications. You're storing the state (the customer_id value) in the request/response itself, as opposed to some other medium (session, database, etc.) where you become responsible to maintaining it.
ASP.NET MVC controllers are instantiated to serve every request. Your local variable this.customer_id is not supposed to survive a call to the server. If you want to save something, it should go in a database table, or a file or somewhere persistent.
You could also store it in memory like in a cache, but that would need to be in a reference to something that lives longer than the controller (which will be thrown away after the server responds back to the browser).
If you want something like I described to be long lived, you might need to use a service to inject into your controllers. Many people would use a DI or IOC framework and life cycle configurations to accomplish this.

Maintain the model lifetime in MVC application

I am new to MVC and I have very simple problem.
When user login to my application I need to create a specific object (model) for the user for eg UserObject.
This object is unique to current logged in user and should only be disposed when user click on logout.
I don’t know how to maintain the lifetime of the object. As if I create object in Action method of controller class then as soon as the request is finished I lose the reference of the object.
How this should have been done?
The lifetime of your models are only going to be as long as the request. So each time the user goes to another page or refreshes, the MVC framework is going to instantiate a new controller (and model within). Otherwise your server would have a ton of static objects floating around in memory which would use up a lot of resources and wouldn't scale.
In order to manage state, you are going to need to use other methods such as sessions/cookies and a database.
So let's say the user logs in via /User/Login. This routes the request to an action named UserController.Login().
Inside this action, it instantiates a UserModel.
public ActionResult Login(string username, string password) {
var userModel = new UserModel();
if (userModel.Authenticate(username, password)) {
// Setup your session to maintain state
Session["username"] = username;
} else {
return View("Login");
}
return View("LoginComplete");
}
You might want the user model to actually create the session, but I've shown it here for clarity.
The user model authenticates the user, and then you create a session just like you would in a traditional non-MVC site.
Then in subsequent requests, you will want to authorize the user, and use any session data you have to retrieve state information.
public ActionResult SuperSecretPlace() {
var userModel = new UserModel();
string username = Session["username"]
var user = userModel.GetUserByUsername(username);
if (user == null) throw new HttpException(401, "User is not authorized.");
return View("SuperSecretPlace", user);
}
In the action above, the UserModel might do something like query a database to retrieve the user's data so you can pass it in to the corresponding view.
If you want to make life easier, you might want to just use .NET's built in forms authentication:
http://www.codeproject.com/Articles/578374/AplusBeginner-splusTutorialplusonplusCustomplusF
For more info about the lifecycle of MVC:
http://www.dotnet-tricks.com/Tutorial/mvc/TbR0041112-Asp.net-MVC-Request-Life-Cycle.html
http://www.asp.net/mvc/overview/getting-started/lifecycle-of-an-aspnet-mvc-5-application
Actually what you are trying to achieve is passing model from controller to controller which is not possible. When an action is executed the context of the model object is disposed at the view and it can cannot be passed from controller to controller. You have to create a new object repopulate it and use it to achieve the goal in different controller.If you need the data to be persisted you can use sessions but still you need to create an object of the model in every controller.
The following image is for your reference as to see what to use when passing data between model-view-controller. Please feel free to ask if you need more information on this.
As opposed to the other aswers I would not use session as it has quite some disadvantages (scalability, pessimistic concurrency which blocks concurrent calls, app pool recycling...). Why you should not use session is documented in a lot of places like here or here.
Instead, I would store it in a cookie.
However, be sure to not store confidential or sensitive data. Whatever you use (cookies or session), it can be tampered with or stolen. If you are dealing with sensitive information, you need other solutions. Read also more about secure cookie solution here.

Does MVC3 require a Model to be passed between views to maintain state?

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.

Pass complex object with redirect in ASP.NET MVC?

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.

Categories

Resources