Wednesday, February 02, 2005

Well, despite having my Domain Controller's hard drive go south last night, I'm back up and running!

I scurried down to the local computer store this afternoon and picked me up a brand new 160 GB drive (oh, and I also grabbed a 250 GB, 16MB Cache drive along with it).  This time I also got a 2 year warranty with them - I'm sick and tired of dealing with downed drives.

Well, prior to today, I had not had the experience of restoring an Active Directory and an Exchange Server...so this was uncharted territory for me.  I had made a backup of my System State last night and the Exchange data, so I was all set there; it was just a matter of restoring it properly.

These are the steps that I underwent:

  1. Put new drive in computer
  2. Format/Partition it as NTFS
  3. Installed the base OS (Windows Server 2003)
  4. Configured the NIC and IP address
  5. Installed IIS, DNS, and DHCP (though I did not configure them)
  6. Rebooted into Directory Recovery mode
  7. Restored my System State data, thereby bringing back the domain
  8. Rebooted
  9. Installed AntiVirus software
  10. Installed Exchange 2003 and SP1
  11. Restored the Exchange backup
  12. Repaired the database (ESEUTIL /P ...)

It's all up and working now and I'm so excited.  What pleased me most was that from taking out the old drive and being completely back up and running it took less than 90 minutes actual time (a few hrs real time including dinner and other sundry items).  Now the whole domain is back and it feels good!

Wednesday, February 02, 2005 3:10:00 PM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |  Trackback

I must be a magnet.  A magnet for hard drive failures.  Which makes sense when you think about it.

Last night, during a not-so-routine backup of data on my domain controller (which happens to dual as my DNS, DHCP, Exchange, some file storage, etc), it encountered several Cyclical Redundancy Check (CRC) errors.  Dismayed, but not to the point of depression, I trudged forward and backed up the System State and the Exchange data and verified them.  I then proceeded to run CHKDSK /R which necessitated a reboot.  Durinng the CHKDSK it displayed each of the segments that were unreadable (starting at about 36,000 it went well into the 100,000s with the occasional skipping of a just few segments).  My rudimentary math tells me that it must have been about 50% of the drive that was bad.

So this turns out to be my 4th drive in about 2 years to go out (though officially this one hasn't crashed per se (yet) like the others).  Not only is it my 4th drive to go out, it's always been the boot volume on the domain controller that has gone out.  On reboot it warns me of imminent failure of the disk...nice, <sarcasm>at least I'm not sweating here</sarcasm>.

Maybe I'm a magnet for bad equipment? for bad luck?  Anyway, I'm on a budget and can't afford anything 'state of the art', nor do I expect 5 9's here at my house where it's only me and a few other people, but I'm heading down to my local Comp USA, Best Buy, or Office Max to get a new drive (as I need it TODAY).  Recommendations before I make the march in a bit here?  Anyone have recommendations for the 'best' drive?  My domain controller isn't even a 'server-class' box, but it's a pretty capable workstation, no SCSI or anything like that.

Wednesday, February 02, 2005 3:45:00 AM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Monday, January 31, 2005

Last night (on January 31, 2005) we held our very first official code review for the game the Devstone programming group is writing entitled Island Frenzy.  As the initiator and primary proponent behind the code review, I was the voluntary guinea pig - we decided to review my code behind my messaging sub-system.  Though not everyone came prepared, which was quite a disappointment on a variety of levels, we were able to review the crux of the material and I feel that everyone left edified.

The purpose of the code review was multi-faceted.

  • To familiarize everyone with the various aspects of the application libraries.  Often times as developers we're islands in that we each work on our own focused areas, while blindly ignoring of the rest of the system.
  • To discuss the strengths and weaknesses of the objects within the system.  It's nice to have more than one set of eyes on the code.
  • To identify ways in which the objects should be extended/reused.
  • To identify the validity of the objects.  Some objects may no longer be used or perhaps are not even necessary anymore.  It's time to clean up the code.
  • To determine (and correct where applicable) whether the objects follow all established standards and conventions.
  • To learn new techniques and strategies.  Let's face it, we can all improve in this arena.

Conducting the code review was a great experience, despite not having full participation.  I was forced to recall historical reasonings behind various motives in the code - stuff that has been identified as candidates for better documentation and commenting.  It was neat to relive the decisions that I made months (sometimes years) ago in the design of the framework and come to the same conclusions in almost every single case.

Thanks to all that participated!

Monday, January 31, 2005 7:57:00 PM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Sunday, January 30, 2005

All my life I've been something of a boardgame nut...probably always will be.  Recently, I discovered a new boardgame that I very much enjoy and like to show to people.  It's called Carcassonne.  This game is a tile-based game in which players take turns strategically placing tiles on the table, ultimately creating a large map of cities, roads, and farmlands.  The game is fantastically addictive.  The game, not including its various expansions contains 72 tiles.  I had some spare cycles today and wondered if I could place all 72 tiles together to form a square board, and have all tiles legally placed.

As it turns out, this was easier that I thought it might be, only taking about 10-15 minutes.  One primary goal was to set the board up such that it was completely contained; that is, set up such that no cities and roads led off of the board.  Well, the result wasn't quite what I was aiming for, but I'll probably give it another try in the coming days (hrs).  Please forgive the blurriness of some of the tiles - I took the photograph at an angle so as to minimize the flash's glare and then photoshopped it with a perspective distortion and a stretch.

Update January 31st, 2005:
Here's an updated one that took a little more time.  As you can see, all of the roads and cities are completely self-contained on the board with the exception of the lower left corner.  Oh, well, it was fun while it lasted.

Sunday, January 30, 2005 5:32:00 PM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Friday, January 28, 2005

They say practice makes perfect, and this is no exception to the rule.  You just never know what you'll find the Microsoft .NET SDK Documentation.  This is a best practice of which I've definitely grown fondle:

 

Does anyone have any best practice recommendations for 'handling' exceptions?

Friday, January 28, 2005 12:37:00 PM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |  Trackback

Well, I finally had a free moment and I just got back in town the other night so I thought a take a moment and do some blogging.  It's been a while since I posted regarding my custom blog rating control; I've been meaning to get this up for download for some time but could never manage to - until now, not to mention that I've gotten quite a few requests to get it :).

Bear in mind, this is NOT the definitive, final release.  It is, however, enough to get started and have as a functional rating system.  Below, I've provided a bit of documentation to help you get it up and running on your site.  Please feel free to contact me if you have any questions.

The first thing you'll need to do is to run a database script against your blog database.  This script, found in the file 'DbScript.sql' does not do anything to alter existing schema.  All it does is adds a table (blog_DevstoneRating), a few store procedures (blog_DevstoneRateItem, blog_DevstoneGetItemRating, and blog_DevstoneGetTopRatedItems), and a function (blog_fnDevstoneGetPostUrl).  Be sure to change the database name to the appropriate name before running the script.  Oh, and backup your database.

The main rating control is called RatingSummary.  There is also another control that I haven't yet packaged because it's not quite finished that is called TopRatedPosts.  It is functional, but not yet friendly to all skins - I'll get it up as soon as I can.  Additionally, there is an HttpHandler and an HttpHandlerFactory class.  These provide integration into the .Text blog website.

The RatingSummary supports being embedded on a variety of pages within your blog (so that users can rate a blog post, a comment, etc).  One of the chief design tenets was that I wanted to be able to place the control on the main page along side each post, thereby providing the user a quick way to enter comments and rate the post.  Note: If a user rates a post and doesn't enter any comments it simply updates the post's rating.  However, if the user provided comments, not only is the post's rating is updated, but the user's comment is added to the list of posts for the site so it appears as a regular comment on the site.

The following examples identify a few of the changes you can make to incorporate the rating control on your site.  You will need to add the following page directive to the top of each control in which you intend to use the control:

<%@ Register tagPrefix="devstone" namespace="Devstone.Web.Blog.Controls" assembly="Devstone.Web.Blog.Controls.Rating" %>

The simple RatingSummary declaration resembles the following:

<devstone:ratingsummary runat=server visible=true />

The RatingSummary control offers a variety of properties that you can set to ensure proper behavior whereever posted.

ShowSummary (bool property):  Defaults to true.  Determines the presence of a descriptive text beneath the graphical rating, explaining the rating.

RatingBarType (RatingBarType property):  Defaults to RatingBarType.Star.  Determines the appearance of the rating control.  Valid values include Star, Dot, and Bar.

ShowRatingLink (bool property):  Defaults to true.  Determines if the link allowing users to rate the post is to be shown.

PostId (int property):  Defaults to -2 (autodetermine).  If you explicitly place the post id on the control, the user will be guided to rate a particular post.  If left as autodetermine (-2), the RatingSummary control attempt to read the post id from the url.  If it cannot, it returns -1 (invalid).

PostLink (string property):  The url to the post being rated.

PostType (PostType property):  Defaults to PostType.BlogPost.  The value of this property determines the text displayed to the user describing what they are rating.

Alignment (HorizontalAlign property):  Defaults to HorizontalAlign.Left.  Determines the alignment of the control.

Rated (bool readonly property):  Returns whether the particular post has been rated by the user.

Now, in order to get the control to work properly, you must make a few changes to the web.config file.  Within the <HandlerConfiguration/HttpHandlers /> node, add the following patterns (NOTE: see the previous post for further details on how to set these up properly if you have a multiple-blog site vs a single-blog site.  These tags are for a multiple-blog site):

   <HttpHandler pattern="^(?:/(\w|\s|\.)+/img/\d\.\d{2}/(?:Star|Dot|Bar)\.dspx)$" type="Devstone.Web.Blog.Controls.RatingHandlerFactory, Devstone.Web.Blog.Controls.Rating" handlerType="Factory" />
   <HttpHandler pattern="^(?:/(\w|\s|\.)+/rate/(?:Star|Dot|Bar)/[12345]/\d+\.dspx)$" type="Devstone.Web.Blog.Controls.RatingHandlerFactory, Devstone.Web.Blog.Controls.Rating" handlerType="Factory" />

Then add the following to the <appSettings /> section, substituting the proper values:

<!-- Devstone Settings -->
<add key="Devstone.DbConnectionString" value="..." />
<add key="Devstone.SendEmail" value="true" />
<add key="Devstone.SmtpServer" value="..." />
<add key="Devstone.Email" value="...
" />
<add key="Devstone.EmailSender" value="...
" />
<add key="Devstone.EmailSubjectHeader" value="Blog Rating" />

The following are examples as they might appear to enable a user to rate various post types:

Comments.ascx:

<devstone:ratingsummary runat=server alignment=right postType="Comment" postId="<%#((Dottext.Framework.Components.Entry)Container.DataItem).EntryID %>" visible=true />

Day.ascx:

<devstone:ratingsummary runat=server alignment=right postId="<%# ((Entry)Container.DataItem).EntryID %>" postLink="<%# ((Entry)Container.DataItem).Link %>" visible=true />

Download: RatingControl.zip

For more information, please see the previous posts:

Part I:  From Vague Concept to Less-Vague Concept
Part II:  Integrated with .Text

Have fun!

Friday, January 28, 2005 5:13:00 AM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [1]  |  Trackback
 Wednesday, January 26, 2005
As others have noted, Megan Davis offers a great guide to running a utility called sysprep on virtual machines.  If you use Virtual Machines like I do (which is for just about everything), you'll not want to miss this!
Wednesday, January 26, 2005 3:31:00 AM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Sunday, January 23, 2005

I've often marvalled at this, but never really considered it or given it much thought.  As you may be aware, if you open up Sql Server's Enterprise Manager, connect to a remote database server and select 'New Database' it will allow you to designate the target location for the database (.mdf) and log (.ldf) files.  It displays a dialog box containing the drives and folders on the remote computer as though you were browsing the computer locally.

It does this even if you have no user access rights on the remote computer - Windows authentication is not required.  How does it do it?  I used to think it used some internal Sql Server magic.  Later I wondered if it might use WMI, but WMI would require you to authenticate to the remote machine to browse its directory structure.  You don't get a Windows token by logging in to a Sql Server as 'sa' ;)  Again, I didn't really consider it or give it much thought.

Well, as it turns out Sql Server has several extended stored procedures nestled neatly in the master database that the dialog uses to return a directory tree:  xp_availablemedia, xp_dirtree, et al.  The dialog is custom tailored to interpret the responses from these stored procedures and render a nice tree-view to the user.  Simply open a copy of Sql Profiler and monitor the SQL:BatchCompleted and you'll see all the calls to xp_dirtree.  This could be pretty helpful if you ever create a custom utility to creates databases on a remote computer without requiring the use of the Enterprise Manager.

I went through the trouble a while back to create one that utilized WMI to perform the same operation, but this would have been a heck of a lot easier and straightforward...I think I'm going to go back and retool a new one to use these instead.

Sunday, January 23, 2005 6:00:00 PM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Friday, January 21, 2005

On the last post, I had introduced the concept of creating an IDE in which types and their instances are generated dynamically.  For a full discussion on the rationale behind the application is in the previous post, along with how properties were designed and organized.  This follow-up discussion, however, is focused on taking the semi-abstract notion of what a property is and converting that into a real type at runtime and its associated instances.

Each of the dynamically created types within the system is similar to other, related types.  For this reason, it was a good idea to define a base class.  This enables us to create a collection or array that is strongly typed to contain instances of each of the disparate types.  I decided to create this type manually in the assembly, vs. creating it dynamically so that I could utilize the strong typing within my code.  I called this base type PropertySetBase.  For consistency purposes (as well as some other more motivating reasons that I'll discuss in another article), it was decided that this base class contain no properties except for a 'Name' property.  Now, I had a special case here in which the names were predefined and not assigned within the source .ascx file.  Were that not the case, I would not even consider having the Name property there...the base class would be nothing but a blank, abstract class.  My class resembles:

public abstract class PropertySetBase {
   private string _name;

   public PropertySetBase(string name) {
      _name = name;
   }

   [ParenthesizePropertyName(true)]
   [Description(“The control identifier.  This property is readonly.“)]
   public string Name {
      get { return _name; }
   }
}

At this point, it now became necessary to take the IToolPropertyDefn instances and convert them into actual, runtime classes.  One of the most powerful, yet perhaps under-utilized aspects of the .NET framework is the ability the runtime has to generate types dynamically via the classes contained within the System.Reflection.Emit namespaces.  Probably because doing so requires knowledge of Intermediate Language (IL).

In order to generate the classes at runtime, I created a builder class.  Recall that all of the properties for a given dynamic type are defined within the ToolPropertyDefns class as flyweight objects.  Therefore, my builder could, given a tool id, resolve to the set of properties pertaining to the type and (if it's not yet created) generate the type, create an instance, and return it.  Once the type is defined, the definition is cached in a Hashtable for fast access later on, when another instance of the same type is requested.  A snippet:

internal sealed class PropertySetBuilder {
   private static Hashtable _types = new Hashtable();

   internal static PropertySetBase Build(Tools toolId) {
     
// establish the property definitions array
      IToolPropertyDefn[] props = ToolPropertyDefns.GetDefn(toolId);

     
// check to see if the type has been defined previously, if not create it
      Type type = _types[toolId] as Type;
      if ( null == type ) {
         // ...logic here to create the type
      }
   }
}

Now, in order to create definitions for these types dynamically we use various classes within the System.Reflection.Emit namespace such as AssemblyBuilder, ModuleBuilder, TypeBuilder, et al.  This functionality I decided to isolate into some private helper methods, additionally, I had to define the instances privately in the PropertySetBuilder class.

private AssemblyBuilder _asmBuilder;
private ModuleBuilder _modBuilder;

private static AssemblyBuilder getAsmBuilder() {
   if ( null == _asmBuilder ) {
      AppDomain domain = Thread.GetDomain();
      AssemblyName asmName = new AssemblyName();
      asmName.Name = “DynTypes“;
      _asmBuilder = domain.DefineDynamicAssembly(asmName, AssemblyBuilderAccess.Run);
   }
   return _asmBuilder;
}

private static ModuleBuilder getModBuilder() {
   if ( null == _modBuilder ) {
      AssemblyBuilder asmBuilder = getAsmBuilder();
      _modBuilder = asmBuilder.DefineDynamicModule(“DynTypeModule“);
   }
   return _modBuilder;
}

private static TypeBuilder getTypeBuilder(string name) {
   ModuleBuilder modBuilder = getModBuilder();
   return modBuilder.DefineType(name + “PropertySet“, TypeAttributes.Class | TypeAttributes.Public, typeof(PropertySetBase));
}

These methods will create a new AppDomain, with a dynamically generated assembly, with a dynamic module into which the types will be defined.  The assembly is given permission enough to run but cannot be persisted to disc.  Additionally, the TypeBuilder defines that the types it creates will be public classes that derive from PropertySetBase.

Once this framework is established, we can go through the motions of taking our IToolPropertyDefn instances and converting them into actual, runtime properties.  Therefore, let's take our first code example above and flesh-out the if {...} block.

if ( null == type ) {
   TypeBuilder tb = getTypeBuilder(toolId.ToString());
   createCtor(tb);

   if ( null != props && props.Length > 0 ) {
      foreach ( IToolPropertyDefn defn in props ) {
         addProperty(tb, defn);
      }
   }
   type = tb.CreateType();

   // cache off the newly created type
   _types[toolId] = type;
}

The following procedures establish how to create the class constructor (so instances can be created) as well as the property/method implementation:

private static Type[] _ctorParamsString = new Type[] { typeof(string) };

private static void createCtor(TypeBuilder tb) {
   ConstructorInfo baseCtor = (typeof(PropertySetBase)).GetConstructor(_ctorParamsString);
   ConstructorBuilder ctor = tb.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, _ctorParamsString);
   ILGenerator il = ctor.GetILGenerator();
   il.Emit(OpCodes.Ldarg_0);         // push the 'this' pointer
   il.Emit(OpCodes.Ldarg_1);         // push the string param
   il.Emit(OpCodes.Call, baseCtor);  // call the base ctor
   il.Emit(OpCodes.Ret);             // return - if omitted we get an InvalidProgramException
}

private static void addProperty(TypeBuilder tb, IToolPropertyDefn defn) {
   FieldBuilder fb = tb.DefineField(defn.PrivateName, defn.DataType, FieldAttributes.Private);
   PropertyBuilder pb = tb.DefineProperty(defn.Name, PropertyAttributes.HasDefault, defn.DataType, Type.EmptyTypes);

   // establish the description attribute
   ConstructorInfo descCi = typeof(DescriptionAttribute).GetConstructor(_ctorParamsString);
   CustomAttributeBuilder descCab = new CustomAttributeBuilder(descCi, new object[] { defn.Description });
   pb.SetCustomAttribute(descCab);

   // establish the default value attribute
   ConstructorInfo defCi = typeof(DefaultValueAttribute).GetConstructor(new Type[] { defn.DataType });
   CustomAttributeBuilder defCab = new CustomAttributeBuilder(defCi, new object[] { defn.DefaultValue });
   pb.SetCustomAttribute(defCab);

   // define the getter and setter methods
   ILGenerator il;
   if ( ( defn.Mode & PropertyMode.Read ) > 0 ) {
      MethodBuilder getter = tb.DefineMethod(“get“ + defn.Name, MethodAttributes.Public, defn.DataType, Type.EmptyTypes);
      il = getter.GetILGenerator();
      il.Emit(OpCodes.Ldarg_0);
      il.Emit(OpCodes.Ldfld, fb);  // load the private field into the return value
      il.Emit(OpCodes.Ret);
      pb.SetGetMethod(getter);
   }

   if ( ( defn.Mode & PropertyMode.Write ) > 0 ) {
      MethodBuilder setter = tb.DefineMethod(“set“ + defn.Name, MethodAttributes.Public, null, new Type[] { defn.DataType });
      il = setter.GetILGenerator();
      il.Emit(OpCodes.Ldarg_0);
      il.Emit(OpCodes.Ldarg_1);
      il.Emit(OpCodes.Stfld, fb);  // assign the string to the private field
      il.Emit(OpCodes.Ret);
      pb.SetSetMethod(setter);
   }
}

That's about all there is to it - at least for a really simple property with an underlying field.  Data gets assigned to and read from the underlying field.  The last remaining step is to create the instance from the type and return it from the Build() method created above.  The following code takes the Type as defined, calls the constructor on it, and returns the instance:

internal static PropertySetBase Build(Tools toolId) {
  
// ... code to create the type as described above
  
   ConstructorInfo ci = type.GetConstructor(_ctorParamsString);
   PropertySetBase ret = ci.Invoke(new object[] { ToolHelper.GetToolName(toolId) }) as PropertySetBase;

   // iterate through the properties and assign the default values
   // note, we cannot necessarily assign the value via the property because not all properties allow
   // writing (e.g. they are readonly).  therefore, reflect to the underlying field we created
   // and assign the value directly to it.
   if ( null != props && props.Length > 0 ) {
      foreach ( IToolPropertyDefn defn in props ) {
         FieldInfo fi = type.GetField(defn.PrivateName, BindingFlags.NonPublic | BindingFlags.Instance);
         fi.SetValue(ret, defn.DefaultValue);
      }
   }

   return ret;

}

As you can see, it's pretty easy and straightforward.  With this object suite and structure, I'm not working with an extremely diverse range of objects, but it's working remarkably well.  In the next discussion, we'll talk about how to programmatically assign values to the properties and how to reverse engineer the instances into their ASP.NET code form reliably.

Friday, January 21, 2005 9:39:00 AM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Thursday, January 20, 2005

Words that should be in the dictionary pretty soon.

splab ('splab)
adj. splab·ber; splab·best

1slang
   a: of or related to a high degree of excellence
   b: very good, fashionable
synonyms:  cool, excellent, fashionable
example: The app was definitely splab.

 

whap ('hwäp, 'wäp)
v. whapped; whap·ping

1: to assemble
2: to create, specifically in the realm of software
3: alt: whap-out: to complete
synonyms: code, develop, write
exampleWe whapped-out that app.

These new words can be combined to form from pretty elaborate sentences that will undoubtedly enhance the developer's vocabulary:

Let's whap a splab app, chap.

Thursday, January 20, 2005 6:03:00 AM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [2]  |  Trackback