Tuesday, April 22, 2008

ASP.NET tips

From Dorian: Here is a summary of the tips I’ve come up with so that you can build websites that can be deployed with precompilation.

TIP: Don’t use duplicate class names

While developing ASP.Net will keep all your pages and controls in separate dlls. It groups some controls together into one dll if they are in the same folder but at other times is keeps them separate. I’ve not determine exactly how it chooses. With this partitioning you would not very lucky run into a problem with two classes in your web site with the same name. If you attempt to merge the site into one dll (using aspnet_merge) however it would fail. I recommend naming your classes based on the directory structure using an _ instead of a slash. That will keep class names unique.

TIP: Don’t assume .ascx or .master files exist

Whilst in a development environment the .ascx and .master files exist under the website. After precompilation they do not. Never assume that they exist. The reason for doing this would be dynamic user control loading. Dynamic user control loading should be done by attempting to load the class and caching its presence. You will need to catch an exception to detect failure. I’ve spent a couple of hours looking for a work around and there is none that I can see.

TIP: Don’t reference generated classes

The following shows examples of good and bad. Whilst the bad code will work while you develop you will not be able to deploy with precompiliation.


User Control Reference
Bad:

Dim singleProductDisplay As ASP.bvmodules_controls_singleproductdisplay_ascx = DirectCast(row.FindControl("SingleProductDisplay"), ASP.bvmodules_controls_singleproductdisplay_ascx)

Good:

Dim singleProductDisplay As BVModules_Controls_SingleProductDisplay = DirectCast(row.FindControl("SingleProductDisplay"), BVModules_Controls_SingleProductDisplay)

Master Page Reference
Bad:

If TypeOf Me.Master Is ASP.bvadmin_bvadminproduct_master Then
DirectCast(Me.Master, ASP.bvadmin_bvadminproduct_master).ShowProductDisplayPanel = False

Good:

If TypeOf Me.Master Is BVAdmin_BVAdminProduct Then
DirectCast(Me.Master, BVAdmin_BVAdminProduct).ShowProductDisplayPanel = False

ASP.Net Precompilation

ASP.Net precompilation has a variety of options. You can chose to leave your aspx and ascx files in place and precompile the rest. This lets you change the HTML of those controls after deployment without recompilation. This is the same as VS2003 did it. You can chose to precompile everything. And you can go one step further to merge all the dlls into one big dll. For both VS2005 and VS2008 you can install a Web Deployment Project type into VS that will make it easier to do all this.

Thursday, April 10, 2008

Validating web service content against an XSD


Rodney has been working with a SOA implementation at one of our clients. As a result, we have changed how we deal with web services, to provide additional clarity and robustness to the WS-I standards that Microsoft utilises.

Recently we decided to split our web service definitions into 3 distinct files:

  1. An XSD to define the structure of the data (the parameters and returned data).
  2. A WSDL to define the operations that the web service will make available.
  3. A second WSDL to define the bindings (such as SOAP) used to communicate with the web service.

We create a C# interface from these three files using wsdl.exe. We create a web service in our web site, add this interface into our library and tweak the code to make it implicitly implement the interface.

This is quite easy and works very effectively. There is however one overlooked problem with this approach. Although the interface and associated classes were generated from an XSD and WSDL, the XSD is not strongly enforced from the web service. For example, the XSD may define a string maximum length:

  
   <xsd:simpleType name="String20">
      <xsd:restriction base="xsd:string">
         <xsd:maxLength value="20"/>
      </xsd:restriction>
   </xsd:simpleType>

The problem is that the web service created in this manner will allow a client to pass a string of length > 20. Not the desired outcome, since the XSD should be doing the work for us.


The reason this happens is that the web service doesn’t reference the XSD, nor does it define any attributes on a property to restrict the length. When you look at the schema automatically generated from the web service (http://myserver/mywebservice.asmx?schema=schema1), it uses the class structure to define the schema and therefore does not have the restrictions.
Validating all the values using code can be quite cumbersome and might not necessarily match the validation of the XSD. If the XSD is changed, then the code will also need to be altered to reflect the changes made to the XSD. Again, not a desired outcome.

Another approach is to validate the input data against the XSD from within the body of the web method. This can cause some problems since every individual parameter needs to be serialized to XML and compared to the XSD. Again, if the XSD changes and a new parameter is added, then the programmer has to remember to add the new parameter and ensure that it is validated against the schema. If this is a really large web service, on a high load site, this can be a significant overhead.

Enter SoapExtensions.

SoapExtensions allow you to perform validation on the incoming XML string before it is de-serialised into CLR objects.

Once Only Setup

SoapExtensions require a once only setup to create custom attribute classes stored in your reusable library.

A number of classes derived from the SoapAttribute abstract class need to be created. Some will be used to decorate the web service class to define the schemas while others are required to decorate the web methods to force validation. Coded correctly, these allow you to pass in a specific XSD or a directory full of XSD files to validate against at runtime.

In addition you will need to create a ValidationExtension class to perform the actual validation using the .NET XML library on the incoming XML message stream and XSD collection.
The full detail of the implementation can be found at http://msdn2.microsoft.com/en-us/magazine/cc164115.aspx.

Web Service Specific Setup

Armed with these new classes, validation on the incoming XML stream is now achieved via the following simple steps:

1. Setup the web.config to use the new ValidationExtension class

<webServices>
    <soapExtensionTypes>
        <add type="MaxSoft.Web.Services.Validation.ValidationExtension, MaxSoftDllpriority="1" group="High" />
    </soapExtensionTypes>
</webServices>

2. Decorate your class to point to the XSD file(s)

If you follow the msdn article above you will be able to point to a specific XSD, or a directory of XSD schema files. Both are shown below.

[ValidationSchemaCache("~/Schemas/")]
[ValidationSchema("~/Schemas/MySchema.xsd")]

3. Decorate your web method to trigger the actual validation

[Validation(CheckAssertions = false)]

4. Optionally you can add more complex validation to compliment the XSD

If CheckAssertions is set to true, you can provide additional validation logic which cannot be expressed in the XSD schema. An example is shown below:

[Assert("(//t:length > //t:width)", "Length must be greater than width")]

Although the capability is there, we don't use this feature. In fact, we have set the default value of CheckAssertions to false.

Not only does CheckAssertions add overhead to the validation, but it stores important business logic in an attribute of the web method rather than in the entity library, obviously poor practice. This additional business logic should be in a library for reuse, testability and for a raft of other reasons.

Note:

This code in Microsoft's was written in 1.1 and you need to make some significant changes for it to run against newer versions of .Net as many of the classes are now marked as obsolete.

Wednesday, April 2, 2008

HP RDYMSG DISPLAY is fun

It was April 01 a few days ago, and I decided to pull a small prank on the office. Unfortunately, it was a little subtle(No one has commented yet), but it was fun to put the code together.

The prank is based on some old l0pht code hanging around on this web site: http://www.irongeek.com/i.php?page=security/hphack which sent some HP Printer Job Language code to a networked printer, changing the "READY" prompt to whatever you choose.

I guess the next logical step is to interrogate the Active Directory and automate the process ... or more evil would be to interrogate an IP range, since that would give you access to printers you cannot access via the AD. Evil.

The entire HP command set I found here: http://printers.necsam.com/public/printers/pclcodes/pcl5hp.htm

Guys, don't do this at work.

As always, blogger eats non breaking spaces, spaces and pretty much any type of indentation I can think of. [Edit and replace all spaces with &#160;]


using System;
//using System.Collections.Generic;
using System.Text;
using System.Net;
using System.Net.Sockets;

namespace HPPrnDisp
{
    class hp
    {
        const int HP_PJL_PORT = 9100;
        const string HP_RDYMSG_HEADER = "\u001B%-12345X@PJL RDYMSG DISPLAY = \"";
        const string HP_RDYMSG_FOOTER = "\"\r\n\u001B%-12345X\r\n";

        // Changes the READY display text for a range of HP Printers
        public static int Main(string[] args)
        {

            Console.WriteLine("HP Display Hack -- sili@l0pht.com 12/8/97");
            Console.WriteLine("-- Re-coded by Kent Bolton 02 APR 2008 --");


            // Ensure that there are only two arguments
            if ( args.Length != 2 ) {
                Console.WriteLine("Usage: printer \"message\"");
                Console.WriteLine("\tMessage can be up to 16 characters long (44 on 5si's)");
                return 1;
            }

            // Process the arguments
            String host = args[0];
            String message = args[1];

            // No more than 44 chars - not sure what happens if there are more
            if (message.Length > 44)
            {
                message = message.Substring(0, 44);
            }

            // Let the user know we know what is needed
            Console.WriteLine("Hostname: {0}", host);
            Console.WriteLine("Message : {0}", message);

            // Set up the IP Connections ...
            IPAddress addr = null;
            IPEndPoint endPoint = null;
            try
            {
                addr = Dns.GetHostEntry(host).AddressList[0];
                endPoint = new IPEndPoint(addr, HP_PJL_PORT);
            }
            catch (Exception e)
            {
                Console.WriteLine("Unknown host: " + e.ToString());
                return 1;
            }

            // Format strings
            StringBuilder textOut = new StringBuilder(100);
            textOut.Append(HP_RDYMSG_HEADER);
            textOut.Append (message);
            textOut.Append(HP_RDYMSG_FOOTER);

            // Now everything is set up, push the string through as an ASCII byte stream
            Console.WriteLine("Connecting ...");
            try
            {
                // Prepare basic socket and encoding stuff
                Socket sock = null;
                System.Text.ASCIIEncoding encoding = new System.Text.ASCIIEncoding();

                // Open socket and pump out the bits
                sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.IP);
                sock.Connect(endPoint);
                sock.Send(encoding.GetBytes(textOut.ToString()));
                sock.Close();
            }
            catch (Exception e)
            {
                Console.WriteLine("IO error: " + e.ToString());
                return 1;
            }

            // Happy message and bail with no error code
            Console.WriteLine("Sent {0} bytes", textOut.Length);
            return 0;


        }
    }
}


Monday, March 31, 2008

VS2008 Express - Launching nUnit on F5

Here is the How To:

Add these two lines into the section of your project .csproj.user file.

<StartAction>Program</StartAction>

<StartProgram>C:\DEV TOOLS\Program Files\NUnit 2.4.3\bin\nunit.exe</StartProgram>


Pressing F5 will launch NUnit now and start debugging.


Thursday, March 27, 2008

Web development 2008 ! We've come a long way ! Now where’s Notepad ?

Darren, our framework developer who plays with all the new cool tech had a play with VS2008 web development, and the results were not very pretty.

Today I had a simple task; Make a small application to demonstrate the new features in Visual Studio 2008 for Webform development. I had attended the Brisbane "Heroes Happen 2008" Microsoft launch event the previous day and thought "A quick 20 minute job". Hmmmm...

The Agenda...

  1. The impressive UpdatePanel
  2. Intellisense in JavaScript editing
  3. Debugging JavaScript in Visual Studio

I should say at this point that I am a dyed-in-the-wool Winforms developer although I have developed a fair few websites in my time. I've built web apps (mostly modestly sized affairs) using everything from Visual Interdev (remember that?), Notepad, and all .NET Visual Studio editions. I've done it but I have to say I don't like it. Why? If I'm brutally honest I hate the pace of development, the fact the tools aren't properly integrated and browsers that can't agree how to render anything useful; These are just my top 3 hates.

Anyway, yesterday I was truly impressed with the demos and found myself thinking maybe web development could be fun at last...

How wrong I was.

I had about 90% of the demo complete well within my initial estimate and then proceeded to put the breakpoint on the JavaScript code in the page and entered a world of pain. I use Firefox as my main browser and, naturally, VS 2008 JavaScript debugging doesn't work there. Okay, set IE 7 as my default browser for the web project, problem solved... well, no. Hmmm... Reboot? No. Repair install of Visual Studio? No. Another reboot? No. Go moan to someone else about how s@#t web development is? Worked like a charm.

Somebody please explain how bringing someone over to your PC and showing them how something doesn't work can suddenly kick it into life. I say this, but it didn't work the *first* time, just the second and every time since.

Its things like this that give me killer headaches and make me want to go back to the nice cosy world of framework development...

Aside from a nice preview of the 'design' and the (admittedly) cool CSS stuff there aren't a lot of compelling reasons to spend a large wad of cash rather than just use notepad. I mean, if I'm going to get stressed and generally harassed by technology that isn't reliable, doesn't work and then magically cooperates I may as well start in the position of expecting things to be tough and just use everyone's favourite free web development tool.

PS I don’t really mean that we should be using notepad because it would be really frustrating. The issue is that Visual Studio 2008 is so close, but so frustratingly far from being a great web development environment.

PPS I love everything else about Visual Studio.

Wednesday, March 26, 2008

FXCop and string culture

Rules are great, unless blindly followed.

I thought I would just send this out as a reminder. While everyone is eager to do right by FxCop we should not forget to use the Invariant Culture where appropriate.

FxCop will complain about any string formatting without an explicit culture. If you are formatting for internal use and not for display you really should consider using CultureInfo.InvariantCulture. This is particularly important if you plan to consume the string with software. Using the current culture would mean the string potentially could not be consumed by another server/PC. For example, if you used BCMax in Dubai it would have a different culture to the StrataMax server running here.

So if it is an internal string not for display or to be interpreted by software anywhere consider doing this:

receiptNumber.ToString("#", CultureInfo.InvariantCulture);

Instead of this:

receiptNumber.ToString(CultureInfo.CurrentCulture);


Thursday, March 6, 2008

Exception handling in ASP.Net with Anthem and Web Services

Dorian, our CIO, has been busily hammering the VB.NET based shopping cart selected for a large client into shape.

Anthem.NET is a free, cross-browser AJAX toolkit for the ASP.NET development environment that works with both ASP.NET 1.1 and 2.0. http://anthem-dot-net.sourceforge.net/

Please ignore lack of indenting ... blogger keeps eating my directives. Kent Bolton

Before Anthem it was enough to put an error handler in the Global.asax on the Application_Error event to log the error and use the tag in the web.config to redirect errors to a common error page.

Anthem complicates things.

In Anthem it is possible to have Ajax call backs to the web page, controls or even custom methods decorated. Exceptions could be raised on the methods called or even on methods not directly called by these methods such as load events for controls that Anthem causes to load dynamically.

If an error occurs during an Anthem Callback the exception will not be caught by Application_Error and won't follow instructions. On the server side the only thing you can do to catch an Anthem Error is catch it on the page using a Page_Error method. This method will also catch regular page exceptions thus preventing Application_Error from firing. It may look something like this:

Private Sub Page_Error(ByVal sender As Object, ByVal e As EventArgs) Handles MyBase.Error
Response.Redirect("http://somewhere/")
End Sub

In order to avoid putting this method on every page it must be placed on the base page class for the entire website. There is a small downside to this which is a page can no longer define its own Page_Error method because control would be lost in the event execution chain when a Response.Redirect is started (it aborts the thread). This is however a necessary evil as there doesn’t seem to be another way to catch Anthem exceptions.

Because we are on the topic of Anthem and error handling here is a couple more useful facts. You can catch errors in Anthem call backs on the client by adding a JavaScript method to pages calls Anthem_Error. If a server side error occurs in an Anthem callback any previously registered JavaScript to execute will still be executed. For example if you had an Anthem button which looked like this:

Protected Sub btnNext_Click(ByVal sender As Object, ByVal e As System.Web.UI.ImageClickEventArgs) Handles btnNext.Click

SomeHiddenPanel.Visible = True

Anthem.Manager.AddScriptForClientSideEval("if (typeof document.someform != ""undefined"") { documentsomeform.submit(); }")

End Sub

And after this code executed a server side error occurred (perhaps in rendering SomeHiddenPanel) the JavaScript added would still be executed. It is important to write such JavaScript to be tolerate of an error occurring. The code above checks for the existence of the form which SomeHiddenPanel would make visible before it calls submit().

Web services are another nasty for exception handling. Basically, you need to try catch every web service method and handle all exceptions. The same error logging routine could be use by web services and page errors if it was tolerant of information that may not be present on web service requests. View state is one example. Don’t expect to find that in a web service.