Associate class instance to Session in asp.net core - c#

I'm currently working on a webserver in asp.net core.
I want the server to process the users input and data and am looking for a good solution to save complex Objects for the runtime.
So my first approach was to use Sessions. In Asp.net, sessions used to work like Session["key"] = new ValueObject()
In asp.net core however you can only use the methods SetString, SetInt32 and Set for byte arrays. I found a lot of solutions which basically converted the objects into Json strings. However in my case this isn't possible due to the objects containing other object references and more.
My second idea was to create a list of objects with the SessionId as identifier. Problem with this is that every time I would make request to the server, it needs to go through all existing Sessions to find the matching one, so this would probably drastically increase the time for the request.
So my question is what would be the best way to save user related objects?
Is using Sessions even the best way for solving this problem or am I missing something?
Note: Request are handled by JQuery AJAX, so reloading the page for accessing data is not an option.

You could try using the MemoryCache that can hold any .net type. It is not a problem but given it is a shared structure, it will be shared to all users, so, you have to carefull manage it. To do it, you could use HttpContext.Session.Id to define the keys on the memory cache instance. For sample (pseudo-code I didn't test):
public class HomeController : Controller
{
private IMemoryCache _cache;
public HomeController(IMemoryCache memoryCache)
{
_cache = memoryCache;
}
public async Task<IActionResult> CacheGetOrCreateAsynchronous()
{
string cacheKey = $"{HttpContext.Session.Id}_data";
var cacheEntry = await
_cache.GetOrCreateAsync(cacheKey , entry =>
{
entry.SlidingExpiration = TimeSpan.FromSeconds(3);
return Task.FromResult(DateTime.Now);
});
return View("Cache", cacheEntry);
}
}

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.

ASP.NET Core MVC - Adding element permanently to Dictionary in Action

I'm working on a ASP.NET Core MVC web app. I have a Model that includes a Dictionary. In one Action I'm adding a new element to it. Then I have other actions supposed to use the object from the Dictionary that was just added. But as it turns out - the dictionary is empty after the first action finished executing. Is there a way to fix it, so that the object is added permanently to the dictionary?
Update:
Well, the object I need to store is basically a virtual medical slide with a Deep Zoom tile generator. The flow is as follows: user click on the link to open the slide -> the ViewSlide Action creates the slide object -> then the OpenSeadragon viewer on the corresponding view sends requests to get XML metadata and JPEG tiles (256x256) on various Deep Zoom levels (based on mouse cursor position). So there's going to be a lot of requests for the tiles and I'm looking for a way to optimize the time needed to create them.
Here's a code snippet of the said actions:
[Route("[controller]/{slug}")]
public IActionResult ViewSlide(string slug)
{
try
{
var currentSlide = slideSet.Get(slug);
return View(currentSlide);
}
catch (Exception)
{
return RedirectToAction("Index");
}
}
public Slide Get(string slideUrl)
{
if (Slides.ContainsKey(slideUrl))
return Slides[slideUrl];
var pathToSlide = FilePaths[slideUrl];
Slides[slideUrl] = new Slide(pathToSlide);
return Slides[slideUrl];
}
[Produces("application/xml")]
[Route("[controller]/{slug}.dzi")]
public string Dzi(string slug)
{
try
{
return slideSet.Get(slug).DeepZoomGenerator.GetDziMetadataString(DEEPZOOM_FORMAT);
}
catch (Exception)
{
RedirectToAction("Index");
return "";
}
}
If you want to add the item permanently you can store it in:
Session (will not work in a web farm)
Cookie
Database
File
Here is how to store it in session:
// Place something in session
System.Web.HttpContext.Current.Session["whatever"] = value;
// Read from session
var whatever = System.Web.HttpContext.Current.Session["whatever"];
MVC also provides TempData which is basically a session which lives during the lifecycle of the trip on the server.
Depending on how you want to use this data, you have different options:
You can store it in the Session, Cookie, or TempData, if it's tied to the client, and no one else will need it. How long do you want to store the data? Cookies can be cleared, and you don't want to hold too much data in the Session either for a long time.
If the data does not belong to specific users, you can use a repository (e.g. singleton dictionary / database / HttpCache), but the first two needs to be cleaned regularly, while the HttpCache is not guaranteed to hold the data until it's requested.
And you could also rethink this concept, and stay stateless. This also makes it easier to scale your application horizontally, as well as adding HTTP cache, or even reverse proxy.
So basically it depends on what kind of data would you like to persist between action calls.

Keep unserializable object in application memory

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.

How do I associate some custom data with current HttpRequest?

I need to somehow attach my custom data to the HttpRequest being handled by my IIS custom modules - so that code that runs in earlier stages of IIS pipeline attaches an object and code that runs in later stages can retrieve the object and use it and no other functionality of IIS pipeline processing is altered by adding that object.
The data needs to persist within one HTTP request only - I don't need it to be stored between requests. I need it to be "reset" for each new request automatically - so that when a new request comes it doesn't contain objects my code attached to the previous request.
Looks like HttpContext.Items is the way to go, although MSDN description of its purpose is not very clear.
Is using HttpContext.Current.Items the way to solve my problem?
This should work - I have done this in a project before.
I have a class which has a static property like this -
public class AppManager
{
public static RequestObject RequestObject
{
get
{
if (HttpContext.Current.Items["RequestObject"] == null)
{
HttpContext.Current.Items["RequestObject"] = new RequestObject();
}
return (RequestObject)HttpContext.Current.Items["RequestObject"];
}
set { HttpContext.Current.Items["RequestObject"] = value; }
}
}
And then RequestObject contains all my custom data so then in my app I can do
AppManager.RequestObject.CustomProperty
So far I have not come across any issues in the way HttpContext.Items works.

How can I have a persistent data through different Context in a webservice?

So we have a webservice that is called from different applications and it runs an extraction of data which takes a while and we don't want it to run multiple times. So we thought we could set an HttpContext.Current.Application["isRunning"] to be persistent through all the requests like :
if ((bool)HttpContext.Current.Application["isRunning"])
And it doesn't work, since a new HttpContext is created when an other application call the webmethod.
Except writing onto the disk or in AppSettings I don't see how I can persist data through every request to only have one instance of my webmethod running at a time. I've tried with Application, Cache and static variables but they all do not persist across requests. It seems it creates a new instance each time.
Preventing a new instance to be created or persist data through instances would fix the issue. Any hint?
You could use EnterpriseLibraries Caching Block to cache the data following extraction.
http://www.codeproject.com/KB/web-cache/CachingApplicationBlock.aspx
Once you have the Enterprise Library assemblies referenced, it's just a case of adding a few lines to your web.config and then using code such as the following inside your service.
//Create Instance of CacheManager
ICacheManager _objCacheManager = CacheFactory.GetCacheManager();
AbsoluteTime timeToExpire = new AbsoluteTime(TimeSpan.FromMinutes(60));
MyData myData = null;
myData = (MyData)cacheManager.GetData("ref");
if (myData == null)
{
//get the data
cacheManager.Add("ref", myData, CacheItemPriority.Normal, null, timeToExpire);
}
return myData;
Take a look at the following links, which provide useful information on wanting to use the Singleton pattern with web services.
http://forums.asp.net/t/881617.aspx/1
http://social.msdn.microsoft.com/Forums/en-US/asmxandxml/thread/72274741-dbbe-4a64-a360-6bbe60026ec9/
http://msdn.microsoft.com/en-us/library/ff650316.aspx

Categories

Resources