Wednesday, July 07, 2010
If you ever get into the situation where you've set up Roles, Users etc. on a machine and then script the database over to another machine, and suddenly the users can't login or aren't in the roles they should be - make sure you have specified the applicationName property in BOTH the membershipProvider and roleProvider sections in the web.config.

http://weblogs.asp.net/scottgu/archive/2006/04/22/Always-set-the-_2200_applicationName_2200_-property-when-configuring-ASP.NET-2.0-Membership-and-other-Providers.aspx

Wednesday, July 07, 2010 1:30:21 PM (GMT Standard Time, UTC+00:00) | Comments [0] | ASP.NET#
Friday, May 28, 2010
Building your first REST service in ASP.NET
Friday, May 28, 2010 4:11:44 PM (GMT Standard Time, UTC+00:00) | Comments [2] | ASP.NET | wcf#
Wednesday, May 26, 2010
Right this way - http://codezest.com/archive/2008/09/06/fiddler-firefox-local-web-site-testing-easily-foxyproxy.aspx

Using Fiddler with Firefox for Local Web site Testing Easily with FoxyProxy

Technorati Tags: ,

 

Fiddler is one of the best tools to analyze web requests sent from a web site and much more. Normally, using Fiddler with FireFox is a huge pain and typically does not work well out of the box when testing localhost requests or any requests for that matter with Fiddler through Firefox.   But by using FoxyProxy which is just a nice plug-in to FireFox, it allows you to setup different proxies in FireFox very easily, and you can then hook into Fiddler's proxy quickly, and start to catch requests over the wire when testing local web applications.

Here is how to set up FoxyProxy for Fiddler and FireFox:

1. If you have not already installed Fiddler or Firefox, install these first.

2. Download & install the FoxyProxy plug-in for FireFox.

3. Now you will need to get to the FoxyProxy options.  In FireFox, at the right-hand corner you should see FoxyProxy running:

    foxyproxy_step7

     Here it's just saying that FoxyProxy is not using any proxies at the moment.

     Now right click that bar and choose "Options".

 

4. Now click the "Add New Proxy" button.  We are going to create a new proxy instance in FoxyProxy that simply points to Fiddler's proxy BowserPAC.js config file.

    foxyproxy_step1_web

5. First though we need to create a new pattern for this proxy instance and base it on a localhost wildcard so that any site that you run on localhost will be also caught when this FoxyProxy instance is running later on.

    So click the Patterns Tab then Add New Pattern button, then input the following:

 

    Pattern Name: localhost

    URL Pattern: *localhost*

    and then leave the default "Whitelist" and "Wildcards" options as is

    foxyproxy_step5_web

6. Now click on the Proxy Details tab and select Automatic Proxy Configuration.  Here is where we are going to browse to Fiddler's proxy config file.

    a) In Windows XP, Browse and select the fiddler proxy config file located at the path

        file:///c:/documents and settings/YOURNAMEHERE/my documents/Fiddler2/scripts/browserpac.js

    b) Enable only the Notification about proxy auto-configuration file loads

    foxyproxy_step3_web

7. Next, click on the General tab and fill in the following to define a name for this Proxy instance:

    Proxy Name: Fiddler

    Enabled: checked

    Animate icons when this proxy is in use (optional): checked

    Include this proxy: checked

    foxyproxy_step2_web

    You've just created a proxy instance for Fiddler inside FoxyProxy.

8. Finally click OK and go back to the "Global Settings" tab then just change the "Statusbar Activation" left-click option to "Cycles through

    modes"

    foxyproxy_step6_web

9. Now we are ready to actually use this proxy instance that we have defined in FoxyProxy.

    So in Firefox, look at the right-hand corner and click once on foxyproxy_step7 .  That's FoxyProxy running in FireFox. 

    Now click on it once.

    Notice it will change from being disabled to the pattern status step8a .

    Now we need to tell FoxyProxy which proxy instance (that we just defined in options) to start using.

    To do this, right-click the patterns box and choose the proxy instance you created.  So select "Use proxy Fiddler for all URLs":

    step8

10. FoxyProxy has now been configured for Fiddler as the proxy, and selected as the instance inside FireFox and it's now running the Fiddler proxy.

      Now we can use fiddler to capture any requests made from FireFox on any website with no problem.

      You should now be seeing results in Fiddler (example shown below) after you click around on your localhost site. 

      Here's how mine looks now that fiddler is capturing any clicks made on my ASP.NET localhost web site :

      step10b_web 

      And now we are all set. As you can see above in my example, now you can inspect a ton of things on any requests captured in Fiddler.


Wednesday, May 26, 2010 3:10:04 PM (GMT Standard Time, UTC+00:00) | Comments [0] | ASP.NET#
Tuesday, May 11, 2010
If your site is available under both http and https (i.e. if you put the certificate at the ROOT level of the site for whatever reason) then be aware that Google will index this site under both protocols.  From what I can see a fair amount of SEOs think that this will impose the duplicate content penalty, although personally I can't see it affecting your ranking too much.


The main thing you have to worry about is what happens when the user requests your page under https.  In IE, by default they may receive the "secure and non-secure content" warning, if there are any resources being requested explicitly over http.  The other is that the site will run slower, and resources won't be cached.

There are a number of solutions to this problem, and if you don't want to move your site certificate about then you can always use an ISAPI filter to intercept the requests and redirect them at IIS level.

We found IIRF, available at http://iirf.codeplex.com/ which worked straight out the box.  Note:  Although it claims to have an install you will have to do this manually, but it's really straightforward and the help is comprehensive.

In order to do the redirect above, put the following into your .ini file (which you place in the root of the site) :

# Iirf.ini
#
# ini file for IIRF
#

RewriteLogLevel 1
RewriteLog F:\Inetpub\wwwroot\website\IIRF
RewriteEngine ON
StatusInquiry ON
IterationLimit 5

RedirectRule ^/secure/(.*) https://mysite.co.uk/secure/$1 [R=301]
RewriteCond %{HTTPS} on
RedirectRule ^/(.*)$ http://mysite.co.uk/$1 [R=301]



Check the help file for instructions on this language.


Tuesday, May 11, 2010 8:36:49 AM (GMT Standard Time, UTC+00:00) | Comments [0] | ASP.NET#
Friday, April 09, 2010
There are a few subscription-only services that allow you to check for mobile browsers so that you can service optimized content, but you can also download this free file from CodePlex which claims to do the same.

I haven't tried it yet, but might be worth a look:  http://mdbf.codeplex.com/wikipage?title=Getting%20Started&referringTitle=Home

Friday, April 09, 2010 1:02:09 PM (GMT Standard Time, UTC+00:00) | Comments [0] | ASP.NET#
Wednesday, January 27, 2010
To get this, you could use an HTTP sniffing tool like Fiddler or HTTPFox, but there may be times that you have problems with this due to proxies etc being used in the company.

Thankfully there's a way to output the trace using the code below:

<system.diagnostics>
<trace autoflush="true"/>
<sources>
 
<source name="System.Net" maxdatasize="1024">
   
<listeners>
     
<add name="TraceFile"/>
   
</listeners>
 
</source>
 
<source name="System.Net.Sockets" maxdatasize="1024">
   
<listeners>
     
<add name="TraceFile"/>
   
</listeners>
 
</source>
</sources>
<sharedListeners>
 
<add name="TraceFile" type="System.Diagnostics.TextWriterTraceListener" initializeData="trace.log"/>
</sharedListeners>
<switches>
 
<add name="System.Net" value="Verbose"/>
 
<add name="System.Net.Sockets" value="Verbose"/>
</switches>
</system.diagnostics>



Acknowledgements to the following post:

http://stackoverflow.com/questions/300674/getting-raw-soap-data-from-a-web-reference-client-running-in-aspnet

Wednesday, January 27, 2010 12:20:01 PM (GMT Standard Time, UTC+00:00) | Comments [1] | ASP.NET | c##
Wednesday, January 06, 2010
I tend to use ViewState in most of my web applications, but when it comes to web sites there's rarely a need for them across all pages.  In fact for some sites it can be switched off altogether.

However, even setting this in the web.config as so

<pages enableSessionState="false" enableViewState="false">

when you View Source there's still a __VIEWSTATE hidden input field.

Expected behaviour - as documented here.

Wednesday, January 06, 2010 9:20:44 PM (GMT Standard Time, UTC+00:00) | Comments [0] | ASP.NET#
Thursday, December 17, 2009
If we need to set an ASP.NET Literal with escaped HTML encoded via javascript's escape() method, we need to use Server.UrlDecode rather than Server.HtmlDecode.

Thursday, December 17, 2009 4:14:48 PM (GMT Standard Time, UTC+00:00) | Comments [0] | ASP.NET | Javascript#
Wednesday, December 09, 2009
And yes it was.

Turns out I was adding a style in my code-behind, like this:

               spanImage.Style.Add("background", "url(" + imagesFolder + imgFileName + ")" + " no-repeat;");

The HTML in front, the span already had the background property set.

In IE, the browser happily interprets this as a last-one-in-wins, as so:

<span id="Template_ctl25_contentImageRight_spanImage" class="frame-video float-left" style="background-image:url(_includes/images/content/pictures/pic1.jpg);background:url(/RFL2010/images/about.jpg) no-repeat;;">

whereas FF 'chokes' (correctly?) and disregards the invalid second attempt to set the background.

There will be a genuine technical explanation for this, if anyone knows then feel free to leave a comment!

Wednesday, December 09, 2009 6:39:18 PM (GMT Standard Time, UTC+00:00) | Comments [1] | ASP.NET | Web Design#
Monday, December 07, 2009
See http://www.west-wind.com/weblog/posts/269.aspx Request Property Function and Example ApplicationPath Returns the a Web server relative path to your application root /WestwindWebStore/ PhysicalApplicationPath Returns a local file system path to your application root D:\inetpub\wwwroot\WestWindWebStore\ PhysicalPath Returns the full file system path to the currently executing script D:\inetpub\wwwroot\WestWindWebStore\Item.aspx CurrentExecutionFilePath FilePath Path In most situations all of these return the virtual path to the currently executing script relative to the Web Server root. /WestwindWebStore/item.aspx PathInfo Returns any extra path following the script name. Rarely used – this value is usually blank. /WestwindWebStore/item.aspx/ExtraPathInfo RawUrl Returns the application relative URL including querystring or pathinfo /WestwindWebStore/item.aspx?sku=WWHELP30 Url Returns the fully qualified URL including domain and protocol http://www.west-wind.com/Webstore/item.aspx?sku=WWHELP30 Page.TemplateSourceDirectory Control.TemplateSourceDirectory Returns the virtual path of the currently executing control (or page). Very useful if you need to know the location of your ASCX control instead of the location of the page. /WestwindWebStore/admin It's also advised to replace any "//" to "/", so that sites will work as virtual directories under Default Web Site, and also straight off a direct web site. string imagesFolder = HttpContext.Current.Request.ApplicationPath + "images/"; imagesFolder = imagesFolder.Replace("//", "/");
Monday, December 07, 2009 2:34:00 PM (GMT Standard Time, UTC+00:00) | Comments [0] | ASP.NET#
Thursday, September 03, 2009

Came a cropper with this today. Wired up a $.ajax() event to my [button] thinking that all should be well.

Couldn't understand why the ajax call was terminating BEFORE the code on the server side was being called, even though the server side code was actually being called!

Should have known this, but [button] behaves as a Submit button under all browsers except IE, if you don't mark the "type" attribute.

See http://www.w3schools.com/tags/tag_button.asp for more info

Thursday, September 03, 2009 2:33:52 PM (GMT Standard Time, UTC+00:00) | Comments [1] | ASP.NET#
Saturday, August 01, 2009
Building an ecommerce system means that we have to be that little bit more careful when it comes to ensuring the integrity of our system.

In my particular case, we need to update a customer's current order at particular stages during the purchasing process.




From this diagram, we can see that we create an Order at Stage 1, and then have to update it in Stage 3.

So this means we have to keep a note of the original OrderID that we generated in Stage 1.  In ASP.NET, we can store this in 4 ways:

Option 1

Store it in Session

We could store this in Session, which means it is completely hidden from the user and available for our application.  However, there are problems with this.  The main problem is that this system is to be deployed in a load-balanced environment, which means that we either use Sticky Sessions (and put up with the problems that come with that) or we store Session out-of-process.  I don't fancy hitting the database for a single int value, so I'm not seriously entertaining this option.


Option 2

Store it in a cookie


We can store it in the user's cookie and go looking for it afterwards.  But given such a critical part of the system, cookies are inherently unreliable and as such we have to keep looking for options.


Option 3

Store it in a hidden field


We can store it in a hidden field and then go looking for it afterwards.  Now, my particular solution uses embedded IFRAMEs (I know, I know) and we would need to get the value from the Parent frame on the client-side and push it to the server, at Stage 3.  This is possible with a bit of JavaScript, but it seems overly-hacky, so I don't fancy it.

Option 4

Transmit it via the query string


We can transmit it on the query string.  Now, even though we are on https, there's still nothing stopping a user being malicious and typing www.mysite.com/response.ashx?orderid=123 which could potentially affect the status of Order 123, whoever that belongs to.

So, we can encrypt the order id, but as this doesn't contain any particularly sensitive information by itself, all we need to do is verify that the query string was composed by us and not someone else.  To do this, we transmit an authentication token with each message.

Hashing and Salt


Hashing involves taking a message and applying a function to it which will return a hash value, such that any change to the original message would result in a different hash being returned. 

For example:

hash('fox') = xyz
Change 'fox' to 'quick fox'
hash('quick fox') = abc

Because xyz != abc, we can therefore verify that the original message must have been altered.  Simples.

The algorithm we will likely use is SHA-1, used by the US Government.

We must also pass in some 'salt' to the hashing algorithm, so that the output is unique to us.  Think of this as a private key which we will use to re-hash the original OrderID to verify the integrity of the message.

The .NET framework provides the classes we'll need in the System.Security.Cryptography namespace.  An example of calling this code is provided here, I have also copied this below.


///////////////////////////////////////////////////////////////////////////////
// SAMPLE: Hashing data with salt using MD5 and several SHA algorithms.
//
// To run this sample, create a new Visual C# project using the Console
// Application template and replace the contents of the Class1.cs file with
// the code below.
//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND,
// EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
// 
// Copyright (C) 2002 Obviex(TM). All rights reserved.
// 
using System;
using System.Text;
using System.Security.Cryptography;

/// <summary>
/// This class generates and compares hashes using MD5, SHA1, SHA256, SHA384, 
/// and SHA512 hashing algorithms. Before computing a hash, it appends a
/// randomly generated salt to the plain text, and stores this salt appended
/// to the result. To verify another plain text value against the given hash,
/// this class will retrieve the salt value from the hash string and use it
/// when computing a new hash of the plain text. Appending a salt value to
/// the hash may not be the most efficient approach, so when using hashes in
/// a real-life application, you may choose to store them separately. You may
/// also opt to keep results as byte arrays instead of converting them into
/// base64-encoded strings.
/// </summary>
public class SimpleHash
{
    /// <summary>
    /// Generates a hash for the given plain text value and returns a
    /// base64-encoded result. Before the hash is computed, a random salt
    /// is generated and appended to the plain text. This salt is stored at
    /// the end of the hash value, so it can be used later for hash
    /// verification.
    /// </summary>
    /// <param name="plainText">
    /// Plaintext value to be hashed. The function does not check whether
    /// this parameter is null.
    /// </param>
    /// <param name="hashAlgorithm">
    /// Name of the hash algorithm. Allowed values are: "MD5", "SHA1",
    /// "SHA256", "SHA384", and "SHA512" (if any other value is specified
    /// MD5 hashing algorithm will be used). This value is case-insensitive.
    /// </param>
    /// <param name="saltBytes">
    /// Salt bytes. This parameter can be null, in which case a random salt
    /// value will be generated.
    /// </param>
    /// <returns>
    /// Hash value formatted as a base64-encoded string.
    /// </returns>
    public static string ComputeHash(string   plainText,
                                     string   hashAlgorithm,
                                     byte[]   saltBytes)
    {
        // If salt is not specified, generate it on the fly.
        if (saltBytes == null)
        {
            // Define min and max salt sizes.
            int minSaltSize = 4;
            int maxSaltSize = 8;

            // Generate a random number for the size of the salt.
            Random  random = new Random();
            int saltSize = random.Next(minSaltSize, maxSaltSize);

            // Allocate a byte array, which will hold the salt.
            saltBytes = new byte[saltSize];

            // Initialize a random number generator.
            RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();

            // Fill the salt with cryptographically strong byte values.
            rng.GetNonZeroBytes(saltBytes); 
        }
        
        // Convert plain text into a byte array.
        byte[] plainTextBytes = Encoding.UTF8.GetBytes(plainText);
        
        // Allocate array, which will hold plain text and salt.
        byte[] plainTextWithSaltBytes = 
                new byte[plainTextBytes.Length + saltBytes.Length];

        // Copy plain text bytes into resulting array.
        for (int i=0; i < plainTextBytes.Length; i++)
            plainTextWithSaltBytes[i] = plainTextBytes[i];
        
        // Append salt bytes to the resulting array.
        for (int i=0; i < saltBytes.Length; i++)
            plainTextWithSaltBytes[plainTextBytes.Length + i] = saltBytes[i];

        // Because we support multiple hashing algorithms, we must define
        // hash object as a common (abstract) base class. We will specify the
        // actual hashing algorithm class later during object creation.
        HashAlgorithm hash;
        
        // Make sure hashing algorithm name is specified.
        if (hashAlgorithm == null)
            hashAlgorithm = "";
        
        // Initialize appropriate hashing algorithm class.
        switch (hashAlgorithm.ToUpper())
        {
            case "SHA1":
                hash = new SHA1Managed();
                break;

            case "SHA256":
                hash = new SHA256Managed();
                break;

            case "SHA384":
                hash = new SHA384Managed();
                break;

            case "SHA512":
                hash = new SHA512Managed();
                break;

            default:
                hash = new MD5CryptoServiceProvider();
                break;
        }
        
        // Compute hash value of our plain text with appended salt.
        byte[] hashBytes = hash.ComputeHash(plainTextWithSaltBytes);
        
        // Create array which will hold hash and original salt bytes.
        byte[] hashWithSaltBytes = new byte[hashBytes.Length + 
                                            saltBytes.Length];
        
        // Copy hash bytes into resulting array.
        for (int i=0; i < hashBytes.Length; i++)
            hashWithSaltBytes[i] = hashBytes[i];
            
        // Append salt bytes to the result.
        for (int i=0; i < saltBytes.Length; i++)
            hashWithSaltBytes[hashBytes.Length + i] = saltBytes[i];
            
        // Convert result into a base64-encoded string.
        string hashValue = Convert.ToBase64String(hashWithSaltBytes);
        
        // Return the result.
        return hashValue;
    }

    /// <summary>
    /// Compares a hash of the specified plain text value to a given hash
    /// value. Plain text is hashed with the same salt value as the original
    /// hash.
    /// </summary>
    /// <param name="plainText">
    /// Plain text to be verified against the specified hash. The function
    /// does not check whether this parameter is null.
    /// </param>
    /// <param name="hashAlgorithm">
    /// Name of the hash algorithm. Allowed values are: "MD5", "SHA1", 
    /// "SHA256", "SHA384", and "SHA512" (if any other value is specified,
    /// MD5 hashing algorithm will be used). This value is case-insensitive.
    /// </param>
    /// <param name="hashValue">
    /// Base64-encoded hash value produced by ComputeHash function. This value
    /// includes the original salt appended to it.
    /// </param>
    /// <returns>
    /// If computed hash mathes the specified hash the function the return
    /// value is true; otherwise, the function returns false.
    /// </returns>
    public static bool VerifyHash(string   plainText,
                                  string   hashAlgorithm,
                                  string   hashValue)
    {
        // Convert base64-encoded hash value into a byte array.
        byte[] hashWithSaltBytes = Convert.FromBase64String(hashValue);
        
        // We must know size of hash (without salt).
        int hashSizeInBits, hashSizeInBytes;
        
        // Make sure that hashing algorithm name is specified.
        if (hashAlgorithm == null)
            hashAlgorithm = "";
        
        // Size of hash is based on the specified algorithm.
        switch (hashAlgorithm.ToUpper())
        {
            case "SHA1":
                hashSizeInBits = 160;
                break;

            case "SHA256":
                hashSizeInBits = 256;
                break;

            case "SHA384":
                hashSizeInBits = 384;
                break;

            case "SHA512":
                hashSizeInBits = 512;
                break;

            default: // Must be MD5
                hashSizeInBits = 128;
                break;
        }

        // Convert size of hash from bits to bytes.
        hashSizeInBytes = hashSizeInBits / 8;

        // Make sure that the specified hash value is long enough.
        if (hashWithSaltBytes.Length < hashSizeInBytes)
            return false;

        // Allocate array to hold original salt bytes retrieved from hash.
        byte[] saltBytes = new byte[hashWithSaltBytes.Length - 
                                    hashSizeInBytes];

        // Copy salt from the end of the hash to the new array.
        for (int i=0; i < saltBytes.Length; i++)
            saltBytes[i] = hashWithSaltBytes[hashSizeInBytes + i];

        // Compute a new hash string.
        string expectedHashString = 
                    ComputeHash(plainText, hashAlgorithm, saltBytes);

        // If the computed hash matches the specified hash,
        // the plain text value must be correct.
        return (hashValue == expectedHashString);
    }
}


IMPORTANT: Of course, if you've looked at this code, you'll see that it appends the original salt to the generated hash value.  So this would give the game away to a potential attacker.  It would be advised that the salt is kept secretly by your application, and passed to VerifyHash() directly.







Almost forgot, credit for this article should go to Chris Varley, who made me aware of this in the first place.  Check out his blog at - http://scalethis.blogspot.com/
kick it on DotNetKicks.com




Saturday, August 01, 2009 4:29:51 PM (GMT Standard Time, UTC+00:00) | Comments [0] | ASP.NET#
Wednesday, June 24, 2009
Some quick notes on some recent research.

Specify pages which should redirect from http to https programatically, which can help out in a test/staging environment:

http://www.codeproject.com/KB/web-security/WebPageSecurity_v2.aspx

http://weblogs.asp.net/pwilson/archive/2004/12/23/331455.aspx


Cross-Domain Scripting, IFrames, SOP (Single Origin Policy) and JSONP

http://remysharp.com/2007/10/08/what-is-jsonp/

http://www.zackgrossbart.com/hackito/jsonp-sop/

http://sazbean.com/2009/02/11/json_and_the_argonauts/

http://softwareas.com/cross-domain-communication-with-iframes

Embedding a script from another domain into your page (example GoogleAnalytics):

<script type="text/javascript">
var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl " : "http://www ");
document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
</script>
<script type="text/javascript">
var pageTracker = _gat._getTracker("UA-xxxxxx-x");
pageTracker._trackPageview();
</script> 

Wednesday, June 24, 2009 9:09:17 PM (GMT Standard Time, UTC+00:00) | Comments [0] | ASP.NET#
Saturday, June 13, 2009
Not a nice and easy topic for Saturday breakfast, I'll agree.

We've been looking at improving the responsiveness and throughput of our web application.  There is a significant amount of processing and database I/O done on the application-layer, in a COM+ Server.  You can see where this is going, I'm sure.

I won't go in to the technical details, but suffice to say the logical step is to make use of Asynchronous pages in ASP.NET.  This article explains what we needed to do to make this happen, in addition to using custom threads (rather than threads straight out of the thread pool!) so that we maintain a high-level of throughput.  This was in ASP.NET 1.x.

These more recent articles (http://msdn.microsoft.com/en-us/magazine/cc163463.aspx and http://msdn.microsoft.com/en-us/magazine/cc163725.aspx) quickly outlines the new technique.

It's important to remember that this is only a step in the right direction, we need to make sure that the back-end engine itself is all tickety-boo.


Saturday, June 13, 2009 9:40:04 AM (GMT Standard Time, UTC+00:00) | Comments [0] | ASP.NET#
Friday, June 12, 2009
I've been recently putting the search engine project through it's paces.  I know that searching for Type A entities return in ~50ms (nice) but Type B entities return in ~1-2 seconds (ouch).  Having said that, I know why - the database query for Type B is horrendous at the moment.  The schema is changing drastically anyway so no point in putting that effort in now.

So I ran it via WAST and got some pretty decent results back.  While it does the job, there are obviously better tools out there.  And some of these can be expensive.

I've had a go of WAPT and been really impressed.  The trial version only allows 20 concurrent users, but you can do all sorts of things such as ramp up over time, specify random delays between 'clicks' and so on.  While my application is primarily AJAX, these are just treated as your run-of-the-mill HTTP requests.

At the end of it all I get a neat report, including interactive charts, which let me see when it reaches the maximum amount of throughput before it starts to decline.

With just Type A entities, it just keeps those pages coming.  With Type B entities...well, we know we have to improve!

Friday, June 12, 2009 9:30:04 AM (GMT Standard Time, UTC+00:00) | Comments [0] | ASP.NET#
Sunday, April 12, 2009
I must have spent the best half of a day tearing my hair out at this.


All information here - http://stackoverflow.com/questions/739859/returning-html-from-json-webservice-what-is-the-d
Sunday, April 12, 2009 10:30:27 AM (GMT Standard Time, UTC+00:00) | Comments [0] | ASP.NET | c##
Sunday, November 02, 2008

Not rocket science, just put here for my own future reference.

We want to add User Actions to each row of a datagrid.  Using the DevExpress ASPxGridView control, we create a DataColumn and then add a DataItemTemplate to this.

In the code in-front, add the following reference to code-behind:

<p><%#MakeActions(Container.DataItem)%></p>

In the code-behind, the MakeActions method should look something like th

        public string MakeActions(object dataItem)
        {
            StringBuilder sb = new StringBuilder();
            int id = Convert.ToInt32(DataBinder.Eval(dataItem, "ID").ToString());
            sb.Append("<a href=\"" + "Details.aspx?id=" + id.ToString() + "\"><img border=0 alt='Open Record' src='Images/folder_go.png'></a>");
            return sb.ToString();
        }

where DataItem is the bound data object of that row.  Clicking on this item will therefore link to the Details.aspx page, passing through the id on the querystring.

Sunday, November 02, 2008 1:52:33 PM (GMT Standard Time, UTC+00:00) | Comments [0] | ASP.NET#
Friday, March 07, 2008

If you want to run some script before or after client validation has been performed, you'll need to redirect the WebForm_OnSubmit() function.  Because of the framework we are using, WebForm_OnSubmit() was returning false immediately, so adding anything to Page.ClientScript.RegisterOnSubmitStatement was never being run.

I didn't have time to sort out where in the framework this was being set up (never mind the impact analysis), so I re-directed the Submit of the form to call my function first, then called the WebForm_OnSubmit code as normal.

if (!Page.ClientScript.IsClientScriptBlockRegistered(submitRedirect))
            {
                StringBuilder sb = new StringBuilder();
                sb.Append("<script>");
                sb.AppendFormat("var oldSubmit = document.getElementById('Form1').onsubmit;");
                sb.Append("function mySubmit()");
                sb.Append("{");
                //Your code here
                sb.Append("return oldSubmit();");
                sb.Append("}");
                sb.Append("document.getElementById('Form1').onsubmit = mySubmit;");
                sb.Append("</script>");
                Page.ClientScript.RegisterClientScriptBlock(GetType(),
                    submitRedirect, sb.ToString());
            }

Thanks to http://forums.asp.net/p/1011848/1354154.aspx for this tip.

 

 

Friday, March 07, 2008 12:43:05 PM (GMT Standard Time, UTC+00:00) | Comments [0] | ASP.NET#
Thursday, March 06, 2008

I was debugging on a colleague's Vista box the other day, and all of a sudden it kicked me out, resetting back into Visual Studio.

I remembered seeing a blog about this issue a while ago, and thankfully was able to find it again, so here it is in case you have encountered the same problem:

http://eamon.nerbonne.org/2007/11/visual-studio-2005-on-vista-timeouts.html

Thursday, March 06, 2008 6:31:53 PM (GMT Standard Time, UTC+00:00) | Comments [0] | ASP.NET#
Sunday, March 02, 2008

I'm sure you'll be aware of the different MVC frameworks currently out there on the web.  RubyOnRails, MonoRail, and the new Microsoft ASP.NET MVC framework all claim to accelerate web development by removing a lot of the mundane, monkey-work plumbing involved in creating a web project.

They also provide a clear separation of concerns, by separating the presentation, logic and data-access into discrete layers.  This has the advantage that we can then introduce unit-testing into our web project, and mock any required components.

So far so good, but personally having looked at these frameworks, I can't fit them into any projects I'm working on at the moment.  It seems to me to be a trade-off between how much control I need and how much it conforms to a typical web layout.  But that's maybe because I haven't actually tried them out on anything other than a sample project. 

Having said that I do like what they offer - GETTING THE CODE OUT OF THE PAGE

Essentially, I believe we can achieve this and also maintain a lot of the control by using the framework outlined below.

As you can see, we have our MVC-triad, with the Controller at the heart.  The Controller interacts with the View via an interface, with each concrete WebPage having a reference to the Controller.  The WebPage also CREATES the Controller object(s) it uses, passing itself (it's interface) in to the Controller's constructor.   When the User selects something on a Page, the Page then calls the required method on the Controller.  The Controller can then carry out any logic and indirectly navigate to another URL.  It does this via the View interface that it has passed to it in it's constructor.

This example shows how the Controller can be used to control application flow.  The user initiates a command on MainPage, which calls Controller.Do_Command1.  In this method, the Controller would then carry out any logic for that Command, but then wants to move to another page.  The Controller can't do this directly, so calls the Show_AnotherPage() method on IMainPage (the MainPage's view interface).  The concrete MainPage class then redirects to AnotherPage.

This example shows the Controller again controlling the application flow, this time with conditions.  You can see how this could be easily tested via mocking.

Admittedly, this framework still relies on the developer putting in a lot of the plumbing, which has been nicely encapsulated by other MVC frameworks.  However, with this you get complete control over what is happening, and you also get to use the traditional WebForms approach, complete with Viewstate et. al. which is essential for 'legacy' apps.  It also means you can pick and choose the areas in the project where you implement it. 

If I was starting a web app from scratch I'd certainly look into MVC ASP.NET and see how it would work, but if you want the benefits of code separation in your existing applications now, then this framework could be adapted very easily.

EDIT: I'm not sure I really would look into MS' MVC ASP.NET framework any more - I can't see what benefits I would get from it that I can't with this approach.

Sunday, March 02, 2008 9:58:16 PM (GMT Standard Time, UTC+00:00) | Comments [0] | ASP.NET | Patterns#
Search
Archive
Links
Categories
Admin Login
Sign In
Blogroll