Wednesday 23 September 2009

Raise an Event in a Master Page from Control in the Content Page

Just a quick one because this was something that was bugging me until recently. I had a button in a repeater in a control in a page which had a master page. Now I wanted to raise an event to update something in my master page when one of that repeaters buttons was pressed. How did I do it?

Here's what I had in my control...


<asp:repeater id="rptOption" runat="server">
<headertemplate></headertemplate>
<itemtemplate>

<div class="additem">
<asp:linkbutton id="btnAdditem" runat="server" text="Add to Basket" commandargument="'<%#Eval(">' CommandName="OfferID" />
</asp:linkbutton></div>

</itemtemplate>
<footertemplate></footertemplate>

My code behind needed a new public event declaring which I will be seen from the page containing the control....

Public Event CartAdded(ByVal source As Object, ByVal e As System.Web.UI.WebControls.RepeaterCommandEventArgs)

Now in the event raised from the repeater control I need to raise this new event so that the page can find out the user just clicked that button.

Protected Sub rptOption_ItemCommand1(ByVal source As Object, ByVal e As System.Web.UI.WebControls.RepeaterCommandEventArgs) Handles rptOption.ItemCommand

RaiseEvent CartAdded(source, e)

End Sub


Now if you go back to your page you can refer to your controls new event. Something like this should do:

Protected Sub productBuyOptions_CartAdded(ByVal source As Object, ByVal e As System.Web.UI.WebControls.RepeaterCommandEventArgs) Handles productBuyOptions.CartAdded
Master.UpdateCart()
End Sub

In this situtation my master page has a public sub called UpdateCart which I am calling.

One more thing before this works, I believe you have to put a reference back to your master page in your content page.

<%@ MasterType VirtualPath="~/yourMasterPage.master" %>

This will let you reference public methods and properties from your content page. Hooray!

Tuesday 18 August 2009

Choosing Passwords

Nobody wants someone else to read their email. Even worse is the possibility of using that email address to reset the password of everything else they use online. Want someone to have access to your paypal? You should choose a strong, secure password that's easy for you to remember but can't be easily guessed by someone having gained intimate knowledge of you. The more random and muddled you make it, the more difficult it is for others to guess. If your password is compromised, the cracker may be able to totally take over your identity.

A small password can be easily cracked if the potential fraudster obtains a crytographic hash of it. Modern computers work fast enough to cycle through all alphabetic combinations shorter than 7 characters. A password is considered weak if it is too short or is still set to a default setting, or which can be quickly guessed by searching a dictionary file full of common passwords.

A strong password would be long, random appearing, or could be produced only by the user who chose it, so that these tools would take too long to complete to make it not worthy of the crackers time.

These are some example security practices that can assist in password picking:

1) Passwords should preferably be between 8 and 14 characters long.

2) Passwords should contain a combination of numbers, letters, and special characters (when allowed by password policies).

3) Passwords should not contain dictionary words from any language, or even from technical dicitionaries.

4) Each password should be totally different from your username.

5) When changing passwords the new and old passwords should differ by at least 3 characters.

6) Avoid choosing names of people, pets, locations, or other personal info that can be easily discovered.

7) Stay away from commonly used keyboard sequences, such as qwerty or zxcv1234 .

8) Never make a password by just adding a digit to a word. If I know your name is Dave I could try dave1, dave2, dave3 etc. with a script that allows me to do hundreds in a second.

9) Avoid writing your password down, storing it on your computer or tattooing it on your face.

10) Never share your password with other people (even your friends).

Friday 12 June 2009

Diagnosing ASP.NET 100% CPU Utilisation Issues

So you built a web application, tested it and deployed it. People are using it and everything is fine, but then one day your site suddenly becomes totally unavailable. You look at the web server and the w3wp.exe process is using up 100% of the cpu time. Well, here's a way to figure out what page is causing the jam without having to pay for any fancy diagnostic tools.

Log Parser

Firstly get and install the latest version of Log Parser from Microsoft. Currently that would be version 2.2.

http://www.microsoft.com/downloads/details.aspx?FamilyID=890cd06b-abf8-4c25-91b2-f8d975cf8c07&displaylang=en


Finding Out Which Site is the Problem
Are you running multiple sites and aren't sure which site is causing the problem? Well if you aren't already try running each site on a separate application pool. This is also a hint at another way to prevent these kind of performance issues which is managing a web garden in IIS. Hopefully I'll cover that another time. This will mean there is a w3wp process for each site. When one of these processes hits 100% you can use process explorer (http://technet.microsoft.com/en-us/sysinternals/bb896653.aspx) to find out which pool is causing the issue. The name of the pool will be at the end of the target field when you click on the properties of the task though I'm sure there's a better way of finding that out.

Configuring IIS Logging

To use log parser effectively you need to ensure that you are logging the right things. Go into IIS manager, open the website tab, click on the logging properties button. Now click on the Extended Properties tab and check Time Taken. This is going to allow you to find pages that take a long time to execute in your logs.

Wait

Now you need to wait for it to happen again. Why not have a cup of tea or dig a hole to nowhere? One thing you could do to speed up this occurring again is to use a site crawler to hammer your application. There are plenty of free trials for these kinds of apps floating about the Internet, though it does assume your problem page is causing it's havoc in something executed during page load and not after user interaction.

Using Log Parser

Huzzah, it's at 100% again! Put down that spade and get back to your server. Open up a command prompt in the directory you installed logparser, copy your most recent logfile for the offending site into the logparser directory and paste this into the command window:

logparser "SELECT TOP 10 cs-uri-stem AS Url, MIN(time-taken) as [Min], AVG(time-taken) AS [Avg], max(time-taken) AS [Max], count(time-taken) AS Hits FROM ex*.log WHERE time-taken < class="blsp-spelling-error" id="SPELLING_ERROR_17">Url ORDER BY [Avg] DESC" -rtp:-1

This will give you the top 50 longest taking pages on your site. Next comes a bit of a tedious task. One or more of those pages is likely to be the page causing your problem. The rest are going to be pages that were held up because of that page that was causing the issue, or for some other erroneous reason like the user is trying to load a page with a dying Internet connection their end. In any case copy the results out of the command prompt and into note pad. Now copy each url, starting from the top, into your browser, maybe fiddle a bit with the ui, until you hit the one that maxes out the CPU of your server. This bit would probably be best done on a test server if you have one. When I last had this issue I didn't have to go any further than the second url to find my problem (though I did check them all for prosperity). It'll likely be something simple and stupid that you didn't expect to happen. Mine was a malformed url that was being generated on the site somewhere that omitted a certain parameter I expected to always be there.

Now, back to that hole to nowhere....

Tuesday 12 May 2009

Import Data from Google Analytics VB.NET

As far as I know there is no current service available for consuming data from Google Analytics. The way I got round this was by setting up my analytics account to email me xml reports and then setting up an application to check the email account for them. When the application gets an email from Google it consumes the xml. It's best to set up a dedicated email address for analytics and save each xml document as they come in, just in case you want to change what you consume to your database at a later date and need to pull the whole lot back in again.

First get the (free) Indy Sockets dll from:
http://www.indyproject.org/index.en.aspx

The Code
' create a pop3 client using indie
Dim pop3 As New Indy.Sockets.POP3
pop3.Username = "******" 'the name of the account you get analytics to email reports to
pop3.Password = "****" 'password of the account
pop3.Host = "******" 'your mail host
pop3.Connect()

Dim nrOfMsgs As Integer = pop3.CheckMessages

For i As Integer = 1 To nrOfMsgs
Dim msg As New Indy.Sockets.Message
'get message
pop3.Retrieve(i, msg)

'attatchments
'ANALYTICS DATA

For x As Integer = 1 To msg.MessageParts.Count - 1 'search through the message parts
Dim att As Indy.Sockets.Attachment
Dim isAtt As Boolean = False

Try
att = CType(msg.MessageParts.Items(x), Indy.Sockets.Attachment)
isAtt = True
Catch
End Try
Dim ct As String = msg.MessageParts.Items(x).ContentType
If isAtt = True And InStr(msg.Subject, "the subject expected") > 0 Then
Dim stream As New Indy.Sockets.TIdNetMemoryStream
att.SaveToFile("c:\analytics\" & Now.Year & Now.Month & Now.Day & "-" & Now.Hour & Now.Minute & Now.Second & ".xml")

'here you can do whatever you like with the xml document, add all the data to your sql
'database!

End If


Next

pop3.Delete(i) 'delete the email from the server

Next




pop3.Disconnect()
pop3.Dispose()

Monday 11 May 2009

This row already belongs to another table ASP.NET

Just a quick look at a problem I came across when trying to copy rows from one datatable to another.

I originally tried this:

for i as integer = 0 to DT.rows.count - 1
newDT.rows.add(DT.rows(i))
next

ASP.NET threw a hissy fit at me and gave me the "This row already belongs to another table" error. I haven't looked to far into this but I assume the datarow objects are bound to a row schema inherited from the datatable schema. A bit of googling led me to the following solution:


for i as integer = 0 to DT.rows.count - 1
newDT = DT.Clone
newDT.ImportRow(DT.Rows(i))
next

So there we go, the clone function copies the schema from the first table to the second which allows for the importing of our rows.

Tuesday 5 May 2009

Posting an Image to a Web Page in asp.net

I recently had to write an application that was able to upload images to a website in order to be filed away and used by it's photo gallery pages. All I did to put this in place was make a timer which periodically checked for new images in a local folder. When it finds new images it loops through the files and passes each image to a posting function. After a successful post the image gets archived away by the local application. The website performs it's own local file and database functions to file away the image and the job is done! Let's look at some snippets:

The business end of the windows app....

Dim postURL as string = "http://www.yourwebsite.com/somepage.aspx"

Private Sub uploadImage(ByVal theImage As Image, ByVal filename As String)

theImage = processImage(theImage) 'this is a function I use for some intelligent resizing of the image
Dim tempFileName As String = Path.GetTempFileName
theImage.Save(tempFileName)

Dim myHTTPRequest As New WebClient
Dim myHTTPResponse As Byte() = myHTTPRequest.UploadFile(postURL, "POST", tempFileName)

End Sub


The website code to get the image...

Page.Response.ContentType = "image/jpg"


Dim myimage As Byte() = New Byte(Page.Request.ContentLength - 1) {}
Dim uppedImage As HttpPostedFile = Request.Files.Item(0)
uppedImage.InputStream.Read(myimage, 0, CInt(Request.Files.Item(0).ContentLength))


Dim theImage As Image
theImage = Image.FromStream(uppedImage.InputStream())
theImage.Save("C:\mylocalsite\" & getImageName() & ".jpg") 'getImageName just returns a unique date stamped file name

Monday 4 May 2009

Creating Image Watermarks in VB.NET

Fancy making your own batch watermarking application? Here's a function that'll get you started. I originally adapted this from some code that was serving images in asp.net watermarked at runtime using a http handler. That was pretty nifty but I really needed it to watermark images for some automated uploading to facebook.


Public Function AddWaterMark(ByVal theImage As Image) As Image


Dim canvas As Graphics


'create a new bmp the size of the passed image
Dim bmpNew As Bitmap = New Bitmap(theImage.Width, theImage.Height)
'create a canvas
canvas = Graphics.FromImage(bmpNew)


'draw the passed image onto the canvas
canvas.DrawImage(theImage, New Rectangle(0, 0, _
bmpNew.Width, bmpNew.Height), 0, 0, _
theImage.Width, theImage.Height, GraphicsUnit.Pixel)
theImage = bmpNew



Dim overlayImg As Bitmap


'make the overlay image
overlayImg = Drawing.Image.FromFile("C:\someplace\mywatermark.png", True)

'draw the overlay onto the top left corner
canvas.DrawImage(overlayImg, 0, 0, overlayImg.Width, overlayImg.Height)

Dim tempImage As Image = bmpNew
AddWaterMark = tempImage



End Function