I had a problem (which is now fixed) but I've no idea why...
I have an Ajax AutoCompleteExtender with a WebService method to populate like
<WebMethod()> _
Public Shared Function populateACE(prefixText As String) As List(Of String)
However this didn't work - I put a breakpoint in, and it didn't even get hit. However...
<WebMethod()> _
Public Function populateACE(prefixText As String) As List(Of String)
..does work (the only difference being not Shared).
Fair enough, but why? If you have an instance of a class then you can access Shared methods of it; if you don't have an instance of a class then you can access Shared methods of it. So what is going on behind the scenes?
If you're calling a page method then it must be Shared/static. But when calling methods attached to asmx services, accordining to John Saunders in this question,
Why are Static Methods not Usable as Web Service Operations in ASMX Web Services?, web methods can't be Shared/static by design.
I'd have to guess that both are design limitations in the pipelines that retrieve pages vs. web methods.
To quote the relevant part from John Saunders' answer..
The answer is: because you can't.
It's not designed that way. The design is that an instance of the web
service class will be created, and then an instance method will be
called.
..but still worth having a look at the full answer.
Related
I am using this example I found to learn how to load class files and access variables through them. This is in a file called Class1.vb in the App_Code folder (this is not an app project):
Imports Microsoft.VisualBasic
Public Class my_class
Public Shared Sub my_sub()
Dim vartest As String
vartest = 10
HttpContext.Current.Session("myvar") = vartest
End Sub
End Class
This is the codebehind on the aspx file:
Imports my_class
Partial Public Class test
Inherits System.Web.UI.Page
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
my_class.my_sub()
Label1.Text = HttpContext.Current.Session("myvar")
End Sub
End Class
How could I access the vartest variable without using a session, since if this is accessed by multiple functions at the same time the variable can be overwritten I assume. Is it possible to go the other way, where a variable is sent to a class file?
It sounds like you need a quick overview of some basic ASP.Net Webforms concepts. Up first I'll counter a common newbie misconception:
Your Page class does not hang around on the web server for very long
I think many new ASP.Net developers have this idea of the web server keeping a single instance of their page class for every user session that hits their site, and each postback or event uses this same page class instance. That's just not how it works. ASP.Net page class instances are nearly always created and destroyed again in well under a second, and most experienced developers see it as a big problem if it takes longer.
ASP.NET relies on the HTTP protocol
The thing to remember here is ASP.Net still relies on the HTTP protocol, and http boils down to requests and responses. When you view a web page, your browser first sends a request to a server. The server responds, usually with an html document. The browser will then parse the html; based on what it sees in the html the browser may send more requests to the server for additional resources, such as javascript, images, or css files. Each request results in a separate response, and the browser uses all these resources to render the page to the screen. However, the ASP.Net runtime normally does not have to process the additional requests (that would make things slower) — ony the initial html needs ASP.Net support; you want the other resources to be basic files that can be cached.
The ASP.Net runtime creates a new instance of your class for every request.
When the ASP.net runtime processes a request for a page, it will create a new instance of your page class. The runtime will follow the ASP.Net Page lifecycle (this should really be named the "ASP.Net Page Request Lifecycle"), and call certain methods or raise certain events in this class instance, in a specific order defined by the lifecycle.
This means every postback or event runs in a different instance of your class.
It also means every postback or event is rebuilding and transmitting all of the html the goes into your page, and not just the portions you want to change. For your server code, the consequence is the only thing class-level variables are really good for in ASP.Net is things that will be used within a single http request. For the browser, the consequence is you're working with a brand new DOM after every event.
To understand all of that, it's important here to also have a good understanding of the difference between a class and an instance of a class. A couple items in your question make me unsure whether you have this understanding yet.
The ASP.Net runtime shares one application instance among all users of your site
The web server typically only has one instance of your application for the entire web site and all it's users. Therefore, anything with a Shared/static scope is common to every user. It's rarely appropriate in ASP.Net for anything to be Shared/static.
So how do you handle data that should live with a single user or visit to your site?
This is exactly what the Session is for. A session will always be unique to an individual request at any given time. You're worried about multiple functions accessing the session at the same time, but this does not happen. The ASP.Net Page Lifecycle ensures that unless you manually spawn additional threads, only one function at a time is running for a given HttpContext and Session. If a user somehow sends two requests at about the same time that should have the same Session/HttpContext, one will be held by the ASP.Net runtime until the other is completed. If you don't want to reference the session all the time, you can build properties in your class that wrap session variables. See #Pankaj's answer for an example.
First, a Session has user-scope, so it will not be overwritten by another Request.
Is it safe to access asp.net session variables through static properties of a static object?
You could encapsulate the access into a property:
Public Shared Property MyVar() As String
Get
If HttpContext.Current.Session("MyVar") Is Nothing Then
HttpContext.Current.Session("MyVar") = ""
End If
Return DirectCast(HttpContext.Current.Session("MyVar"), String)
End Get
Set(value As String)
HttpContext.Current.Session("MyVar") = value
End Set
End Property
Then you can get the variable by:
Label1.Text = my_class.MyVar
In addition to the "Tim Schmelter" reply....
You can create a BaseClass which will inherit from
System.Web.UI.Page
Place the property as suggested by "Tim". The only change you need to do is to change the access modifier to Protected and you should remove Public and Shared
You can also keep other common functions, properties that can we reused in other classes also... Similarly you can create BaseControls as well for your User controls
Finally, inherit this class in the web form....
Hope this will help you...
Base Class code
Imports System
Imports System.Collections.Generic
Imports System.Linq
Imports System.Web
Public Class BaseClass
Inherits System.Web.UI.Page
Protected Property MyVar() As String
Get
If HttpContext.Current.Session("MyVar") Is Nothing Then
HttpContext.Current.Session("MyVar") = ""
End If
Return Convert.ToString(HttpContext.Current.Session("MyVar"))
End Get
Set
HttpContext.Current.Session("MyVar") = value
End Set
End Property
End Class
Sample Code "Behind Code" - Showing the usage of Protected member Data from Base Class
Imports System
Imports System.Collections.Generic
Imports System.Linq
Imports System.Web
Imports System.Web.UI
Imports System.Web.UI.WebControls
Public Partial Class Default5
Inherits BaseClass
Protected Sub Page_Load(sender As Object, e As EventArgs)
If Not Page.IsPostBack Then
Dim str As String = Me.MyVar
End If
End Sub
End Class
Generally you can use different places to store application state: Application (application wide, saves state into application domain), Session (there can be saved everything what will be accessed by current browser session), ViewState (variables stored in hidden input field and will be posted on every postback). Of course you can also save state to database or file. I'm not sure what you want to achieve, but looks like you looking for something like ViewState.
Read ASP.NET State Management
I have a complex Regex which is used to help strip out HTML from user input. I'm aware that .NET caches static Regex calls to some extent, but this one is big and used frequently, so I'd like it to hang around.
In a web site project, I'd like to define it as a shared object, within a helper class in App_Code, eg:
AppHelper.vb in App_Code:
Public Class AppHelper
Private Shared _rxRemoveHTML As New Regex("(<[\s\S]*?(style|script)[\s\S]*?>[\s\S]*?</[\s\S]*?(style|script)[\s\S]*?>)|<[^<>]*>|&#[0-9a-z]+;|&#|<|>|\\|`|\t", RegexOptions.IgnoreCase + RegexOptions.Multiline)
This will keep it alive for the life of the app.
A public function does the work:
Public Shared Function RemoveHTML(ByVal sIn As String) As String
Return _rxRemoveHTML.Replace(sIn, "")
End Function
The question is, is this thread-safe and otherwise ok for a web app?
Yes, it is perfectly safe to do so. Taken from MSDN:
Thread Safety
The Regex class is immutable (read-only) and thread safe. Regex objects can be created on any thread and shared between threads
our ASP.NET application is using COM+ to connect to Database
we have this structure:
A Base Class :
Imports System.EnterpriseServices
Public Class Base Inherits ServicedComponent
A Child Class:
Public Class Member Inherits Base
'Propreties
.
.
.
'Methods
Public Sub SetMember(ByVal SelectedQueue As String)
...
End Sub
In a Aspx page, we search for a member and set details:
Dim newMember As Member = New Member
newMember.SetMember(MemberNumber)
Session("SelectedMember") = newMember
We then dispose newMember:
If Not newMember Is Nothing Then
newMember.Dispose()
End If
but whenver we access the session we got an exception:
If Not Session("SelectedMember") Is Nothing Then
'Something
Else
'Something else
End If
the exception is :
Cannot access a disposed object. Object name: 'ServicedComponent'.
How can I dispose the object but keep my session valid?
I can see what you're doing wrong, but can't be clear on what would be right. Your logic as stated is:
Obtain object.
Store object.
Clean-up object, rendering it useless.
Retrieve object.
Use object.
Having 3 before 5 makes no sense.
If the object is quick to obtain, you should just do so on every page. (Often people over-estimate the cost of this).
If the object is slow to obtain, and it makes sense to store for a long term, then it shouldn't need to be cleaned-up. What is Dispose() actually doing here? With it obtaining and releasing resources used by members as needed.
I suspect that the first is the one to go for here, but that's mostly a guess.
I'd also be concerned when you talk about the database, does your object hold a database connection? If so, and pooling is available, then you should be releasing those connections as fast as possible, rather than holding onto them.
I've been given the thrilling task of re-writing our exception handling system. Whilst I will state that handling exceptions from an application-wide point of view isn't something we want, typically it's unavoidable when our team are understaffed for the sheer amount of work we need to push out the door, so please, no flaming the globalised solution to exception handling here :)
I've had a good hunt to see what common solutions exist. At the moment we use Global.asax with the Application_Error event to do Server.GetLastError() which is placed in Session state then a redirect is called to another page where the session data is then retrieved and output in a human readable format. The redirect also calls a sproc which will carefully audit the error information which is a) e-mailed to the developers and b) viewed from a web page only viewable by developers.
The new way I've seen of doing things is using the IHttpModule interface using a class in App_Code to do something along these lines (this is my quick implementation)
Imports Microsoft.VisualBasic
Public Class ErrorModule : Implements IHttpModule
Public Sub Dispose() Implements System.Web.IHttpModule.Dispose
' Not used
End Sub
Public Sub Init(ByVal context As System.Web.HttpApplication) Implements System.Web.IHttpModule.Init
AddHandler context.Error, AddressOf context_Error
End Sub
Public Sub context_Error(ByVal sender As Object, ByVal e As EventArgs)
Dim ex As Exception = HttpContext.Current.Server.GetLastError
' do something with the error
' call the stored procedure
' redirect the user to the error page
HttpContext.Current.Server.ClearError()
HttpContext.Current.Response.Redirect("index.htm")
End Sub
End Class
My question is, what is the benefit of this solution over using Global.asax events? Additionally, what is the best way to hand the data to an error page?
EDIT: The code above does work by the way ;)
EDIT: Also, how does the HttpModule work behind the scenes? Does it just register the Error event to that particular function on application start?
UPDATE:
Upon much further investigation it seems grabbing session data is really, really messy when it comes to using IHttpModule interface. I don't think MS have matured HttpModule enough for it to be used in our particular scenario - until there are events specific to session data it's too dangerous for us to use.
Using a module has the advantage of being easily removable, all you need to do to disable it is to remove it from <httpModules> in your config.
As far as your data goes, try going with Server.Transfer or Server.RewritePath - that will keep all the current data (including the last server error).
If for some reason it clears the last error, you can save the error to HttpContext.Items before the transfer/rewrite and then retrieve it afterwards.
Edit: In response to your edit, an IHttpModule attaches to any appropriate events in it's IHttpModule.Init implementation.
HttpModule basically does the same thing as Global.asax. It's designed as a more reusable and self-contained module for event handling.
I need to get a value from an API I made with ASHX and normally it is called from javascript but I need to call it right in ASP.NET I figured this shouldn't be a problem but I'm not sure the syntax.
Well you have a couple options
You can refactor the code in your ASHX to be in a shared library so you can access the methods directly and so can the handler.
You can instantiate the handler and invoke the members if they aren't private.
You can create a webrequest to the handler and handle the response.
These are just a few of the easy ways.
I personally like the first method because it promotes code reuse, but depending on scenario you can do what you like.
Edit to provide answers for question in comment.
Essentially Yes... Instead of having a bunch of code in your handler you make a class called something meaningful to you contextually. Inside that class you place the logic that was in your handler. Then from your handler you can create an instance or call a static version of the class (depending on how you implemented it) passing it the HttpContext object or whatever is required for that logic to run correctly. Do the same thing in your ASPX page. You can now call into an object that contains the logic from anywhere in your app instead of having it reside in the handler alone.
EX:
Public Class MyCommonLogic
Public Shared Function ReturnSomethingCommon(context As HttpContext) As String
Return "Hello World!"
End Function
End Class
Then from the handler or the aspx page..
Dim something As String = MyCommonLogic.ReturnSomethingCommon(...)
I made the function static, but that is just an example of course I would implement it however would make more sense in your scenario.
Changed code to VB sorry about that.
If the ASHX is on the same server especially if its within the same web app, you should refactor your logic out of the ashx into a common class that both the aspx and ashx can call.
Otherwise you can look at using: System.Net.WebClient