Speaking schedule for the next quarter – level 000

On April 1st, I’ll be giving a talk on web services to a groups of students in a
.Net web development class at Texas A&M University.  Then on May 2nd, I’ll
be presenting to the Texas A&M .Net User Group an overview of the Dell IDD,
a smart client shell to host multiple applications.  Then I’ll move on to show
how to implement a plugin design in web applications.

Trying out the Model-View-Presenter pattern – level 300

After reading Martin
Fowler’s Model View Presenter article
, I did some deep thinking about how to
implement that pattern.  The purpose of the pattern is to decouple UI process
logic from the UI container.  If  you think about the code-behind classes in
your UI, all the examples we see have button-click handlers performing process
logic.  Below is my first attempt at creating a behavior layer for UI
testability and portability:

I’ll present this example using a simple ASP.NET page:

<%@ Page language=”c#” Codebehind=”default.aspx.cs”
AutoEventWireup=”false” Inherits=”ModelViewPresenter.WebForm1″
%>
<!DOCTYPE HTML PUBLIC “-//W3C//DTD HTML 4.0 Transitional//EN”
>
<HTML>
 <body>
  <form id=”Form1″ method=”post”
runat=”server”>
   <asp:textbox id=”txtName”
runat=”server”>
   </asp:textbox><asp:button id=”btnAdd”
runat=”server” Text=”Add Name”></asp:button>&nbsp;
   Number of
items:
   <asp:label id=”lblCount”
Runat=”server”></asp:label><br>
   <asp:datagrid
id=”dgdNames”
runat=”server”>
    <Columns>
     <asp:ButtonColumn
CommandName=”delete” ButtonType=”PushButton” Text=”Remove”
/>
    </Columns>
   </asp:datagrid></form>
 </body>
</HTML>

This page is used to maintain a list of names.  Type in a name and click the
add button; remove a name from the list.  The current names are shown in a
datagrid, and the count is shown next to the button.  Here is the
code-behind:

   14 public class WebForm1 : System.Web.UI.Page,
Presenter.INamesView
   15 {
   16     protected
System.Web.UI.WebControls.TextBox txtName;
   17     protected
System.Web.UI.WebControls.Button btnAdd;
   18     protected
System.Web.UI.WebControls.DataGrid dgdNames;
   19     protected
System.Web.UI.WebControls.Label lblCount;
   20 
   21    
private Presenter.NamesPresenter
_presenter;
   22 
   23     private
Model.Names _names {
   24         get { return
(Model.Names)ViewState[“names”]; }
   25    
    set { ViewState[“names”] = value; }
   26     }
   27 
   28    
private void
Page_Load(object sender, System.EventArgs
e)
   29     {
   30         _presenter = new Presenter.NamesPresenter(this);
   31    
    if(!IsPostBack) {
   32             _presenter.Load();
   33         }
   34     }
   35 
   36    
public Model.Names Names {
   37         get {
return _names; }
   38         set {
_names = value; }
   39     }
   40 
   41    
public void
Update() {
   42         dgdNames.DataSource
= _names;
   43        
dgdNames.DataBind();
   44 
   45         lblCount.Text =
_names.Count.ToString();
   46    
}
   47 
   48     private
void btnAdd_Click(object sender, System.EventArgs e) {
   49        
_presenter.AddName(txtName.Text);
   50    
    txtName.Text = string.Empty;
   51     }
   52 
   53    
private void
dgdNames_DeleteCommand(object source,
DataGridCommandEventArgs e) {
   54        
_presenter.RemoveName(e.Item.ItemIndex);
   55     }

I have omitted the using statements and the IDE-generated code, so this is
not complete, but it shows how I am delegating behavior to the presenter layer. 
Here is my presenter:

    5 public class NamesPresenter
    6 {
    7    
private INamesView _view;
    8 
    9    
public NamesPresenter(INamesView view)
{
   10         _view = view;
   11     }
   12 
   13    
public void
AddName(string nameToAdd) {
   14         _view.Names.Add(nameToAdd);
   15         _view.Update();
   16     }
   17 
   18    
public void
RemoveName(int indexOfNameToRemove) {
   19        
_view.Names.RemoveAt(indexOfNameToRemove);
   20         _view.Update();
   21     }
   22 
   23    
public void
Load() {
   24         _view.Names = new Model.Names();
   25         _view.Update();
   26     }
   27 }

This presenter controls the model (my business object) and tells the UI what
to do and when through the interface that the UI must implement (below):

    5 public interface INamesView
    6 {
    7    
Model.Names Names{get;set;}
    8    
void Update();
    9 }

As long as my UI implements this interface, my presenter can control it, and
I can make changes to the UI (including total replacement) without impacting the
core behavior. 

Next, all this exists to manage my model or business object:

    6 [Serializable]
    7 public class Names : IEnumerable
    8 {
    9    
private ArrayList _entries = new ArrayList();
   10 
   11    
public string
this[int
index] {
   12         get { return
_entries[index].ToString(); }
   13        
set { _entries[index] = value; }
   14     }
   15 
   16    
public int
Count {
   17         get { return
_entries.Count; }
   18     }
   19 
   20    
public void
Add(string name) {
   21         _entries.Add(name);
   22     }
   23 
   24    
public void
RemoveAt(int index) {
   25         _entries.RemoveAt(index);
   26     }
   27 
   28    
public IEnumerator GetEnumerator() {
   29         return
_entries.GetEnumerator();
   30    
}
   31 }

Events from the UI are delegated to the presenter, which decides what to do
and calls interface methods on the UI.  The presenter controls interaction
between the UI and the model just as a repository layer would control
presistence between the model and a data store.

I don’t pretend that this example is perfect, and I’m open for criticism, but
the model view presenter pattern for the UI is very tempting for testability and
UI portability.

Implementing a data repository class with a _true_ business object all with TDD – level 300

I’ve revised my previous sample that contained a Category class.  This
Category has an id, a name and description.  There is a Category table to
persist the object. I pulled everything data out of my business object and
created a CategoryData object that will know the DAL and know how to persist and
hydrate the Category object.  This approach greatly simplifies the business
object and decouples persisting the object to the database.  In the future, if I
changed the persistence method, I don’t have to change the business object, just
the data object.  Here are my sample classes:

Category:  note how simple my business object is now:

    7 public class Category {
    8 
    9    
private int
_id = 0;
   10     private string _name
= string.Empty;
   11     private
string _description = string.Empty;
   12 
   13    
public int ID
{
   14         get { return this._id; }
   15         set {
this._id = value; }
   16     }
   17 
   18    
public string
Name {
   19         get { return this._name; }
   20         set {
this._name = value; }
   21     }
   22 
   23    
public string
Description {
   24         get { return this._description; }
   25         set {
this._description = value; }
   26     }
   27 }

And my unit test class for this:

   12 [TestFixture]
   13 public class CategoryTest
   14 {
   15    
private Category _category = null;
   16 
   17    
public CategoryTest() {
   18         Trace.Listeners.Add(new TextWriterTraceListener(Console.Out));
   19     }
   20 
   21    
[SetUp]
   22     public void
GetACategory(){
   23         this._category = new
Category();
   24     }
   25 
   26    
[Test]
   27     public void
CreateCategory() {
   28        
Assert.IsNotNull(this._category);
   29     }
   30 
   31    
[Test]
   32     public void
CategoryID() {
   33         this._category.ID = 2;
   34         Assert.AreEqual(2, this._category.ID);
   35     }
   36 
   37    
[Test]
   38     public void
CategoryName() {
   39         this._category.Name = “Beverages”;
   40         Assert.AreEqual(“Beverages”,
_category.Name);
   41     }
   42 
   43    
[Test]
   44     public void
CategoryDescription() {
   45         this._category.Description =
“MyDescription”;
   46        
Assert.AreEqual(“MyDescription”, _category.Description);
   47     }
   48 }

Then I have my CategoryData object that knows how to persist using the
DAAB:

    7 public class CategoryData
    8 {
    9    
private Database _database = null;
   10 
   11    
public Database Database {
   12         get {

   13             if(this._database ==
null)
   14                 this._database =
DatabaseFactory.CreateDatabase(“DEV”);
   15             return this._database; }
   16         set {
this._database = value; }
   17     }
   18 
   19    
public Category GetCategory(int categoryID) {
   20         Category cat = null;
   21    
    using(IDataReader reader = this._database.ExecuteReader(“GetCategoryByID”,
categoryID)) {           
   22 
   23             if(reader.Read()){
   24                 cat = new Category();
   25                 cat.ID =
reader.GetInt32(reader.GetOrdinal(“CategoryID”));
   26                 cat.Name =
reader.GetString(reader.GetOrdinal(“CategoryName”));
   27                 cat.Description =
reader.GetString(reader.GetOrdinal(“Description”));
   28                 return cat;
   29             }else {
   30    
            return null;   
   31             }
   32         }
   33     }
   34 
   35    
public void
Insert(Category newCategory) {
   36        
newCategory.ID = (int)this._database.ExecuteScalar(“InsertCategory”,
newCategory.Name, newCategory.Description);
   37     }
   38 }

And the unit tests with stubs for the CategoryData class:

   12 [TestFixture]
   13 public class CategoryDataTest
   14 {
   15    
private CategoryData _categoryData = null;
   16 
   17    
[SetUp]
   18     public void
GetACategory(){
   19         this._categoryData = new CategoryData();
   20         this._categoryData.Database = new DatabaseStub();
   21     }
   22 
   23    
[Test]
   24     public void
GetCategory() {
   25         Category cat =
this._categoryData.GetCategory(1);
   26         Assert.IsNotNull(cat);
   27         Assert.AreEqual(1, cat.ID);
   28         Assert.AreEqual(“Beverages”,
cat.Name);
   29        
Assert.AreEqual(“MyDescription”, cat.Description);
   30     }
   31 
   32    
[Test]
   33     public void
GetCategoryThatDoesntExist() {
   34        
Category cat = this._categoryData.GetCategory(500);
   35         Assert.IsNull(cat);
   36     }
   37 
   38    
[Test]
   39     public void
InsertCategory() {
   40         Category
cat = new Category();
   41         cat.Name = “Jeffrey”;
   42         cat.Description = “Hello
category”;
   43         this._categoryData.Insert(cat);
   44         Assert.IsTrue(cat.ID == 400);
   45     }
   46 
   47    
internal class
DatabaseStub : SqlDatabase {
   48        
public override object
ExecuteScalar(string storedProcedureName, params object[]
parameterValues) {
   49             switch(storedProcedureName) {
   50                 case “InsertCategory”:
   51                     return 400;
   52                     break;
   53    
        }
   54 
   55             return null;
   56    
    }
   57 
   58 
   59    
    public override IDataReader ExecuteReader(string storedProcedureName, params object[]
parameterValues) {
   60 
   61             switch(storedProcedureName) {
   62                 case “GetCategoryByID”:
   63                     if(parameterValues.Length == 1 && (int)parameterValues[0] == 1) {
   64                         return new
DataReaderForCategory1Stub();
   65        
            }
   66                    
break;
   67             }
   68 
   69    
        return new EmptyDataReaderStub();
   70         }
   71     }
   72 
   73    
internal class
DataReaderForCategory1Stub : IDataReader  {
   74 
   75    
    public bool Read() {
   76             Trace.WriteLine(“Returning true
from Read()”, this.GetType().Name);
   77             return true;
   78    
    }
   79 
   80         public
string GetString(int index) {
   81             string retValue = string.Empty;
   82             switch(index) {
   83                 case 1:
   84    
                retValue = “Beverages”;
   85                     break;
   86    
            case 56:
   87                     retValue =
“MyDescription”;
   88                    
break;
   89             }
   90 
   91    
        return retValue;
   92         }
   93 
   94    
    public int
GetInt32(int index) {
   95             int retValue = int.MinValue;
   96             switch(index) {
   97                 case 0:
   98    
                retValue = 1;
   99        
            break;
  100             }
  101 
  102    
        return retValue;
  103         }
  104 
  105    
    public int
GetOrdinal(string name) {
  106             int retValue = 0;
  107             switch(name) {
  108                 case “CategoryName”:
  109                     retValue = 1;
  110                     break;
  111    
            case “CategoryID”:
  112                     retValue = 0;
  113                     break;
  114    
            case “Description”:
  115                     retValue = 56;
  116                     break;
  117    
        }
  118 
  119             return retValue;
  120         }
  121 
  122        
#region
IDataReader Members
  123 
  124         // extra members omitted
  279        
#endregion

  280     }
  281 
  282    
internal class
EmptyDataReaderStub : IDataReader {
  283        
#region
IDataReader Members
  284 
  285         // implementation omitted 
  468        
#endregion

  469 
  470     }
  471 }

This makes my model so much easier because now my business rules don’t have
to have anything to do with the database, and if we change databases later, my
business object doesn’t have to change.  I’ve been talking with Scott Bellware a lot lately
about Domain-driven design, and it’s some pretty deep stuff.  One thing we agree
on:  RAD sucks!

My first Test-Driven Development presentation – and concerns raised – level 100

Sorry folks, no code in this post. 

I just finished giving a one-hour presentation on how to do TDD with VS 2003
and NUnit.  I gave a brief overview of TDD but didn’t try to persuade them to
change right off the bat.  Heck, I’m just getting started!  I started with the
approach of referring them to the explanation given on AgileData.  Then I went into
a demonstration.  I emphasized the need to see the test fail to ensure that your
test is correct.  One time, I forgot to add an Assert to the test, and, of
course, it would never fail.  That hit home the point that seeing it fail
verifies that the test is good.

One of the concerns raised about using this methodology is:  “That’s a lot
more code to write”.  We talked as a group about the benefits of having unit
tests for all the code in a project.  The project could be shipped to another
building totally cut off from all networks, and another team could add
functionality and be sure they didn’t break anything else as long as all the
existing unit tests all continue to work.

Another question was if this approach would lead to better code. . .or “what
about the novice programmer”.  We talked about how this can lead to a clean
design but that it would not artificially make a bad programmer’s code better. 

I only started doing TDD last week, so if almost feels like I’m leading the
group while stumbling through the dark myself.  It’s becoming familiar quickly,
though.  I’m learning how to decouple external dependencies and test in
isolation, using SetUp and TearDown methods, etc.  Performance counters were a
bit tricky, though because I couldn’t decide if they should be considered an
external dependency that should be stubbed or not.  After all, I’m just calling
classes in the .Net Framework.  The code isn’t calling over the network.  With a
database, it’s clear:  stub it! 

Another topic that came up concerned some of our existing codebase.  We have
some class libraries that were designed to be consumed in a flexible manner, but
not test-first, and we don’t have unit tests for them.  Each of the classes has
about 5 overloaded constructors.  We discussed that if we developed it with TDD,
we’d only have the constructors that were required or that we tested.  We may
still decide that we want 5 constructors, but each one would have to have a
consuming test written first, and I would think all other tests on that class
would need to be performed with an object constructed with each of the
constructors.

The more I learn about TDD, the more I find I need to learn.  What a vicious
cycle!  I’m loving it, though.

TDD with the new Enterprise Library DAAB redux – level 300

Yesterday’s attempt at TDD with the DAAB prompted me to use NMock to imitate
the external dependency so my test would be a true _unit_ test.  Since then,
I’ve given it more thought – about the best way to proceed for future tests. 
I’ve revised my test and included two concrete stub classes:  one that derives
from the DAAB SqlDatabase class, and one that implements IDataReader.  These
classes have turned out to be easier to work with, and I like being able to see
the code directly in my test.  I would run against  a roadblock if I needed to
stub a class that didn’t implement an interface _and_ was sealed (I’m hoping
none of the .Net Framework is like this).

My Category class is the same:

    7 public class Category {
    8 
    9    
private string
_name = string.Empty;
   10     private
static Database _database = null;
   11    
private static
string _databaseInstance = “DEV”;
   12 
   13    
public string
Name {
   14         get { return this._name; }
   15         set {
this._name = value; }
   16     }
   17 
   18    
public static
Database Database {
   19         get {
   20    
        if(_database == null)
   21    
            _database =
DatabaseFactory.CreateDatabase(_databaseInstance);
   22 
   23    
        return _database;
   24         }
   25         set {
_database = value; }
   26     }
   27 
   28    
public Category(int categoryID)
   29     {
   30         IDataReader reader =
Category.Database.ExecuteReader(“GetCategoryByID”, categoryID);
   31 
   32    
    if(reader.Read()){
   33             this._name =
reader.GetString(reader.GetOrdinal(“CategoryName”));
   34         }
   35     }
   36 }

But my unit test class has changed.  The [Test] method has been simplified
with the use of concrete stubs instead of dynamic Mocks.  My stubs are nested
classes so that they are truly dedicated to this test class:

   12 [TestFixture]
   13 public class CategoryTest
   14 {
   15    
public CategoryTest() {
   16         Trace.Listeners.Add(new TextWriterTraceListener(Console.Out));
   17     }
   18 
   19    
[Test]
   20     public void
GetACategory(){
   21        
Category.Database = new
DatabaseToReturnCategory1();
   22 
   23         Category existing = new Category(1);
   24         Assert.AreEqual(“Beverages”,
existing.Name);
   25     }
   26 
   27    
public class
DatabaseToReturnCategory1 : SqlDatabase {
   28         public
override IDataReader ExecuteReader(string storedProcedureName, params object[]
parameterValues) {
   29             return new
DataReaderToReturnBeveragesAsCategoryName();
   30         }
   31     }
   32 
   33    
public class
DataReaderToReturnBeveragesAsCategoryName : IDataReader  {
   34         public
bool Read() {
   35             Trace.WriteLine(“Returning true
from Read()”, this.GetType().Name);
   36             return true;
   37    
    }
   38 
   39         public
string GetString(int index) {
   40             string retValue = string.Empty;
   41             switch(index) {
   42                 case 1:
   43    
           
    Trace.WriteLine(“Returning Beverages from
GetString(int)”, this.GetType().Name);
   44                     retValue =
“Beverages”;
   45                     break;
   46    
        }
   47 
   48             return retValue;
   49         }
   50 
   51    
    public int
GetOrdinal(string name) {
   52             int retValue = 0;
   53             switch(name) {
   54                 case “CategoryName”:
   55                     Trace.WriteLine(“Returning
1 from GetOrdinal(string)”, this.GetType().Name);
   56                     retValue = 1;
   57                     break;
   58    
        }
   59 
   60             return retValue;
   61         }
   62 
   63        
#region
IDataReader Members
   64 
— unneeded stub methods not
posted
  224 
  225        
#endregion

  226     }
  227 }

I have started a switch statement in the stubs so that I can easily add some
more cases to test the other fields that I expect back.

Note also my use of a trace listener.  I’ve grown to prefer Trace.Writeline
instead of Console.Writeline because with Trace I can hook up more listeners and
also get this output to other callers.  Using Console.Out with the listeners
also allows me to get these statements to the console (and the NUnit gui).

This approach is more straight-forward to me than dynamic mocks, so I’m going
to stick with this approach as much as I can.

This afternoon I’ll be giving a presentation to my team about TDD and doing
this demo to help get them started.  I hoping my team can quickly start using
iterative development to drive quality and flexibility in our software.

TDD with the new Enterprise Library DAAB – level 300

First, I give props to they guys at Microsoft who put out the Enterprise
Library.  I especially like the data access application block.  I do, however,
have one beef with it.  Everyone preaches “code to an interface”, and ADO.NET
exhibits this (IDataReader, IDbCommand, etc), but the DAAB doesn’t do this. 
Consequently, I can’t stub out a mock object to decouple the DAAB from the code
I’m trying to unit test with NUnit.  I then tried NMock to create a dynamic
mock, but the ExecuteReader(string, params object) method wasn’t marked as
virtual.  After changing it to virtual and recompiling the source, NMock would
work (it appears most methods were marked virtual, but that some were
overlooked).  Not having the interface to stub out _really_ makes TDD
difficult.  It forces me to use dynamic mocks (which are more brittle).

Here’s a quick test I whipped up to create a Category class that is a record
in the Categories table of Northwind.  Note the use of NMock to get my unit test
to work without the dependency on the DAAB:

    7 public class Category {
    8 
    9    
private string
_name = string.Empty;
   10     private
static Database _database = null;
   11    
private static
string _databaseInstance = “DEV”;
   12 
   13    
public string
Name {
   14         get { return this._name; }
   15         set {
this._name = value; }
   16     }
   17 
   18    
public static
Database Database {
   19         get {
   20    
        if(_database == null)
   21    
            _database =
DatabaseFactory.CreateDatabase(_databaseInstance);
   22 
   23    
        return _database;
   24         }
   25         set {
_database = value; }
   26     }
   27 
   28    
public Category(int categoryID)
   29     {
   30         IDataReader reader =
Category.Database.ExecuteReader(“GetCategoryByID”, categoryID);
   31 
   32    
    if(reader.Read()){
   33             this._name =
reader.GetString(reader.GetOrdinal(“CategoryName”));
   34         }
   35     }
   36 }

Now my test:

   10 [TestFixture]
   11 public class CategoryTest
   12 {
   13    
[Test]
   14     public void
GetACategory(){
   15 
   16         Mock mockDatabase = new NMock.DynamicMock(typeof(Database));
   17         Mock mockReader = new NMock.DynamicMock(typeof(IDataReader));
   18 
   19    
    mockReader.ExpectAndReturn(“GetString”, “Beverages”, 56);
   20        
mockReader.ExpectAndReturn(“GetOrdinal”, 56, “CategoryName”);
   21         mockReader.ExpectAndReturn(“Read”,
true);
   22        
mockDatabase.ExpectAndReturn(“ExecuteReader”, mockReader.MockInstance,
“GetCategoryByID”, new object[]{1});
   23 
   24    
    Category.Database = (Database)mockDatabase.MockInstance;
   25 
   26    
    Category existing = new
Category(1);
   27        
Assert.AreEqual(“Beverages”, existing.Name);
   28 
   29    
    mockReader.Verify();
   30        
mockDatabase.Verify();
   31     }
   32 }

I had to use NMock for the Database class, so I just used it for the
IDataReader interface as well (as I further this class, I will modify that to
use a mock stub that implements IDataReader).

This code work and allows me to test the instantiation of my Category object
with the ID of a category.

The data access block is awesome, but I WISH I had an interface to code
against instead of just the abstract class.  I guess I’m going to have to insert
the interface myself.

By the way, I attended the webcast about this block this afternoon, and I
have to say that it was the most fun webcast I’ve every attended.  The
presenters were in shorts and cutting up and laughing the whole time.  🙂

Thanks to those in the community from whom I’ve learned – level 000

I have to take some time and formally thank those in the .Net community who
have helped me achieve MCSD either by direct learning or by their webcasts,
articles, or blog posts.

  • Noah Coad (MS) – The release manager for VSTS
    Dev & Test tools.  Noah and I went to college together at Texas A&M University and have been good
    friend since.  He has done some amazing stuff in his life with software, and
    I’ve learned a great deal from him.  He is excited about software, and it’s
    contagious.
  • Mike Hnatt – Mike is a good
    friend, and an old professor of mine from Texas A&M.  He makes a living by
    selling custom software to manage fairs and consulting.  He’s an inspiration as
    well.
  • Mark Dunn, Carl Franklin, Rory Blyth (.Net Rocks) – My education
    in .Net really began in January of 2003.  I had poked around with it when it
    first came out, but in January, I really got serious with it.  The trouble came
    when in Feb 2003, I was deployed with my Army Reserve unit to Iraq.  I purchased
    a Dell laptop over there, and I began reading blogs, articles, and anything .Net
    I could get my hands on.  This included .Net Rocks, which debuted while I was
    over there.  I synchronized as much content as possible while connected to the
    Net, and I digested it all offline.  I have learned a ton from .Net Rocks.  I
    even had an opportunity to call in to the show from Kuwait.  Thanks guys.
  • Steve Hickman – Hired me for my first programming job.  I programmed web
    apps for him using IDC/HTX.  This is where it all got started.
  • Scott Mitchell – So many
    articles.  I’ve learned a great deal from them.  I especially enjoyed the series
    on Data
    Structures
    that came out while I was overseas.
  • Scott Bellware (C# MVP) –
    Scott is a part of the local Austin, TX .Net User
    Group
    and is big into TDD.  I’m just getting started in TDD, and he’s been a
    great help here.
  • Rockford Lhotka – Rocky doesn’t
    know me too well, but his articles on SOA and OO are awesome.  He spoke at our
    user group once, and it blew my mind.  If you ever get to hear Rocky speak, take
    advantage of it.  Also, it’s not very often that I run across another developer
    bigger than me (I’m 6’3″, 275lbs) (Rocky is a rock!)
  • Jeffrey
    Richter
    – The first .Net book I ever read was Mr. Richter’s Applied
    Microsoft .Net Framework Programming book.  It was like drinking from a fire
    hose.  I’m better off for it, though.  The detail about garbage collection has
    enabled me to debug memory problems in my code.  I had the pleasure of meeting
    Mr. Richter and the whole Wintellect crew when they came to Austin
    last month.
  • Paul Wilson – Specifically,
    his master pages controls taught me a lot about the ASP.NET control hierarchies
    and the page life cycle.  I dove in and extended his controls and have published
    my own set here.
  • Brad Abrams (MS) – I’m sure we’ve
    all learned from him in some form, but I thoroughly enjoyed his MSDN TV and .Net
    Show appearances.  Brad recently visited our User Group, and I had the opportunity to converse
    with him over BBQ!

Thank you all for sharing your knowledge.

I became an MCSD.Net today! And 70-300 turned out to be not so bad – level 000

I passed 70-300 today.  I had left that test till last because I had gotten the
impression that it was the hardest one.  It turned out not to be.  70-229 (SQL
Server) was the hardest for me.  Indexes, etc!  I’m an MCSD.Net now.  I went the
track of doing MCAD.Net and now MCSD.  I’m excited.  Today I join the 12,829
people world-wide to hold this certification.  I guess now the number is 12,830.

XP SP2 makes it a snap to disable the Shockwave add-in – level 000

If you are like me, you are annoyed when a flash advertisement on a website steals the entire screen and won’t give it back until I click a small “close” button, but I have to make sure my mouse is on the exact right spot or I’ll be forwarded to a site asking me to buy something.

I have found no value in flash as it is currently used on the Net.  I know it can create some great UI stuff, but all it does for me is annoy me.  With SP2 for XP, a button is added in IE to Internet Options called “Manage Add-ins” under the Programs tab.  Here I was able to easily disable the shockwave add-in, and now I can’t be happier.  Those annoying adds don’t show up anymore, and if I need to use a site that depends on Flash, it’s just a few clicks away.  In fact, if a site tries to load Flash while it’s disabled, a quick click in the IE status bar will enable it for me.