How to use caching? - asp.net

Basically, I retrieved a dataset and I want to cache it in the server memory for one month. So I don't need to call the query again and again when running the page within this month.
I did some research and think http://msdn.microsoft.com/en-us/library/system.web.caching.cacheitemremovedcallback%28v=vs.110%29.aspx is the way to do the cache, basically i modified the sub codes to fit into my application
Public Sub RemovedCallback(k As String, v As Object, r As CacheItemRemovedReason)
itemRemoved = True
reason = r
End Sub
Function AddItemToCache(cacheKey as String, ds as Dataset)
itemRemoved = False
onRemove = New CacheItemRemovedCallback(AddressOf Me.RemovedCallback)
If (IsNothing(Cache(cacheKey))) Then
Cache.Add(cacheKey, ds, Nothing, DateTime.Now.AddMonths(1), TimeSpan.Zero, CacheItemPriority.High, onRemove)
End If
End Function
There are quite a few errors in this piece of code. One of the error is for Cache(cacheKey) says that " Cache is a type and cannot be used for expression"? where did i do wrong?

Sounds like you're using IIS cache. First of all, if this is your route - if you have an assemblies that may use cache (when available), you need to create Caching assembly in which you check for HTTPcontext. If it is null - you are running outside of IIS and caching will not be available.
A good alternative is to download Enterprise Library Caching blocks if you working with framework up to 3.5. If you use FW4.0+ you use system.runtime.caching. This way cache will be available always. There is also AppFaric and some third party cache implementations but this is probably outside the scope for you.
For your error, it sounds like your identifier Cache [your code is not showing how you assign it] is actually a type. That is if you did this
If Integer Is Nothing....
What you need is to use is syntax
System.Web.Caching.Cache.Add...
Now, this is instance. So, what you can do is
Dim c as Cache = System.Web.Caching.Cache
c.Add(....

Related

Loosing session variable data from one page to the other in VB.NET

I am a bit new to VB.NET. I have a page that sets 2 session variables and does a redirect to second page. The second pages is at least using one of the session variables. I can tell because on the second page, if the session variable is not correct the user is redirected to an access denied page. The second page also uses the session variable in question. It will read it an fill a gridview based on the value of the variable. I set the variable like so
Session("ID") = Convert.ToInt32(a_value)
and on the second page I retrieve the variable like this
a_page_variable = Session("ID")
What I find strange is that when I run this code in visual studio it works as expected but when I deploy and run it, I get 0 from my session variable instead of the true value of "a_value". I have tried a few things like making sure the data types match up from page to page and trying different ways to retrieve the variable such as
Session("userID")
and
CType(Session.Item("userID"), Int32)
I've also tried to see what is coming in to the second page by using
Response.Write
I also tried to use SQL Profiler to see what kind of call is being made to fill the gridview but I haven't had any luck. The gridview gives me an empty dataset and the Profiler does not detect a call being made from the application. I thought working with session variables was pretty straight forward but obviously, I am missing something.
Thanks for your help,
Billy
One possibility (and the only one that could be guessed at with how little information we have) could be the response.redirect causing the application to terminate due to an exception.
When redirecting, you want to always pass a false, and then call complete request.
Response.Redirect(urlstring, False)
Response.CompleteRequest()
not following these steps can cause exceptions, which may drop session.
Additionally, resolve virtual paths, as some browsers (mobile especially) can see those redirects as new requests entirely, thus generating new session tokens.
Dim urlstring As String
urlstring = Page.ResolveUrl("~/default.aspx")
that said, there are a number of possible causes for this situation.
Application Pool restarts
App Domain restarted
Code changing value unexpectedly
AV tinkering with files
deployed to web farm
With the description provided above, we just don't have enough information to really troubleshoot.
Thank you ADyson, Stephen Wrighton and everyone else who took a stab at helping me figure this out. I was able to find out what was going on by adding code that wrote to a log file on the server. Found the logging code here. I found that I never reached the code that set the session variable and that is the reason it never populated on the second page. I was trying to get the logon name for the user by using Environment.UserName which will return the user name of the person who is currently logged on to the operating system. But what I really wanted to do was get the logon name of the user that was visiting my site. For this I used User.Identity.Name. This works great when you need to know which user from an Active Directory domain is visiting your site.

ASP.NET modify connectionstring at runtime

I need to change dataset connectionstrings to point to different DBs at run time.
I've looked at a number of solutions however they all seem to be related to WinForms or web application projects or other technology slightly different than what I'm using, so I haven't figured out how apply them.
The application is like a discussion. It's a web site project based on code originally written under VS2005, and there's no budget (or personal talent!) for major changes at this time. The app is written in vb.net; I can understand answers in c#. I'm working in VS2013.
The app has three typed datasets pointing to one MDF, call it "MainDB.mdf". There are dozens of tableadapters among the three datasets.
I'm going to deploy the app it as an "alpha/demo" version. I would like to use the same code base for all users, and a separate physical version of MainDB for each user, to reduce chances that the users crash each other.
The initial demo access URL will contain query string information that I can use to connect the user with the right physical database file. I should be able to identify the database name and thus the connection string parameters from the query string information (probably using replace on a generic connection string). If necessary I could use appsettings to store fully formed connection strings, however, I would like to avoid that.
I would like to be able to change the connection strings for all the datasets at the time that the entry point pages for the app are accessed.
Changing the tableadapter connection strings at each instantiation of the tableapters would require too much code change (at least a couple of hundred instantiations); I'd just make complete separate sites instead of doing that. That's the fall back position if I can't dynamically change the connectionstrings at runtime (or learn some other way to make this general scheme work).
Any suggestions on how to approach this would be appreciated.
Thanks!
UPDATE: Per comments, here is a sample instantiation of tableadapter
Public Shared Sub ClearOperCntrlIfHasThisStaff( _
varSesnID As Integer, varWrkprID As Integer)
Dim TA As GSD_DataSetTableAdapters.OPER_CNTRLTableAdapter
Dim DR As GSD_DataSet.OPER_CNTRLRow
DR = DB.GetOperCntrlRowBySesnID(varSesnID)
If IsNothing(DR) Then
Exit Sub
End If
If DR.AField = varWrkprID Then
DR.AField = -1
TA.Update(DR)
DR.AcceptChanges()
End If
End Sub
UPDATE: Below is the test code I tried in a test site to modify the connectionString in a single instantiation of a tableadapter. It feeds a simple gridview. I tried calling this from Page_Load, Page_PreLoad, ObjectDataSource_Init, and Gridview_Databind. At the concluding response.writes, the wrkNewConnString looks changed to TestDB2, and the TA.Connection.ConnectionString value looks changed to TestDB2, but the displayed gridview data is still from TestDB1. Maybe it needs to be called from somewhere else?
Sub ChangeTableAdapter()
Dim wrkNewConnStr As String = ""
Dim wrkSel As Integer
wrkSel = 2
wrkNewConnStr = wrkNewConnStr & "Data Source=.\SQLEXPRESS;"
wrkNewConnStr = wrkNewConnStr & "AttachDbFilename=D:\9000_TestSite\App_Data\TESTDB1.MDF;Integrated Security=True;User Instance=True"
Select Case wrkSel
Case 1
wrkNewConnStr = wrkNewConnStr.Replace("TESTDB1", "TESTDB1")
Case 2
wrkNewConnStr = wrkNewConnStr.Replace("TESTDB1", "TESTDB2")
Case 3
wrkNewConnStr = "Data Source=localhost; Initial Catalog=test01;"
wrkNewConnStr = wrkNewConnStr & " User ID=testuser1; Password=testuserpw1"
End Select
Try
Dim TA As New DataSetTableAdapters.NamesTableAdapter
TA.Connection.ConnectionString = wrkNewConnStr
Response.Write("1 - " & wrkNewConnStr)
Response.Write("<br/>")
Response.Write("2 - " & TA.Connection.ConnectionString)
Catch ex As Exception
Dim exmsg As String = ex.Message
Response.Write(exmsg)
End Try
End Sub
The connection string:
<add name="TestDB1ConnectionString"
connectionString="Data Source=.\SQLEXPRESS;
AttachDbFilename=D:\9000_TestSite\App_Data\TESTDB1.MDF;Integrated Security=True;User Instance=True"
providerName="System.Data.SqlClient" />
UPDATE: the following post has lots of solutions, however, they seem to focus on web application projects, that have a project file with settings, which this web site project does not.
link with possible solutions
UPDATE: this next link was brought to my attention, and in working on it I did get it to work, however, it still relies either on having a web application project (with project file) or modifying each table adapter as they are instantiated. So, while I'm not going to implement it, I believe that is the technical answer.
modifying connection strings
sorry if this answer is too late, but I have exactly the same problem and eventually came up with a solution using Reflection.
My solution was to "save" a new default value for the connection string in the settings at run time, which means any further use of the table adapter uses the the new connection string.
It should be noted the term "save" is misleading as the new value is lost when the application closes.
Have tested and worked perfectly.
public void ChangeDefaultSetting(string name, string value)
{
if (name == null)
throw new ArgumentNullException("name");
if (value == null)
throw new ArgumentNullException("value");
Assembly a = typeof({Put the name of a class in the same assembly as your settings class here}).Assembly;
Type t = a.GetType("{Put the full name of your settings class here}");
PropertyInfo propertyInfo = t.GetProperty("Default");
System.Configuration.ApplicationSettingsBase def = propertyInfo.GetValue(null) as System.Configuration.ApplicationSettingsBase;
//change the "defalt" value and save it to memory
def[name] = value;
def.Save();
}

Best way to distinguish between Test and Production with Classic ASP

I have inherited a classic ASP application. There are various things that need tidying-up but I am forced to do things gradually (I can't do a wholesale change of every script).
There are places in the system with hard-coded urls. Some scripts have to be changed before promoting to live so that the test web root name is changed to the live web root name. I am looking at ways to avoid this (essentially working out the server programmatically).
It isn't difficult. A simple check on Request("SERVER_NAME") and this sort of thing:
appName = Request.ServerVariables("SCRIPT_NAME")
i = InStr(2, appName, "/") 'keep initial "/"
If i > 0 Then
appName = Left(appName, i)
End If
This in an "everywhere-included" script will do the trick. Then just set up a global variable to hold the full "http(s)://server/app/" or a function to MakeUrlAbsolute() and off you go.
However, this smells a bit suspect to me. Wouldn't this be better stored at application level? This suggests setting it up in Global.asa, something like:
Sub Application_OnStart()
Application("WebRoot") = ...
End Sub
But now I can't get hold of the SERVER_NAME so I'm reduced to having 2 different Global.asa files (one per environment).
Is this my only choice? Work it out on every request or hard-code in Application_OnStart?
Perhaps using Session_OnStart would be a compromise. Or is there some clever trick to access information in Application_OnStart? Perhaps people go with the hard-coding because Global.asa doesn't change often.
my method for ADO Connection looks like this.
'Servername for Application Root Drive
Select Case Left(Request.ServerVariables("PATH_TRANSLATED"), 1)
Case "c" strSrvr = "80.212.207.211" 'my remote server ip
Case Else strSrvr = "(local)" 'local Sql instance (my project on D: drive)
End Select
strConn = "Driver={SQL Server};Server="& strSrvr &";uid=DBUSER;pwd=MYPASS;database=MYDATABASE"
Set objConn = Server.CreateObject("ADODB.Connection")
objConn.Open(strConn)

Iterating through IHtmlElementCollection

I have a VB webapplication that needs to read information from an excisting webpage on the internet. Therefore I use the mshtml library. I read the html into an ihtmldocument3 interface. After that I iterate through an ihtmlelementcollection and everything worked fine in Visual Studio 2010 Debugger. At least, the first time. When I debug the code for the second time, after iterating a few elements, the next elements return nothing and I get an exception. (When I break into the code the ihtmlelementcollection shows 0 items.) When I rename all the variables, it runs properly, but again, only the first time.
Here's the code I use to debug. I have outlined the actual code because that responds into an exception (null reference). Do I need to manually release a collection or something or am I doing something stupid?
'global variable
Private tables as IHTMLElementCollection
...........................................
Dim tableChildren As IHTMLElementCollection = tables(3).children
Dim trElements As IHTMLElementCollection = tableChildren.item(0).getElementsByTagName("tr")
Dim intCount As Integer 'just for debugging purposes
For Each element As IHTMLElement In trElements
intCount += 1 'for debugging purposes
Debug.Print(intCount.ToString & vbNewLine & element.innerHTML)
'strLine1 = element.children(0).innerText
'strLine2 = element.children(1).innerText
'and so on...
Next
I assume that by this point you've already resolved the problem one way or another, but I thought I'd suggest the HtmlAgilityPack. It has the advantage of being written to support just this type of scenario, and (as far as I know) is a native .NET library rather than being COM-based. It might be a better fit for your situation.

is it possible to issue dynamic include in asp-classic?

I mean, like php'h include...
something like
my_file_to_be_included = "include_me.asp"
-- >
for what I've seen so far, there are a couple of alternatives, but every one of them has some sort of shortcoming...
what I'm trying to figure out is how to make a flexible template system... without having to statically include the whole thing in a single file with a loooooong case statement...
here there are a couple of links
a solution using FileSysmemObject, just lets you include static pages
idem
yet another one
same thing from adobe
this approach uses Server.Execute
but it has some shortcomings I'd like to avoid... seems like (haven't tried yet) Server.Execute code runs in another context, so you can't use it to load a functions your are planning to use in the caller code... nasty...
same thing
I think this one is the same
this looks promising!!!
I'm not sure about it (couldn't test it yet) but it seems like this one dinamycally handles the page to a SSDI component...
any idea???
No you can't do a dyanmic include, period.
Your best shot at this is a server.execute and passing whatever state it needs via a Session variable:-
Session("callParams") = BuildMyParams() 'Creates some sort of string
Server.Execute(my_file_to_be_included)
Session.Contents.Remove("callParams")
Improved version (v2.0):
<%
' **** Dynamic ASP include v.2.0
function fixInclude(content)
out=""
if instr(content,"#include ")>0 then
response.write "Error: include directive not permitted!"
response.end
end if
content=replace(content,"<"&"%=","<"&"%response.write ")
pos1=instr(content,"<%")
pos2=instr(content,"%"& ">")
if pos1>0 then
before= mid(content,1,pos1-1)
before=replace(before,"""","""""")
before=replace(before,vbcrlf,""""&vbcrlf&"response.write vbcrlf&""")
before=vbcrlf & "response.write """ & before & """" &vbcrlf
middle= mid(content,pos1+2,(pos2-pos1-2))
after=mid(content,pos2+2,len(content))
out=before & middle & fixInclude(after)
else
content=replace(content,"""","""""")
content=replace(content,vbcrlf,""""&vbcrlf&"response.write vbcrlf&""")
out=vbcrlf & "response.write """ & content &""""
end if
fixInclude=out
end function
Function getMappedFileAsString(byVal strFilename)
Dim fso,td
Set fso = Server.CreateObject("Scripting.FilesystemObject")
Set ts = fso.OpenTextFile(Server.MapPath(strFilename), 1)
getMappedFileAsString = ts.ReadAll
ts.close
Set ts = nothing
Set fso = Nothing
End Function
execute (fixInclude(getMappedFileAsString("included.asp")))
%>
Sure you can do REAL classic asp dynamic includes. I wrote this a while back and it has opened up Classic ASP for me in a whole new way. It will do exactly what you are after, even though people seem to think it isn't possible!
Any problems just let me know.
I'm a bit rusty on classic ASP, but I'm pretty sure you can use the Server.Execute method to read in another asp page, and then carry on executing the calling page. 15Seconds had some basic stuff about it - it takes me back ...
I am building a web site where it would have been convenient to be able to use dynamic includes. The site is all ajax (no page reloads at all) and while the pure-data JSON-returning calls didn't need it, all the different html content for each different application sub-part (window/pane/area/form etc) seems best to me to be in different files.
My initial idea was to have the ajax call be back to the "central hub" main file (that kicks the application off in the first place), which would then know which sub-file to include. Simply including all the files was not workable after I realized that each call for some possibly tiny piece would have to parse all the ASP code for the entire site! And using the Execute method was not good, both in terms of speed and maintenance.
I solved the problem by making the supposed "child" pages the main pages, and including the "central hub" file in each one. Basically, it's a javascript round-trip include.
This is less costly than it seems since the whole idea of a web page is that the server responds to client requests for "the next page" all the time. The content that is being requested is defined in scope by the page being called.
The only drawback to this is that the "web pieces" of the application have to live partly split apart: most of their content in a real named .asp file, but enough of their structure and relationship defined in the main .asp file (so that, for example, a menu item in one web piece knows the name to use to call or load another web piece and how that loading should be done). In a way, though, this is still an advantage over a traditional site where each page has to know how to load every other page. Now, I can do stuff like "load this part (whether it's a whole page or otherwise) the way it wants to be loaded".
I also set it up so each part can have its own javascript and css (if only that part needs those things). Then, those files are included dynamically through javascript only the first time that part is loaded. Then if the part is loaded repeatedly it won't incur an extra overhead.
Just as an additional note. I was getting weird ASCII characters at the top of the pages that were using dynamic includes so I found that using an ADODB.Stream object to read the include file eliminated this issue.
So my updated code for the getMappedFileAsString function is as follows
Function getMappedFileAsString(byVal strFilename)
Dim fso
Set fso = CreateObject("ADODB.Stream")
fso.CharSet = "utf-8"
fso.Open
fso.LoadFromFile(Server.MapPath(strFilename))
getMappedFileAsString = fso.ReadText()
'Response.write(getMappedFileAsString)
'Response.End
Set fso = Nothing
End Function

Resources