SQL Server connection-specific variables - asp.net

I'm upgrading my application to use stored procedures rather than dynamic SQL as it is now. What I'd like to do is call some procedure, for example setUser(id), and then for that ID to be carried forward for the duration of the current connection. I have a UserVariables table which stores important data related to the current user (I'm not using the session to store this data as the session only lasts for so long; this way the users data is persisted across logins). I want to select data, such as the ID of the client they're currently viewing, without having to pass the user ID into each stored procedure. Call it laziness, if you like!
I've searched for this quite a bit, and looked at various articles, but they're all about either global variables (which we can't change) or something unrelated.
Basically what I want to do is set the user ID at the beginning of the page load (may even move this into the session_start method at some point) and then access this variable during all subsequent stored procedure calls or queries.
Edit: What I'm after is like when you set a variable at the beginning of an asp page (my application is written in good ol' classic asp) and you then use it throughout the page and any includes. Think of the asp page representing the connection and the includes representing the stored procedures, if you like.

I have found a suitable alternative to setting connection-specific variables. I have a function which takes the sproc name, an array of parameter names, an array of parameter values and a variable to return (i.e. a byref variable). I have modified this function so that, when the parameters are refreshed, it checks for a userid parameter and sets it automatically if it exists. It then returns the 'retval' parameter if it exists, otherwise sets the return variable to myCmd.execute.

Related

Asp.Net Core Session Variable Overridden

I have an Asp.Net Core application. I have used session to keep track of objects a user may access.
On one of the pages on the website, I have two lists that are loaded by JavaScript AJAX. When the IDs of the records for these lists are found, these IDs are then stored in a session variable as a serialized string (case from a list of ints).
These are 2 unique lists, with 2 unique session variables, lets call them List1 and List2.
So it ends up with something like:
List1 = [1,2,3,4,5]
List2 = [100,101,102,103]
The problem is, as these two are called at the same time or in very quick succession via JS, it seems like the Session server side is being overridden by whichever is stored last.
I am using HttpContext.Session.SetString to set these.
I have tried using a setTimeout of a second, before calling the second list and that makes everything work, but that's not the most elegant or reliable.
It seems as though the whole Session object is updated when the SetString is called and I end up losing the Session variable for one or another list.
Is there any way around this, or something I'm missing?
Thanks!
David

What should be best approach to keep data in memory temporarily(at user level & for reuse the data) in ASP.NET?

Currently I am using 'Session' to keep the datatables in memory.
But after doing few R&Ds, I came to know the it is not a good practice
e.g.
Session("Syllabus") = RegistartionLogic.GetSyllabusInfo(Session("StudentID"))
Requirement:
The items of dropdown will be different based on student-type.
The dropdown data will be fetched from DB and these controls are used
in more than one screen.
Multiple DB call is not preferred from different screens for same
data.
So I need to call only one DB call, keep the data in memory and
then read data from memory next time onward.
I tried with 'cache' as well, but the issue was "Cache is not unique to the user.The scope of the data caching is within the application domain unlike "session". Every user can able to access this objects".
Kindly help me out.
For your scenario, HttpContext.Current.Cache should work.
Yes cache is not unique to the user. But cache key can be made unique.
var studentId = GetStudentIdFromRequest();
var cacheKey = "SyllabusInfoCacheKey_" + studentId;
Then you can make use of the unique cachekey, to insert and later get values for the particular student.
Session is also at Application level. Every user has a ASP.NET_SessionId cookie that is sent from client side and used at the server side to store/retrieve values.
Note: For Session and Asp.Net Cache to work in a load balanced environment, load balancer should be sticky.

How to handle an exception in VB code if the stored procedure doesn't return one of the fields?

I have a VB function which calls a stored procedure to get 2 kinds of reports (actives users/inactive users). The stored procedure returns the data (columns) based on the report we choose. If we choose to get active users report, the sproc doesn't return an Inactive_Date for the users, but it will return the same field, Inactive_Date field for Inactive users report. I'm getting an error when I choose to get active users report, because the sproc doesn't return Inactive_Date field, but my VB.NET code is same for both active/inactive users reports. Here are some of the ways I tried to resolve, but no luck.
If IsDBNull(dr("Inactive_Date")) Then
Result.Inactive_Date = Nothing
Else
Result.Inactive_Date = SafeStr(dr("Inactive_Date"))
End If
and
If IsDBNull(dr("Inactive_Date")) Then
Result.Inactive_Date = DateTime.Now
Else
Result.Inactive_Date = SafeStr(dr("Inactive_Date"))
End If
You're getting the exception because you're basically trying to access a dictionary entry that doesn't exist. You have a few options.
Have your stored procedure for Active Users return Inactive_Date as null. This would allow you to use code you have posted in VB with no changes.
Create a function that checks if the datareader has that column before you attempt to access it, as in this answer: Check for column name in a SqlDataReader object - this would be akin to checking if the dictionary has the key first
Write two separate functions in your VB code - one for the active report and one for the inactive report. Or, add a parameter to your function to check if this is a request for an active user, and only try to access the "Inactive_Date" field if that parameter is set to true.
If it were up to me, I'd update the stored procedure (option 1). That would probably be the least amount of work, shouldn't hurt anything semantically (unless you have queries that SELECT * from the results of that procedure), and requires no other changes to your codebase. The only other problem I can see is confusion that might arise from having a function in your VB code that's used for both active and inactive users.
The second option will involve iterating through all the fields, and that just feels wrong to me (seems like it's asking for performance issues if used incorrectly). It's the least invasive though if you can't change the stored proc.
The third option probably creates more problems elsewhere in your code base. That said, you probably shouldn't have one function do two very different things - I'd avoid the parameterized version if you end up going down this route, and go for two separate functions.
From your code and the fact that your variable is named dr, I am going to assume that you are using a DataReader.
The DataReader class exposes the column names as indexed values.
Dim columnNames = Enumerable.Range(0, dr.FieldCount).Select(dr.GetName).ToList()
Will call the method dr.GetName(index) for all columns, returning a list of column names you can check using columnNames.Contains("yourColumnName")
There is also dr.GetOrdinal, but that will throw an exception if the column doesn't exist.

Efficeintly maintaining a cache of distinct items in a huge DB table

I have a very large (millions of rows) SQL table which represents name-value pairs (one columns for a name of a property, the other for it's value). On my ASP.NET web application I have to populate a control with the distinct values available in the name column. This set of values is usually not bigger than 100. Most likely around 20. Running the query
SELECT DISTINCT name FROM nameValueTable
can take a significant time on this large table (even with the proper indexing etc.). I especially don't want to pay this penalty every time I load this web control.
So caching this set of names should be the right answer. My question is, how to promptly update the set when there is a new name in the table. I looked into SQL 2005 Query Notification feature. But the table gets updated frequently, very seldom with an actual new distinct name field. The notifications will flow in all the time, and the web server will probably waste more time than it saved by setting this.
I would like to find a way to balance the time used to query the data, with the delay until the name set is updated.
Any ides on how to efficiently manage this cache?
A little normalization might help. Break out the property names into a new table, and FK back to the original table, using a int ID. you can display the new table to get the complete list, which will be really fast.
Figuring out your pattern of usage will help you come up with the right balance.
How often are new values added? are new values added always unique? is the table mostly updates? do deletes occur?
One approach may be to have a SQL Server insert trigger that will check the table cache to see if its key is there & if it's not add itself
Add a unique increasing sequence MySeq to your table. You may want to try and cluster on MySeq instead of your current primary key so that the DB can build a small set then sort it.
SELECT DISTINCT name FROM nameValueTable Where MySeq >= ?;
Set ? to the last time your cache has seen an update.
You will always have a lag between your cache and the DB so, if this is a problem you need to rethink the flow of the application. You could try making all requests flow through your cache/application if you manage the data:
requests --> cache --> db
If you're not allowed to change the actual structure of this huge table (for example, due to huge numbers of reports relying on it), you could create a holding table of these 20 values and query against that. Then, on the huge table, have a trigger that fires on an INSERT or UPDATE, checks to see if the new NAME value is in the holding table, and if not, adds it.
I don't know the specifics of .NET, but I would pass all the update requests through the cache. Are all the update requests done by your ASP.NET web application? Then you could make a Proxy object for your database and have all the requests directed to it. Taking into consideration that your database only has key-value pairs, it is easy to use a Map as a cache in the Proxy.
Specifically, in pseudocode, all the requests would be as following:
// the client invokes cache.get(key)
if(cacheMap.has(key)) {
return cacheMap.get(key);
} else {
cacheMap.put(key, dababase.retrieve(key));
}
// the client invokes cache.put(key, value)
cacheMap.put(key, value);
if(writeThrough) {
database.put(key, value);
}
Also, in the background you could have an Evictor thread which ensures that the cache does not grow to big in size. In your scenario, where you have a set of values frequently accessed, I would set an eviction strategy based on Time To Idle - if an item is idle for more than a set amount of time, it is evicted. This ensures that frequently accessed values remain in the cache. Also, if your cache is not write through, you need to have the evictor write to the database on eviction.
Hope it helps :)
-- Flaviu Cipcigan

Limiting records returned by a sqldatasource, using session variable in where clause

I have a web application which has a Sql Server database on the backend. As the site will be rolled out to different clients (or different virtual directories in IIS), each client derivative of the site will use the same backend.
There is an admin page where text on the site can be changed (enter text in a listview, and choose the page to select where that text will show up, and also you can see company-specific details in the other listviews. As this is a shared database, that means a client can see each other's data.
What I am trying to do is store the accountId (a guid returned from the database from login_authenticate), and stick this into session. I then retrieve this on the admin page, and I want to use this value (But it's always 0000-0000 etc), to limit the records returned in the listview.
Is there an example of this? Also, how can I set the default value (this is in the where clause of SqlDataSource), to programatically what the account id is (so I can give me all records = what the current accountid is, or perhaps, what the login is - this is stored in the account table).
Thanks
This is what I tried.
What I am confused about, though, is whether the where clause, when using a session object, is getting an object that I have written the code to retrieve from the session, or an object I have only added but not retrieved. I get the accountID when logging in (verified via stepping in - obviously - or the login will fail).
I will try again with storing the object in session # the login page when I have just retrieved the accountid variable, and then retrieve it on another page.
For some reason I keep getting 0s so I will look at this in my application.
It sounds like your method should be working. I would follow a debugging process:
Check that you are getting the accountID value from the database. Print it on screen immediately after retrieving the value for the first time.
If this is working, store the value in the Session and immediately retrieve it, and check that you are getting the value back.
Create 2 test pages, one where you set the Session variable and another where you retrieve it.
I know this seems really basic, but the failure is being introduced somewhere in the above 3 places. If you can find which step fails, you will be able to fix it.

Resources