Shared methods in ASP.NET sessions - asp.net

As a followup to the previous question I have asked "ASP.Net Architecture Specific to Shared/Static functions"
I am still struggling to understand the implications of using shared methods in ASP.NET.
So for example let us take the following code.
Public Shared Function GetCategoryByID(ByVal CategoryID As Guid) As Category
If Not CategoryID.Equals(Guid.Empty) Then
Dim res As New Category
Using sqlConn As New SqlConnection(My.Settings.ConnectionString)
sqlConn.Open()
Using dr As IDataReader = CategoryDataLayer.GetCategoryByIDQuery(sqlConn, CategoryID)
Return CType(BaseDataLayer.FillObject(res, dr), Category)
End Using
End Using
Else
Return Nothing
End If
End Function
Now I imagine client 1 connecting and running this method GetCategoryByID() from their session with the guid {A20E625F-2333-4742-BFD9-05BE7649222D}.
Let us now say that for example the process is about to execute the following line.
Using dr As IDataReader = CategoryDataLayer.GetCategoryByIDQuery(sqlConn, CategoryID)
At this point client 2 runs the same shared method but with the guid {6D806B82-FC7F-4124-AFB9-45E2689BC9F5}.
Does CategoryID not at this point become {6D806B82-FC7F-4124-AFB9-45E2689BC9F5} and therefor interfere with client 1 because now CategoryID has changed given that this is a shared method?
Could someone please clarify?
PS: I do apologize for what is essentially a duplicate post but in retrospect I don't feel the answer in the original post was clear enough (at least for me). Perhaps I wasn't specific enough either...

In your code sample, the variable res is a local variable of the shared method. No one, not even another shared method can touch that variable. It will exist for the lifetime of the method and then its gone. CategoryId is the exact same, its a local variable that cannot be touched from outside.
VB has another concept called Static which is very different than the C# version of static which can cause some confusion.

The only issue with Shared methods is shared state.
If you use a Shared field or Static variable, it will be shared across requests, and cause trouble.
However, a Shared method that doesn't use any external state or shared objects will work fine.

Even though the same method is being run, it is being run in two different contexts. Any variables local to that method (including the CategoryId parameter) are not shared.

Related

is static methods secure in asp.net

heys guys,
i have a website, which contains lots of db work to display data on page, so i have created a VB class which is public, under App_Code.
Now i have all the methods and functions under that class are Shared(Static), also i have a connection variable which is also static.
Client complains, that sometime there appears an error on the page, one of those error is Field Name does not belong to table Table, i dont understand, about this, as this is very rare, if there is no field with name, then this should occur everytime, one of my colleague says that there should not be Shared methods or functions... is this correct..
There is no "security" problem with a static method. Your colleague is confused. Whether or not the code you wrote should be static or instance methods depends on what exactly it does. But having them as static methods is not "dangerous."
I suggest you track down the query that is causing the problem because the method being static is certainly not the issue.
As far as your connection goes, I would not recommend keeping it as a static variable. I assume this is a SqlConnection, or something similar. In that case, if you keep it as a static variable, it is possible for the following to occur:
Your connection is never closed, even after you're done using it.
You will have issues if you have multiple queries trying to use the connection at the same time.
So I recommend you use the following pattern to ensure your connections are only kept open as long as they are in use.
public void DoSomething()
{
//Doing some work that doesn't need a connection.
//Now ready to submit or fetch data from the database.
using (SqlConnection connection = new SqlConnection(...))
{
using (SqlCommand command = new SqlCommand(..., connection))
{
//Now, working with the connection and command.
}
}
//Done with the connection, doing more work now.
}
The using statement works with anything that is IDisposable. Your connection variable here will be automatically closed and destroyed at the closing bracket of the using statement. I recommend you use it for anything that you can. Streams, SqlConnections, Fonts, etc.
It sounds to me like you have a infrequently-used SQL statement that refers to a column that does not exist on a table.
For example - suppose you had SQL like so
SELECT Col4 FROM Table2
and Col4 was not a member of Table2. You would get the error you describe.
If you're building SQL dynamically (which is dodgey) you might run into this.
But I don't think it has anything to do with your method 'security.'

Sql data reader in classes

Hi
On several pages of our website, I want to check if the currently logged in user has accepted our terms and conditions in the past. This boolean value is stored in the application database. Rather than creating a sql data reader afresh on each relevant page, I thought I could put it in a class and then assign the true/false to a variable. This is what I have so far and it does exactly what I want it to:
Public Shared ReadOnly Property termsCompleted As String
Get
Dim selectTerms As String = "SELECT Terms FROM tblPersonal WHERE Ref=#Ref"
Dim dbconn As String = ConfigurationManager.ConnectionStrings("ApplicationServices").ConnectionString
Using myConnection As New SqlConnection(dbconn)
myConnection.Open()
Dim cmdTerms As New SqlCommand(selectTerms, myConnection)
cmdTerms.Parameters.AddWithValue("#Ref", myUser.ToString())
Dim readerTerms As SqlDataReader = cmdTerms.ExecuteReader()
readerTerms.Read()
termsCompleted = readerTerms.Item(0)
readerTerms.Close()
myConnection.Close()
End Using
End Get
End Property
I am them using the following on each page that is relevant to deny access and redirect (in the page_load):
If Variables.termsCompleted = False Then
Response.Redirect("Terms.aspx")
End If
While this works ok, i'm interested in how secure it is, and is there a better way to do this?
Thanks
Have you considered retrieving the information once during Session_Start, and carrying it around in Session so that you can interrogate it any time you want?
If you can't retrieve the data during authentication/authorization, you would retrieve the data in the same way as you show above.
To put the value into Session: Session["termsCompleted"] = "true";
To read the value from Session: if (Session["termsCompleted"] == "true")....
As an alternative, you could add the information to HttpContext.Current.User.
Sub Session_Start(ByVal sender As Object, ByVal e As EventArgs)
' Code that runs when a new session is started
Dim selectTerms As String = "SELECT Terms FROM tblPersonal WHERE Ref=#Ref"
If Request.IsAuthenticated = True Then
Dim dbconn As String = ConfigurationManager.ConnectionStrings("ApplicationServices").ConnectionString
Using myConnection As New SqlConnection(dbconn)
myConnection.Open()
Dim cmdTerms As New SqlCommand(selectTerms, myConnection)
cmdTerms.Parameters.AddWithValue("#Ref", Variables.myUser)
Dim readerTerms As SqlDataReader = cmdTerms.ExecuteReader()
readerTerms.Read()
Session("termsCompleted") = readerTerms.Item(0)
readerTerms.Close()
myConnection.Close()
End Using
End If
End Sub
And in the code-behind:
If Session("termsCompleted") = False Then
Response.Redirect("Terms.aspx")
End If
Unfortunately this is redirecting to the terms.aspx page every time regardless of what is in the database. From debugging it's picking up the reader item as 'False' even when it's true..
Thanks
Create a base page and have each page inherit from that. In this base page you can do the data access once to perform this check. Then store it in session state.
I don't think you have a security issue...I think it's more of a best practice issue. It's not good practice to put your data access requests in a property. In projects I work on, I typically will have a class that has functions that handle my data access with a buisiness layer that makes the calls to my data access. An n-tier project design may not fit your project...I'm just speaking from my experience.
If you need to reuse the bit flag, just store it in Session.
This logic doesn't really belong on a page. If accepting the terms of use is a requirement for accessing parts of your site then you should handle it that way. This problem is a very similar situation to having an administrator section of a site that only a few users can access.
Ideally this is something you would handle before the request gets to the page. There are (at least) two ways to approach this.
You could write a custom HTTP module that subscribes to the AuthorizeRequest event. It will check whether this page requires that you accept terms of agreement or not and if so checks to see if the user has or not. If not it will redirect the user to the terms of use page.
The other option is to put this code into your Global.ascx file. You would want to subscribe to the AuthorizeRequest event and perform your logic there.
I don't think it matters which option you chose (though the second one may be a little more straight forward to implement). The benefit is that this concern is handled outside of the page itself. This way as you add new pages to your site, you can't forget to add your validation code to it. If you ever decide that in addition to accepting terms of agreement users need to do something else, you now have one place to change instead of going through all of the pages, etc.
You should also take advice of some of the other answers and store this value into the Session to avoid having to to do a database request every time a page loads.
More information:
Http Modules and handlers
How To Create an ASP.NET HTTP Module Using Visual C# .NET (VB should be the same concept with VB syntax).
Application, Page and Control lifecycle (to help you better understand how ASP.NET application lifecycle works and what else is possible).

3 tier application pattern suggestion

I have attempted to make my first 3 tier application. In the process I have run into one problem I am yet to find an optimal solution for.
Basically all my objects use an IFillable interface which forces the implementation of a sub as follows
Public Sub Fill(ByVal Datareader As Data.IDataReader) Implements IFillable.Fill
This sub then expects the Ids from the datareader will be identical to the properties of the object as such.
Me.m_StockID = Datareader.GetGuid(Datareader.GetOrdinal("StockID"))
In the end I end up with a datalayer that looks something like this.
Public Shared Function GetStockByID(ByVal ConnectionString As String, ByVal StockID As Guid) As Stock
Dim res As New Stock
Using sqlConn As New SqlConnection(ConnectionString)
sqlConn.Open()
res.Fill(StockDataLayer.GetStockByIDQuery(sqlConn, StockID))
End Using
Return res
End Function
Mostly this pattern seems to make sense. However my problem is, lets say I want to implement a property for Stock called StockBarcodeList. Under the above mentioned pattern any way I implement this property I will need to pass a connectionstring to it which obviously breaks my attempt at layer separation.
Does anyone have any suggestions on how I might be able to solve this problem or am I going about this the completely wrong way? Does anyone have any suggestions on how I might improve my implementation? Please note however I am deliberately trying to avoid using the dataset in any form.
Use the app.config file for your connection string.
Is there a particular reason you pass ConnectionString at all? It seems like a configuration value to me? So using something like a constant (or a Config singleton) might be a better idea.

Provide strongly typed access to the session object

What is the best way to provide strongly typed access to the session object? I am planning on turning on Option Strict, which is causing the compiler to complain about my lazy programming technique of directly accessing the session object:
Dim blah As Integer = Session("Blah")
My initial thought is to create a class that wraps the session and provides strongly typed properties for the information stored in the session. However, I cannot decide if the class should be a singleton, or instantiated on every use, or where the code should reside (i.e. within the web project or within a class library).
I'm leaning towards a singleton in my class library, but I don't know if that is the best solution, or if I am missing any other possibilities.
Proposed Solution:
Public Class SessionAccess
Public Shared Property Blah(ByVal session As HttpSessionState) As Integer
Get
Return Convert.ToInt32(session("Blah"))
End Get
Set(ByVal value As Integer)
session("Blah") = value
End Set
End Property
End Class
Code Behind:
Dim blah As Integer = SessionAccess.Blah(session)
I deleted my original answer as #Jason Berkan made a very good point when he questioned my answer. Jason, I think this idea is fine.
The only thing I would change in your code example is to check to ensure that the session variable exists.
Either my proposal is the "standard" way to do it, or else no one wraps their session access, since this question hasn't received very many answers.
I did find one line in this answer that mentioned creating a SessionManager:
Wrap the ASP.NET Session with a
SessionManager to avoid development
mistakes in spelling, etc. when
referencing items from Session.
I have not thought of any reason to not use a singleton class to provide typed access to the session, so that is the solution I went with in the project.

Shared Functions in VB.Net

I have to send emails when a person receives a personal message on my website and for that I use a StringBuilder to create the HTML markup of the email.
also since it is required at many other places as well I have made a Shared Function (I am using VB.NET). now my only concern is that since shared functions are shared among all objects and maybe asp.net sessions, can it be possible that before one person email is being formed and another person access the same function, it would cause the data in the stringbuilder to be overwritten..
Currently my site doesn't have many users but can this become an issue in the future... Please advise me on this... Is there any better way or standard procedure to follow when using shared functions?
Also at one time I made the mistake of using a shared connection object and it would cause close if many people were to access it.
Shared functions can only access static/global variables as well as variable inside the function scope. So, if the the function is working with any number of static/shared resources then you need to synchronize between the calls to the function.
In your case, however, it doesn't sound like you're working with any shared resources, so it shouldn't be a problem.
Here's a case that illustrates the problem:
private static myCounter As Integer = 0
public shared function IncreaseCount() As Integer
myCounter += 1
for i as integer = 0 to 10 million
//'do extensive work
next
return myCounter
End Function
Imagine that you call the function for the first time, and you would expect it to return the number 1. But due to the fact that the function was called again before the first function call got to return the counter was increased once more, which means that both function calls return 2 instead of respectively 1 and 2. All the problem arrives when you want several things working on the same static resource.
Instead of using a static method you can have an EmailSender object attach to current HttpContext.This way each user will have its own EmailSender instance.
Here's the code in C# :
private static EmailSender _instance;
public static EmailSender GetEmailSender()
{
if(System.Web.HttpContext.Current != null)
{
if(! System.Web.HttpContext.Current.Items.ContainsKey("EmailSender"))
System.Web.HttpContext.Items["EmailSender"]=new EmailSender();\
return (EmailSender)System.Web.HttpContext.Current.Items["EmailSender"];
}
if(_instance==null)
_instance=new EmailSender();
return _instance;
}
It will work in web and windows application.
now every time you want to send an email you can do as follows:
GetEmailSender().SendMail(MailInfo mailInfo);
Also, if you're using VB.NET on Framework 3.5, you may want to look into using XML literals to build your HTML instead of StringBuilder. XML literals will make your code SUPREMELY more readable, and allow for very easy insertion of data into your message.
As a SIMPLE example...
Dim msg = <html><body>
Message sent at <%= Now.ToString() %>
</body></html>
myMailMessage.IsBodyHtml = True
myMailMessage.Body = msg.ToString()

Resources