CodeBetter.Com
CodeBetter.Com
RSS 2.0 via Feedburner
           Do you Twitter? Follow us @CodeBetter

Grant Killian's Blog

No, this has nothing to do with beer -- but maybe it should?

October 2003 - Posts

  • IIS 6 Resource Kit

    Check out the IIS 6 Resource Kit; it's got some cool tools for administering your new IIS 6 (Windows 2003) server.  You can get it from Microsoft here.

    I discovered this kit in the process of debugging a Response Buffer problem on the applications we migrated from IIS5 to IIS6, Dave Burke has blogged the same sort of experience (and the solution I arrived at, too) here, so I don't need to get into the MetaBase explorer.

    The Resource Kit has some cool tools for testing and analyzing a web server!

    Happy .Netting!

  • Async Kitchen "Sync"

    Since I've discussed Asynchronous processing on past blogs, I've gotten a few questions about how to cancel an asynchronous process.  I figure I should blog my response, and while I'm at it, include most of the Async stuff into a single unified example; providing a .Net Kitchen “Sync“ (I know Darrell really appreciates these puns). 

    Using the techniques discussed in my post here (http://dotnetjunkies.com/weblog/grant.killian/posts/1805.aspx) and earlier, we can add a "Cancel" feature to our example without too much trouble.

    For this example, I place two button controls and a progressbar on a form (leaving the default names for simplicity); one button will launch our asynchronous work, the other button will cancel it.  We need to extend our StatusEventArgs class to include a boolean indicator for when the user cancels the async work, something like:
     public bool isStopped;

    Of course a public property is better technique, but not as terse for an example.

    Next, we can add a public boolean variable to the winform; when button2 is clicked, we set this variable to true:
     public bool _blnIsStopped;
     private void button2_Click(object sender, System.EventArgs e)
     {
      _blnStopped = true;
     }

    Next, we need to build in the logic to stop asynchronous processing based on this _blnStopped flag (this is in the winForm app and handles updates broadcast by the asynchronous work):
     void ShowProgress( object sender, StatusEventArgs e)
     {
      if( this.InvokeRequired == false )
      {
       ShowProgressHandler showProgress =
        new ShowProgressHandler( ShowProgress );
       Invoke( showProgress, new object[] { sender, e } );
      }
      else
      {
       e.isStopped=_blnStopped;
      }
     }

    By setting the isStopped flag to true in the synchronous ShowProgress method, thanks to the reference variable StatusEventArgs, we can communicate the "cancel" request to the asynchronous processing.  To complete the coding, we just need something like the following:
     if( !e.isStopped ) { //check to see if the user stopped the processing
      //do time consuming work
      ShowProgress( sender, e ); //call to win app
     } else {
      //the user stopped the processing . . .
      break;
     }

    As promised, here is a unified example showing asynchronous processing with exception handling, status updates to the client application, and support for cancellation of the async work from the client app:

    //WinForm code:
      public delegate void ShowProgressHandler(object sender, StatusEventArgs e);
      public delegate void ClientShowProgressDelegate( showProgressDelegate del );
      public bool _blnStopped = false;

      private void button1_Click(object sender, System.EventArgs e)
      {
       showProgressDelegate del =
        new showProgressDelegate( ShowProgress );
       util obj = new util();
       ClientShowProgressDelegate  cliDel =
        new ClientShowProgressDelegate( obj.populateProducts );
       cliDel.BeginInvoke( del, null, null );
      }

      void ShowProgress( object sender, StatusEventArgs e)
      {
       if( this.InvokeRequired == false )
       {
        ShowProgressHandler showProgress =
         new ShowProgressHandler( ShowProgress );
        Invoke( showProgress, new object[] { sender, e } );
       }
       else
       {
        if( object.ReferenceEquals( e.TheException, null ) ) {
         progressBar1.Maximum = e.TotalProgress;
         progressBar1.Value = e.ProgressSoFar;
         if( e.isComplete )
         {
          MessageBox.Show( "Finished" );
         }
         e.isStopped=_blnStopped;
        }else {
         MessageBox.Show( "Exception thrown: " + e.TheException.ToString() );
         progressBar1.Value = 0;
        }
       }
      }

      private void button2_Click(object sender, System.EventArgs e)
      {
       _blnStopped = true;
      }


    //Our async code in the same namespace:

     public delegate void showProgressDelegate( object sender, StatusEventArgs e );
     public class util{
      public void populateProducts( showProgressDelegate ShowProgress )
      {
       StatusEventArgs e = new StatusEventArgs( 10, 0 );  
       object sender = new object();           
       try{            
        ShowProgress( sender, e ); //call to win app                      
        for( int x = 0; x < 10; x++ )
        {
         if( !e.isStopped ) {
          System.Threading.Thread.Sleep( 5000 );
          e.ProgressSoFar+=1;
          ShowProgress( sender, e ); //call to win app
         } else {
          //the user stopped the processing . . .
          e.ProgressSoFar=0;
          break;
         }
        }
        if( !e.isStopped ) {
         e.isComplete = true;
        }
        ShowProgress(sender, e); //final call to win ap
       } catch( Exception ex ) {
        e.TheException = ex;
        ShowProgress( sender, e );
       }
      }
     }

     public class StatusEventArgs : EventArgs
     {
      public bool isComplete;
      public int TotalProgress;
      public int ProgressSoFar;
      public Exception TheException;
      public bool isStopped;
      public StatusEventArgs( int totalProgress, int progressSoFar )
      {
       this.isStopped = false;
       this.isComplete = false;
       this.TotalProgress = totalProgress;
       this.ProgressSoFar = progressSoFar;
      }
     }

    This concludes the async portion of this weblog (probably, but I wouldn't underestimate my ability to beat a dead horse -- just ask a few students!).

    Happy .Netting!

  • Viewing ViewState

    My ASP.Net class is just getting underway and we're getting into the topic of state management.  We briefly covered the intrinsic Application and Session objects as State repositories, and ViewState was brought into the conversation.  ViewState plays an important role in maintaining control values (a.k.a state) between postbacks of your ASP.Net pages.  Let's learn a bit more about ViewState by decoding it in a simple web application.

    I added a textbox, a button, and a datagrid to a webform and added the following code to the page (be sure to make the textbox TextMode="MultiLine" for ease of reading):

      private void Page_Load(object sender, System.EventArgs e)
      {
       string cn = "server=server;database=northwind;uid=sa;pwd=password;";
       string sql = "SELECT TOP 5 * FROM Customers";
       DataGrid1.DataSource = SqlHelper.ExecuteDataset( cn, CommandType.Text, sql );
       DataGrid1.DataBind();
      }

      //writes the decoded viewstate to the textbox
      protected void decodeViewState()
      {
       string strV = HttpContext.Current.Request[ "__VIEWSTATE" ].ToString();
       byte[] arrBytes = System.Convert.FromBase64String( strV );
       ASCIIEncoding enc= new ASCIIEncoding();
       char[] charArray = new char[ enc.GetCharCount( arrBytes, 0, arrBytes.Length ) ];
       enc.GetDecoder().GetChars( arrBytes, 0, arrBytes.Length,charArray, 0 );
       foreach( char c in charArray )
       {
        Textbox1.Text += c;
       }
      }

      private void Button1_Click(object sender, System.EventArgs e)
      {
       decodeViewState();
      }

    I should note I also added a reference to the MSFT Data Access Application block (that's where the SqlHelper class originates) and set the following using statements for convenience:
    using Microsoft.ApplicationBlocks.Data;
    using System.IO;
    using System.Text;

    When this page loads, our datagrid displays the first 5 rows from the Customers table in the NorthWind database.  If we view the generated HTML source for this page, we can see the viewstate stored in a hidden HTML input tag; the HTML will look something like the following (I deleted a lot of the value since I think you get the idea):

    INPUT type=hidden value=dDwxOTI0OTQwNjAwO3Q8O2w8aTwxPjs+iAzNCA2Nzs+Pjs+Ozs+Oz4+Oz4+Oz4+Oz4+Oz4+Oz4W9zriiE/kuozP9dKgDteQYb9P5w== name=__VIEWSTATE

    If we press our Button1 (and invoke the decodeViewState method), our textbox will display the decoded version of the viewstate (I chose to NOT delete any of the decoded text so you could see the whole thing):

    t<1924940600;t<;l;>;l;>;l;l;i<5>;i<1>;i<5>;>>;>;;;;;;;;;@0<@0;l;>>;;;;>;@0;l;>>;;;;>;@0;l;>>;;;;>;@0;l;>>;;;;>;@0;l;>>;;;;>;@0;l;>>;;;;>;@0;l;>>;;;;>;@0;l;>>;;;;>;@0;l;>>;;;;>;@0;l;>>;;;;>;@0;l;>>;;;;>;>;>;l;>;l;i<2>;i<3>;i<4>;i<5>;>;l;i<1>;i<2>;i<3>;i<4>;i<5>;i<6>;i<7>;i<8>;i<9>;i<10>;>;l;l>;>;;>;t;l>;>;;>;t;l>;>;;>;t;l>;>;;>;t;l>;>;;>;t;l>;>;;>;t;l< \;;>>;>;;>;t;l<12209;>>;>;;>;t;l>;>;;>;t;l<030-0074321;>>;>;;>;t;l<030-0076545;>>;>;;>;>>;t<;l;i<1>;i<2>;i<3>;i<4>;i<5>;i<6>;i<7>;i<8>;i<9>;i<10>;>;l;l>;>;;>;t;l>;>;;>;t;l>;>;;>;t;l>;>;;>;t;l>;>;;>;t;l>;>;;>;t;l< \;;>>;>;;>;t;l<05021;>>;>;;>;t;l>;>;;>;t;l<(5) 555-4729;>>;>;;>;t;l<(5) 555-3745;>>;>;;>;>>;t<;l;i<1>;i<2>;i<3>;i<4>;i<5>;i<6>;i<7>;i<8>;i<9>;i<10>;>;l;l>;>;;>;t;l>;>;;>;t;l>;>;;>;t;l>;>;;>;t;l>;>;;>;t;l>;>;;>;t;l< \;;>>;>;;>;t;l<05023;>>;>;;>;t;l>;>;;>;t;l<(5) 555-3932;>>;>;;>;t;l< \;;>>;>;;>;>>;t<;l;i<1>;i<2>;i<3>;i<4>;i<5>;i<6>;i<7>;i<8>;i<9>;i<10>;>;l;l>;>;;>;t;l>;>;;>;t;l>;>;;>;t;l>;>;;>;t;l<120 Hanover Sq.;>>;>;;>;t;l>;>;;>;t;l< \;;>>;>;;>;t;l>;>;;>;t;l>;>;;>;t;l<(171) 555-7788;>>;>;;>;t;l<(171) 555-6750;>>;>;;>;>>;t<;l;i<1>;i<2>;i<3>;i<4>;i<5>;i<6>;i<7>;i<8>;i<9>;i<10>;>;l;l>;>;;>;t;l>;>;;>;t;l>;>;;>;t;l>;>;;>;t;l>;>;;>;t;l>;>;;>;t;l< \;;>>;>;;>;t;l>;>;;>;t;l>;>;;>;t;l<0921-12 34 65;>>;>;;>;t;l<0921-12 34 67;>>;>;;>;>>;>>;>>;>>;>>;>?:??O?????????a?O?

    As we see, the ViewState is simply base64 encoded text; some of it is hard to read even after the unecoding with all the angled brackets and semi-colons, but the data is all apparent.  The ASP.Net plumbing uses this to track state data (such as location in a paged datagrid, make datagrid rows available in PostBack event handling, etc.).  You will may never need to decode ViewState like I've done, but it is good to understand that there is no magic to it.  It's just some encoded text storing properties and data related to the controls on your page.  I should point out that you can add your own data to ViewState (as an alternative to Session state -- but with a different set of pros and cons); you can also encrypt ViewState using the machine.config file on the server.  These are both topics for another blog.

    If you can't wait of another blog on ViewState, here are some additional resources . . .

    Back in June, Chris Hale did an overview of ViewState for WeProgram.Net.  Those materials are available here (at the bottom of the page).  For a solid MSFT treatment of the subject, including how to tune or disable ViewState to improve performance, check out this link: http://msdn.microsoft.com/netframework/downloads/samples/default.aspx?pull=/library/en-us/dnaspnet/html/asp11222001.asp.

    Happy .Netting!

  • Why I use Equals() and Comparative Simplicity

    Students frequently ask me why I prefer Equals() over = when coding VB equality comparisons.  To answer this question, we need to get into ILDasm, the tool that we can use to view IL code in any .Net assembly.

    Take the following VB snippet:   

    public sub doStuff()
            dim a as String = "Cats"
            dim b as String = "Dogs"
            if a = b then
                MessageBox.Show( "Unlikely!" )
            end if
            if a.Equals( b ) then
                MessageBox.Show( "Still unlikely!" )
            end if
    end sub

    Compile your program and check out the IL this generates using ILDasm.  We can use ILDasm by firing up ILDasm.exe and manually browsing to the assembly with our doStuff() method in it; ILDasm.exe can be found in your \Program Files\Microsoft Visual Studio .NET\FrameworkSDK\Bin directory.  Alternately, and this is my preferred approach, I add ILDasm as a VS.Net add-in (by going to Tools-->External Tools and choosing to “Add” a new Tool with the path to ILDasm.exe as the “Command” (it's C:\Program Files\Microsoft Visual Studio .NET\FrameworkSDK\Bin\ildasm.exe on my machine) and the “Argument“ $(TargetPath) -- this fires up ILDasm for the assembly I'm working with in VS.Net, very slick!).  Note, you only have to add ILDasm once to your External Tools; from then on, it can be invoked from the Tools menu directly.

    Regardless of how you do it, open ILDasm and explore the treeview representation of your assembly (for more on ILDasm, check out this article or this MSDN piece).  Expand some of the ILDasm nodes and appreciate the hierarchical view ILDasm provides . . . by double clicking on a method in your class, you can peruse the IL.

    If you open the doStuff method in ILDasm you'll see the instructions created by using “=“ versus Equals().  The “=“ approach creates a call like the following: 

    IL_0010:  call       int32 [Microsoft.VisualBasic]Microsoft.VisualBasic.CompilerServices.StringType::StrCmp(string,
                                                                                                                  string,
                                                                                                                  bool)

    while the Equals() approach creates the following:

      IL_0026:  callvirt   instance bool [mscorlib]System.String::Equals(string)

    The approach using mscorlib, Equals(), is smaller and quicker than the = method in VB because with “=” we're calling into the VB specific string comparison logic instead of the leaner .Net framework implementation.  Can a human appreciate the performance difference for a single comparison, certainly not, but in looping situations and other performance critical operations, it's good to avoid the “=” comparison.  I use “Equals()” instead of “=” all the time so I'm in the habit of producing the most efficient code as possible.

    Note that the c# “==” comparison operator doesn't suffer from the same legacy comparison framework; if( a==b ) creates the same IL as if( a.Equals( b ) ) in c#.  Score another point for c#.  Since I do a lot of programming in both languages, anything I can do to cut down on language nuances is a plus, so I stick with Equals() when using c# too; it makes my life comparatively simpler!

    Happy .Netting!

  • You Wont See Me at The PDC . . .

    • Not because PDC is sold out (if I was going, I would've bought my seat a while ago!)
    • Not because all the stuff Microsoft will unveil will be made available publicly when the products actually get to market (which will be years away in some cases)
    • Not because it's in California
    • Not because of the price tag ($2,000 + air + food + hotel + other expenses) or because my company's training budget can't squeeze this one in . . . for the right set of circumstances, I would pay my own way!

    You won't see me at the PDC because I'll be in the Howl At The Moon adventure race that takes me out of town starting the 23rd of October; I should be back to work Tuesday the 28th, and while I could catch some PDC action from that point, I'm choosing to pass and rely on the strength of the blogging, magazine, book, and more local presentation communities (such as WeProgram.Net) to keep me abreast of what I need to know.

    I think the thing I'll miss the most is the fun and excitement of seeing the features and developing paradigm shifts first hand . . . but I'll know that in the years to come, when I look back, I'll be glad I took my money and time and busted my butt trekking, paddling, and biking through the Florida wilderness instead of sitting in a conference hall with hundreds of other computer professionals.  My career revolves around software, but not quite my entire life!

    To those that go to the PDC, have a great time and I'm sure you'll see a great show!  To the rest of us, WeProgram.Net will be adding a “PDC Tracking” section to document and share all the cool PDC stuff that develops.

    'Nuff said; I'm scheduled for a 100 minute training run in a few minutes (with backpack) . . .

    Happy .Netting!

  • Do customers deserve more than they ask for?

    Some people in my company spent most of this afternoon feverishly running around because one person on our staff gave a customer more than they asked for; the “extra effort“ ended up causing problems and complications and boiled over into some colorful telephone “conversations“ and has yet to be resolved.  I'm just a casual observer to this meltdown, but it got me thinking . . .

    I don't want to go into specific details, so let's take a hypothetical situation where a developer creates an application to track business contacts.  The application runs fine.  The .Net framework makes building in SMTP support easy, so the developer has a little extra time and builds an extra feature for emailing messages and newsletters to the contacts; this feature is developer-defined scope creep and doesn't appear on any list of customer requirements.

    The developer thinks they've gone above and beyond to please a customer, and in some respects they have, but when problems start with the SMTP function -- problems that impact the rest of the application (perhaps the program crashes when an SMTP Server is unavailable), how does one handle the support for this?  Does the customer pay to fix a feature they didn't ask for, but now causes problems with their program?  It's a sticky situation.

    There are many development methodologies out there, but I'm not aware of any that advocate doing more than you're asked like this.  For one, Agile Methods (Martin Fowler has a good resource on “Agile Methods” here) dictate you do the absolute minimum to satisfy the immediate customer requirement and don't program for future extensibility.  This leaves a bad taste in my mouth, however, because I usually find a certain level of prior planning and foresight is wise. 

    For example, after our WeProgram.Net session a few days back, Brian Noyes (from IDesign and SoftInsight) shared his experience with the User Interface Process (UIP) Block and related how he developed a small program for a customer that could have used the UIP, but he chose to save the extra time effort (which means money to the customer).  As often happens, the customer came back wanting to extend the application, and the UIP would've made that extension so much easier (and cheaper for the customer)!

    My point is, sometimes customers deserve more than they ask for.  A smart, extensible framework can be very valuable!  Other times, a simple and direct approach to solving a problem might be the way to go. 

    How does a developer choose? 

    There aren't any easy answers.  It's a combination of experience, communication with the client, and luck.  If I had to assert some rules regarding these situations, I would offer:

    • Always error on the side of simplicity and stick to the specs!  Avoid over engineering -- not everyone is building the next Amazon.com!
    • Check out the Application Blocks (UIP and others from Microsoft) and develop an organizational set of practices to ensure a measure of consistency in your applications.  It really makes enhancements and debugging easier!
    • Don't create functions the client doesn't request, unless it's behind the scenes and related to your intelligent framework.  In my hypothetical example, the programmer shouldn't have added a full set of features never requested.

    I wish I had a happy ending to wrap this up with, but I don't.  The two companies are going back and forth over the issue.  If there's anything worth sharing on this issue, I'll be sure to keep you updated!

    Happy .Netting!

  • On Flexible Work Schedules

    I wrote about are we working too much before, but I feel like sharing a little more on this.

    While I’m not aware of any detailed scientific studies, organizations don't appreciate how valuable flexibility in a working schedule can be.  For example, I typically work over 50 hours each week, but it feels like 30 because my company doesn’t have the draconian and antiquated idea that “in order to be working, they must be sitting within 100 feet of me.”  For more on this general subject, check out http://www.flexwork.eu.com/.  [Update: Here is another good study courtesy of Mark DiGiovanni.]

    I wake up about 7 am and catch up on email, handle some admin stuff related to our web applications (I’ll be automating those soon!).  I’ll then eat breakfast and head to in to the office around 9 am.  I usually work there or at a customer’s site until around 5 PM.  That’s 1 hour at home, and 7 or 8 at the office depending on any lunch break I take.  On many week nights, around 9 PM I work remotely for 2 or 3 more hours.  On the typical weekend, I will work 4-5 hours spread between Saturday and Sunday, with Sunday night being a common time for me to get work done in anticipation of the coming week. 

     

    Let me count:

    That’s 1 morning hour from home 5 days per week.

    7.5 hours at “the office” 5 days per week.

    2.5 hours in the evenings 2 nights per week.

    4.5 hours in weekend evenings.

    Totals 52 hours.

     

    I should point out, if I had to spend the full 52 hours in our company offices, I doubt I would feel as up beat about this.  I would be less productive because 1) I'd be unhappy seeing less of my family and 2) I would not have time to “recharge” between work sessions -- which is the big benefit I enjoy currently.  Also, I have peace and quiet at home that I DON'T have at my current job (my office doesn't have a door I can close and it's hard to concentrate sometimes!).  Having the freedom to work in a way that fits my lifestyle is crucial to my success as a developer and continued “freshness” when it comes time to solve problems and generally be smart; I would surely burn out on a forced regimen, but when it’s contoured to fit around my wife and activities (soccer, etc), I couldn’t be happier.

     

    To close, I should point out that my estimated 52 hour week doesn’t count the time I spend preparing and teaching ITPro classes, reading/learning Tech topics, and other industry related “work;” this can be a considerable (2 or 3 classes per week!).  Also, like everyone, I have an occasional all-nighter or other marathon work weeks.

     

    How does your organization approach this issue?

  • A different programming ASPECT

    There are so many interesting facets to the software development industry!   One “aspect” (pun intended, read on . . . ) of the industry I've been working to follow is Aspect Orientated Software Development (AOSD) and what problems I can solve through an aspected oriented approach.  Here is a good reading list compiled by a bright Aspected Oriented thinker, Jason Furlong, http://www.cs.queensu.ca/~stl/pdf/AOSD_handout.pdf; admittedly, some of it's a bit academic and heady, but isn't that often the good stuff?!  If your tired of the beginner articles about ASP.Net validation controls or which managed ADO.Net data provider to choose, check out Furlong's list. 

    If you start really getting into AOSD, check out the discussions at http://aosd.net/mailman/listinfo/discuss!

    Happy .Netting!

  • Sweet . . . Nourishing . . . CodeSmith

    I usually avoid the "yah, what they said . . ." blog entries since there are plenty of those around, but I'm making an exception in this case.  Mark "Bonafiglio" Bonafe was very impressed with CodeSmith, and I have to agree with him.

    I had heard good things about CodeSmith, and after I read Mark's blog on the topic a few weeks ago I figured I should add "Checking Out CodeSmith" to my todo list.  When it came time to add some more entity objects to an application I'm working on, I decided to fire CodeSmith up and give it a whirl.  The architecture my project uses frequently maps business objects directly to database objects, so there's lots of repetitive coding involved with the creation of properties; object creation and persistance is handled by my home grown DataAgent object that uses Reflection and custom Attributes to handle SQL SELECT, INSERT, and UPDATE statements.  The plumbing to work with this DataAgent is simple, but involves placing parameterized attributes on each class and property like so:
    namespace EntityObjects
    {
     [DBTableAttribute( "tblTask" )]
     public class TaskData
     {
      public TaskData()
      {
      }
      private int _intID = -1;
      [DBColumnAttribute( "TaskID", true )]
      public int TaskID
      {
       get{ return _intID; }
       set{ _intID = value; }
      }
      private string _strTitle;
      [DBColumnAttribute( "Title" )]
      public string Title
      {
       get{ return _strTitle; }
       set{ _strTitle = value; }
      }
     }
    }

    Once I created the proper CodeSmith template (I modified the BusinessObject template) the tedious work of typing everything was completed in seconds, including the strongly typed collections for my objects -- everything!

    I was so giddy, I added proper comments to the Template and some other bells and whistles, regenerated the entire set of EntityObjects from the database, and now my EntityObjects are 100% consistent with eachother and completely uniform.  For many programmers, myself included, that "uniformity" is a thing of pristine beauty.  Knowing that all the curly braces and indentations line up straight, all the member variables are properly prefixed and formatted (admittedly, I'm still a Hungarian notation guy), and that each class is literally cut from the same cloth are additional reasons to use a good code generator like CodeSmith.   

    I should also point out, that my framework allows for the recreation of these EntityObjects at any time (provided the database names don't change!), so if I add a column I can just regenerate the class and I'm all set with the business object.  On an architectural aside, any modifications to these EntityObjects are made in derived classes; so if I want to add a specific behaviour to the object (an "AssignTask" method, for example), I'd keep the functionality outside the generated class and in a derived “Task” object:

    public class Task : TaskData

    Anyway, now that I've drank the CodeSmith kool-aid, I'm sure I'll discover many interesting applications for it.

    Happy .Netting!

  • SQL Replication Consternation

    We've been experimenting with a proof-of-concept application using the .Net Compact Framework; right now, we're just exploring replicating data between a mobile PocketPC (with SQLServerCE) and a SqlServer.  Plain vanilla sort of stuff.  It was working well despite the errors (typos and misplaced parenthesis) in the MSDN “QuickStart” tutorials, but I got into true trouble when I exported the database and moved it to another server for some other development work we were doing.  The web applications hitting this other database weren't functioning properly; all the database activity was causing odd errors referencing things like “sysmergearticles“ and “maxversion” when the ASPX pages would load. This new copy of the database was failing all over the place!

    Before long I was searching google and MSDN for any insight, and although there was no direct response to my problem, bit-by-bit (pun fully intended!) the problem started to come into focus.  Sysmergearticles and maxversion pointed me towards our replication experimentation, and I was on the right track.

    We were using Merge Replication between our SqlServerCE and SqlServer, and part of that replication instrumentation (SQL Server db objects) were messing up the exported database.  Our subscriptions and publications (two main pieces of Replication) were apparently fouled up; I removed all the publishing information I could find from the new database, but our problems still persisted.  Somewhere there were still replication components that needed cleaning up!  Finally, I fired up Red-Gate softeware's SQL Compare to see if I could find anything between the two versions of the databases (note, Brendan Tompkins turned me on to this product several months ago), and after a few minutes of comparing db schemas, I learned that the triggers on the replicated tables were not automatically removed when I used Enterpise Manager to remove the Replication from my database.  I deleted the unnecessary triggers from the replicated tables and all was well.  Deep breath . . .

    I don't know if this is common and I'm just new to SQLServerCE replication, or if it's due to the method I exported the database with, but I thought I'd share this experience.  I learned a lot from the process, so it wasn't a futile exercise!  Along the way I discovered two new (at least to me) .Net Compact Framework related sites:

    Happy .Netting!

More Posts