Friday, January 19, 2007

This is part two in a series of posts about ASP.Net Controls.

ASP.Net Controls, Part 2: Composite Custom Controls - some assembly required

Now these controls are able to be designed to have some reuse and life outside one particular project.  The up side is that you can piece together odds and ends from System.Web.Ui.WebControls just how you like them, and as per normal the actual rendering to HTML is left to ASP.Net.

The down side is you just start with a class, descend from WebControl and piece it together in code.

Here's a bare bones sample to illustrate the concept, but first some notes:

  • Import System.ComponentModel for the attributes on the properties.  This is how they display in your Visual Studio properties window
  • Use actual controls as backing variables for the properties.  You want to be holding instances of the controls you will display in your class, and then abstract their properties behind your own properties.
  • CreateChildControls is where you assemble what will eventually be rendered down to the browser by adding to the Controls collection exposed by the base class.
  • Call EnsureChildControls.  A lot.  :-) 
  • This sample control no behaviour.  Use += / AddHandler to wire up your controls to event handlers.  Omitted for clarity.
using System.Web.UI;
using System.Web.UI.WebControls;
using System.ComponentModel;

namespace MyProject.Controls
{

    public class CompositeControl : System.Web.UI.WebControls.WebControl
    {

        TextBox txtInput = new TextBox();
        Button cmdSubmit = new Button();
       
        protected override void CreateChildControls()
        {
            EnsureChildControls();

            txtInput.Text = DefaultText;
            Controls.Add(txtInput);

            cmdSubmit.Text = "Submit";
            Controls.Add(cmdSubmit);

            base.CreateChildControls();
        }

        protected override void Render(HtmlTextWriter writer)
        {
            base.Render(writer);
        }

        [Category("Appearance"), DefaultValue("Enter text here")]
        public string DefaultText
        {
            get
            {
                EnsureChildControls();
                return txtInput.Text;
            }
            set
            {
                EnsureChildControls();
                txtInput.Text = value;
            }
        }

    }
}

Friday, January 19, 2007 1:55:26 PM (AUS Eastern Standard Time, UTC+10:00)  #    Disclaimer  |  Comments [0]  | 

It occurred to me that ASP.Net has been a topic missing from my blog, so to rectify the situation I'm posting three pieces on Controls in the ASP.Net space.  Took me ages to get this straight in my own head so with any luck it will make sense here.

Part 1:  Web User Controls (ASCX files), the low hanging fruit

What better place to start.  These are the odd one out in the ASP.Net control space for a couple of reasons. 

  • While it is possible, they don't do reuse between projects well. 
  • They do have a drag and drop design time experience!
  • You drag them onto a page from the Solution Explorer, not the toolbox.

First thing to note is they have their own life cycle after Page_Load, and it feels a little like Server.Execute()

While they do have their own code behind class that inherits from System.Web.UI.UserControl, I try to not use too many properties on the class because I feel it gives a misleading impression in implementation.  Just say the thing has a text() property and in my Page_Load I set the text to something, well after Page_Load the thing can do what ever it wants to and then I have to go looking for what happened to my value for the text() property.

Re-use between projects requires some planning because they are not seperate from your web project, but re-use inside the same project is what they are best at.  Things like a navigational device, a customer lookup widget or something that makes an appearance more than once are good examples.  Pull them out, glue them together with a Web User Control, drop them back in.  Easy. 

Friday, January 19, 2007 1:00:54 PM (AUS Eastern Standard Time, UTC+10:00)  #    Disclaimer  |  Comments [0]  | 
 Wednesday, January 17, 2007

Oddly enough, this blog post is to remind me of a discussion I had with a friend tonight about SOA;

Here's one to not tackle after pork, sausage, squash and cabbage (no, really! *burp*): Does it count if it is not over SOAP Web Services?  What if your services are in the same app domain as in a plugin architecture?

The benchmark I lean on here is:  If our systems agree on schema & contract over classes & data then we are on the path to SOA.

Which brings us back to Jean Paul de Sousa.  The story of the small ISV in the corporate space can be -to lean on a canonical example- recieving a requiment like: Here's our definition of "The Customer" <insert schema here>. 

The good news is for the ISV maybe I don't care how you store your customer, so long as the system boundaries talk with respect to the agreed on schema for a customer.  The bad news is for, again to pick on a common example, what if wearing the ISV hat I store the customer_name as one field, however "The Customer" definition has it as first_name & last_name.

As the ISV, the temptation is to split our customer_name field at the service boundary translation code on the first occurance of space, so that "John Smith" becomes "John" "Smith".  "Jean Paul de Sousa" however should be split on the second space! 

My ETL experience and ponderings, coupled with the non-trivial digestion task I am faced with, bring me to the conclusion that this isn't a new problem at all!  Just a new place for an old problem to live.  Awesome.  Schema and contract it is ;-D

Listening To: Almost Crimes by Broken Social Scene from You Forgot it in People

Tuesday, January 16, 2007 11:47:04 PM (AUS Eastern Standard Time, UTC+10:00)  #    Disclaimer  |  Comments [2]  | 

I'm just getting into the second week of some annual leave.  Note to my employer:  Don't let me remember just how... how... relaxing, yes!  that's it, relaxing annual leave can be, I digress...

Today's story is that for the sake of nothing more than the coffee was good and that else I would spend the day on the couch, I went to wire up a network in a friends store today.  It was also for the sake of making sure I could still handle on some of the basics like crimp RJ45 ends on CAT-5CAT-6 and so on.

I don't think my friend is able to get just what I get from it.   It's something like brewing your own beer and juicing your own oranges, something organic.  For four hours I got to get dusty in the problems of his business.  Very interesting.  He uses a paper diary even!

Consider this little thought experiment:  So your business involves printing peoples photos.  Way back in the day, the average guy shot 24 or 36 frames on some Kodak Gold and got the role printed at the minilab, because otherwise it was just a tin - it wasn't "photos" until you printed them.

Then the digital world hits and 10x the people own cameras, PDAs and phones.  Now each frame is a photo in its own right as soon as you take it!  You could print it on your inkjet, email it, post it to Flickr, show it on your ipod, show it on your media centre, show it on your mobile, et cetera...

Now so many more people are taking digital pictures, but instead of everyone printing every frame, about 15% of people taking digital images are making any prints at all.  Smaller slice of a bigger pie.  This wasn't lost on Agfa, Fuji are watching and it isn't lost on Kodak and Konica Minolta.  Time to give the business model a rethink... but how?!?  As a small business you may not get too many bites at it! 

(Love to hear any of your ideas - post a comment!)

Tangentally, this reminds me of a tech firm I once had chance to experience.  Management thought the one performance review form could be used for both a Sales Exec/BDM role and a junior network admin role.  Turns out that "Networking" means different things to these two roles.  Whoda thunk it?  *rolls eyes*

Sorry for the cryptic title, it's to subtly encourage your thoughts down an empirical path...

Listening To: spose iz Lily innit, a'rite?

Tuesday, January 16, 2007 11:01:59 PM (AUS Eastern Standard Time, UTC+10:00)  #    Disclaimer  |  Comments [1]  | 
 Tuesday, January 09, 2007

I've just watched a show on Discovery (yay, annual leave :-) about life being elsewhere in the solar system (europa's seas) or elsewhere in the universe, and the search thereof.

I was happily digesting a sandwitch and Tooheys when a silvery senior figure from NASA tried to quell the discussion in saying that single cellular life would be a significant find.  Speculation followed from the usual sources, with counter arguments from again, usual sources.

Please people!  enough.

Once you start a back-and-forth on semantics of proof (not to mention some very poor web design there guys!) you are already on the wrong path.

Bill Bryson concluded that life doesn't want to be much.  No social commentary intended, just his noting that of all living creatures, only one species has been so motivated to write books about living species.  The rest are just content to be moss, plants and etc.

Are Bryson and our aforementioned NASA silvery senior on the right path?  Maybe.  In either case, consider if "life" were substituted with "a mere inevitable chemical reaction"; if so Bryson should be careful to attribute "want", and our silvery senior should be careful when attributing "life" to it also.

Jon Kabat-Zinn noted that our subspecies name of Homo sapiens sapiens is no mistake, sapiens derives from the latin sapere, to taste but also to know.  The word sapient derives here also- meaning wise and insightfil, but isn't in wide use in present day English - maybe there is less call for it these days? :-)  But seriously, the use of sapiens sapiens implies a meta awareness.  Man who knows and knows that he knows as Kabat-Zinn puts it. 

And this is exactly where the wheel comes off the cart for the extraterrestrial life argument.  Where can the term life be defined outside the realm of subjective experience?  To be alive and not know it renders me unable to describe life.  If we sustain that line of reasoning one step further, any concept dependant on a definition and description of life, be it religion or God, can only itself be described in the context of a subjective description.

While ever discussion of these matters is bound by shifting semantics of language, you will find me perched on an esky at the sideline tossing my empties into the skirmish.

Tuesday, January 09, 2007 1:39:04 PM (AUS Eastern Standard Time, UTC+10:00)  #    Disclaimer  |  Comments [5]  | 
 Wednesday, January 03, 2007

c'on, log files are for nerds... the Web 2.0 way to know if no one is reading your blog is look at your keyboard. 

If the keyboard is attached to a past-its-use-by-date bottm-o'-the-range Inspiron, no one reads your blog. 

Spolsky, Mitch, etc, we get it already!  people read your blog. :D

Here's the oil lads, the next two people to blog about my blog get:

  • A 500ml Orange Juice.
  • An empty DL size envelope with the word "expenses" autographed on it by yours truely.
  • A selection of push pins suitable for pinning envelopes etc to workspace walls. 
  • Nov 7 edition of The Bulletin mag.  This is the sweetner, because although it is in mint condition, I boosted it from someone else who after this time may not even realize he had it.
  • One of those little single-serve soy sauce fishes you get with take-out sushi.

As is the norm, your disclosure is all your decision.  I will deliver your booty regardless.

Wednesday, January 03, 2007 2:31:36 PM (AUS Eastern Standard Time, UTC+10:00)  #    Disclaimer  |  Comments [7]  | 
 Tuesday, December 12, 2006

OK,  so I was trying to kick this Code Generation bent I have been on of late, but… opportunity knocked yesterday for a query where one of the possible solutions involved codegen and I was weak  :-)

 

So imagine we have some convieniently breif and neatly anonymized canonical example like the following table:

 

SELECT customer_id, order_id

FROM OrderCustomer

 

 

customer_id

order_id

1

32

1

33

1

34

2

821

2

831

2

851

2

861

2

871

2

911

3

1

3

2

3

3

3

4

3

5

 

Now imagine the project is to remove all the rows from this table, except the lowest number order for  each customer.  The first step is to write a query to exceptionalize these rows:

 

SELECT customer_id, min(order_id) AS AS LowestOrderID

FROM OrderCustomer

GROUP BY customer_id

 

customer_id

LowestOrderID

1

32

2

821

3

1

 

The next step is to select some string literals with the original query so that the result is valid T-SQL

 

SELECT 'DELETE FROM OrderCustomer WHERE customer_id = ', customer_id,

       'AND order_id > ', min(order_id) AS LowestOrderID

FROM OrderCustomer

GROUP BY customer_id

 

Tangentally the little unit of joy in this whole experience for me is that  the <Ctrl-T> keyboard shortcut is the same in SQL Server 2005 Management Studio as it was back in Query Analyzer. 

 

So, hit <Ctrl-T> to output the results window as Text, then F5 to return the results to get something like:

 

DELETE FROM OrderCustomer WHERE customer_id =  1           AND order_id >  32

DELETE FROM OrderCustomer WHERE customer_id =  2           AND order_id >  821

DELETE FROM OrderCustomer WHERE customer_id =  3           AND order_id >  1

 

(2 row(s) affected)

 

(5 row(s) affected)

 

(4 row(s) affected)

 

Select all the DELETE FROM statements and copy and paste them into a new query window, then F5 to remove the rows.

 

Look at the table to test the result:

 

SELECT customer_id, order_id

FROM OrderCustomer

 

customer_id

order_id

1

32

2

821

3

1

 

Tuesday, December 12, 2006 8:35:10 AM (AUS Eastern Standard Time, UTC+10:00)  #    Disclaimer  |  Comments [0]  | 

This is something I have noticed in a lot of the samples around plugin architecture (for example).  The common theme is to reflect over the DLLs in an path and load the types that impement a certain interface.  Fair enough so far.  Once you have a collection of pointers to entry points you have plug-ins... but you also have an attack vector.

These examples need to flesh out the scenario of testing the plug-in for authenticity imho.

My gut feeling is I want an X.509 cert in there somewhere as a pre-shared secret, but I don't quite have the full picture in my head just yet, should that tie in with strong naming assemblies, or be seperate additional layer.  Suggestions welcome :-)

Tuesday, December 12, 2006 8:24:19 AM (AUS Eastern Standard Time, UTC+10:00)  #    Disclaimer  |  Comments [0]  | 

Love him or hate him, the late Bill Hicks had a way with words.  From his 1989 live recording, Sane Man...

Wouldn't you like to see a positive LSD story on the news? To hear what it's all about, perhaps? Wouldn't that be interesting? Just for once?

"Today, a young man on acid realized that all matter is merely energy condensed to a slow vibration … that we are all one consciousness experiencing itself subjectively. There's no such thing as death, life is only a dream, and we're the imagination of ourselves. Here's Tom with the weather."

Tuesday, December 12, 2006 8:10:15 AM (AUS Eastern Standard Time, UTC+10:00)  #    Disclaimer  |  Comments [0]  | 
 Monday, December 04, 2006
This is the last one in this current thread for a while :)

By the end of the last example we had code that would discover what stored procs were in a SQL Server database and generate a managed wrapper library that could be extended.

The next step it occured to me was to be then generate an assembly from this source and load it into the currently executing program.  The MSDN pages I have linked to have some good samples.

I'm adding this one to my toolbox of hammers looking for a nail.
Monday, December 04, 2006 3:21:23 PM (AUS Eastern Standard Time, UTC+10:00)  #    Disclaimer  |  Comments [0]  | 
 Monday, November 27, 2006

(this post is an appendix to my prior post on Basic Code Generation with XSLT, because I forgot this bit the first time!)

I just wanted to expand on thsi line in the VB code part of the template:

Public Class DatabaseAccess
    Inherits ConvienientBaseClass

Including a (facetiously named) base class for the generated code was no accident.  It provides a good way to seperate the generated code from the human written code.  All the generated code should be the tedious, repetitive, error prone code.  Exception handling, transaction enrolement, logging etc should be implemented in the base class so it can be ripped out if need be and of equal importance; the generated code can be re-generated without clobbering any human written code.

But that isn't very .NET 2.0 now is it.  We can extend the concept of seperating the generated code from any human written parts by changing our template to generate partial classes:

Public Partial Class DatabaseAccess
    Inherits ConvienientBaseClass

 

 

Monday, November 27, 2006 9:00:59 PM (AUS Eastern Standard Time, UTC+10:00)  #    Disclaimer  |  Comments [0]  | 
 Friday, November 24, 2006

This was meant as a "Part II" to my prior post on generating Text, HTML & more XML with XSLT.  The point for today's post is that source code files are text files.  The example I am thinking of is generating a library of VB.NET wrapper classes for the stored procedures in a SQL Server database.

(I've also ticked the Continuous Integration category for this post.  It wouldn't be hard to think of a scenario where a build process would generate a library from a reference database on the check-in of a stored proc script, then deliver the latest rev of the library to the developers, anyway...)

Step one would be fetch the meta data about the stored procedures, for example: 

SELECT procs.Specific_Name, params.Parameter_Name, params.Data_Type, params.Parameter_Mode
FROM
INFORMATION_SCHEMA.ROUTINES procs
LEFT
JOIN INFORMATION_SCHEMA.PARAMETERS params
ON params.Specific_Name = procs.Specific_Name
WHERE
procs.Routine_Type = 'PROCEDURE' AND
procs
.Specific_Name NOT LIKE 'dt_%'
FOR
XML AUTO

Once we add a document node to this, we will have a document that contains many element sets like this one:

  <procs Specific_Name="GetContactByID">
    <params Parameter_Name="@ContactID" Data_Type="nvarchar" Parameter_Mode="IN"/>
    <params Parameter_Name="@ContactGUID" Data_Type="int" Parameter_Mode="INOUT"/>
    <params Parameter_Name="@Found" Data_Type="bit" Parameter_Mode="INOUT"/>
  </procs>

I think that's all we need to get started.

Step two is to transform this data into VB.NET code.  For today's example it suits me to generate two files: 

  1. One will contain classes that wrap ADO.NET calls to the database
  2. The other will provide types that wrap properties for passing into and out of the first

The text of the first template is as follows

<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="
http://www.w3.org/1999/XSL/Transform" version="2.0">
  <xsl:output method="text" indent="yes" />
 
  <xsl:template match="/">

Imports System
Imports System.Data.SqlClient
Imports System.Data
Public Class DatabaseAccess
    Inherits ConvienientBaseClass
    Public Sub New(ByVal cn As SqlConnection, ByVal trn As SqlTransaction)
        MyBase.New(cn, trn)
    End Sub
   
    <xsl:apply-templates select="/database/procs" />
    
End Class
  </xsl:template>
 
  <xsl:template match="procs">
    '
    ' Wraps stored proc: <xsl:value-of select="@Specific_Name" />
    '
    Public Function Execute<xsl:value-of select="@Specific_Name" />(ByVal params As <xsl:value-of select="@Specific_Name" />Struct) _
                                             As <xsl:value-of select="@Specific_Name" />Struct
        Dim exec As New SqlCommand
        Dim param As SqlParameter
        With exec
            .CommandText =
"<xsl:value-of select="@Specific_Name" />"
            .CommandType = CommandType.StoredProcedure
            .Connection = MyBase.DatabaseConnection
            .Transaction = MyBase.CurrentTransaction
        End With
  <xsl:apply-templates select="params" /> 
        Try
            If Not exec.Connection.State = ConnectionState.Open Then exec.Connection.Open()
            exec.ExecuteNonQuery()
        Catch ex As Exception
            Throw
        Finally
            If Not exec Is Nothing Then exec.Dispose()
        End Try
    End Function
  </xsl:template>
 
 
  <xsl:template match="params">
        param = exec.CreateParameter
        With param
            <xsl:if test="@Parameter_Mode='INOUT'">.Direction = ParameterDirection.Output</xsl:if>
            <xsl:if test="@Parameter_Mode='IN'">.Direction = ParameterDirection.Input</xsl:if> 
            .DbType = DbType.<xsl:value-of select="@Data_Type" />
            .Value = params.<xsl:value-of select="@Parameter_Name" />
            .ParameterName =
"<xsl:value-of select="@Parameter_Name" />"
        End With
        exec.Parameters.Add(param)
 
</xsl:template>
 
</xsl:stylesheet>

And the text of the second template is as follows:

<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="
http://www.w3.org/1999/XSL/Transform" version="2.0">
  <xsl:output method="text" indent="yes" />
 
  <xsl:template match="/">

Imports System
    <xsl:apply-templates select="/database/procs" /> 
  </xsl:template>

 
  <xsl:template match="procs">
'
' Parameters for method: Execute<xsl:value-of select="@Specific_Name" />
'
Public Class <xsl:value-of select="@Specific_Name" />Struct
   <xsl:apply-templates select="params" />  
End Class 
  </xsl:template>
 
  <xsl:template match="params">
   Private _<xsl:value-of select="substring(@Parameter_Name,2,string-length(@Parameter_Name)-1)" /> As Date
 Public Property <xsl:value-of select="substring(@Parameter_Name,2,string-length(@Parameter_Name)-1)" />() _
                                                                     As  <xsl:value-of select="@Data_Type" />
    Get
      Return _<xsl:value-of select="substring(@Parameter_Name,2,string-length(@Parameter_Name)-1)" />
    End Get
    Set(ByVal Value As <xsl:value-of select="@Data_Type" />)
      _<xsl:value-of select="substring(@Parameter_Name,2,string-length(@Parameter_Name)-1)" /> = Value
    End Set
   End Property
 
</xsl:template>
 
</xsl:stylesheet>

To my eye, these look more like VB.NET source files than XSLT templates.  That's because they started life as .vb files.  Then I renamed them .xslt and started inserting the XSLT tags in places where I needed substitution from the XML source. 

To emphasise the hybrid-ness (is that a word) of these files, I have highlighted the VB.NET parts blue and the XSLT parts green, rather than keeping the VB.NET syntax highlighting.

Some breif thoughts: 

  • The value of this to my mind is in not having to hand code *every* one.  You hand code one, then generate the rest - in theory this reduces the opertunity for bugs which should be the focus of the exercise. 
  • Maybe this may find a home in a long-running project to have this as part of the build process, or maybe as part of some tooling focused on small one-off
  • This are commercial products that do code gen based on XSLT.  I haven't used them, but they may well be better than my home-brew sample :-)
  • Maybe useful for trainers, or producting samples for demos etc.  I'm thinking now about times where there may be a need to generate side-by-side VB.Net, C# & Java sample code for example.
  • Also include comment blocks that are readable by NDoc!

[edit: added line breaks for formatting.]

Friday, November 24, 2006 9:58:13 AM (AUS Eastern Standard Time, UTC+10:00)  #    Disclaimer  |  Comments [0]  |