Blog Maintenance

November 10th, 2008

 

I had been a little remiss in updating the blog (both posts and administrative stuff.) Updating a WordPress site is not particularly difficult but can be a little tedious because after backing up the site you either have to delete a bunch of files (but only certain files) from several different directories (but not all) then copy in the new files. I’ve done that in the past but I’ve also updated just by copying new files over top of the old ones. It’s not the recommended way to do it but I’ve never had a problem. Bottom-line is either way it’s a task that needs to be scheduled and with all the other tasks I need to do it usually gets pushed off.

So while looking for an anticipated release date of version 2.7 I stumbled over a reference to an automatic upgrade installer plugin (you can find it here.) Very simple to use, takes only a few minutes, and still follows the “best-practices.” Only glitch I ran into is Vista refuses to open the .ZIP files that the upgrade creates when it backs up the existing files and database. WinZip on WindowsXP has no problem with it.

While I was at it I added the new encryption salts to the configuration file (this is a feature added to WordPress 2.5 and enhanced in 2.6.) Hey, a little more security never hurt anyone especially if it’s transparent to the visitor or user.

Exploring Entity Framework

November 7th, 2008

 

Since the dashboard project reached a new milestone last month I’ve had time to start planning the next two projects. The first is a private Internet site for customers to place orders, check on order status, check on payment status, etc. The second, and much larger, is a total rewrite of the order entry, shipping, inventory, receivables, payables, and general ledger system from the mish-mash of legacy languages and data storage to a .NET / SQL Server based system.

The dashboard uses a combination of the patterns & practices Enterprise Library and naked ADO.NET. The client is deployed via ClickOnce and talks to the server via ASPX Web Services. It’s built using the 2.0 framework and that was about it for data access options (I’m ignoring things like NHibernate, Subsonic and LLBLGen for the moment.) Now with the new projects I’m reviewing the current crop of options, starting with Entity Framework (which RTM’d when SP1 for VS2008 was released) and LINQ to Entities. I built the database and then created the entity model:

EntityDesignerDiagram

I’m starting with some simple CRUD operations for the salesman, route, chain, and customer tables:

image

For the moment the data model closely matches the legacy data model; that’s why the orders table references the customer table and the chain table while the customer table also references the chain table. This will most likely change going forward as I refine the model.

Then I created an ugly-as-sin, break-every-rule form for the chain table:

image

Here’s the FormLoad:

   1: void HandleFormLoad(object sender, EventArgs e)
   2: {
   3:     InitializeForm();
   4:     LoadStatesDropdown();
   5:     TransitionToMode(FormMode.KeyMode);
   6:     txtChainCode.Focus();
   7: }

InitializeForm() simply sets the MaxLength of each TextBox; it’s hard-coded now but I’d like it to come from the model’s meta-data.

LoadStatesDropdown() does exactly what it says; it uses a small piece of LINQ to populate an Infragistics combobox with the states:

   1: private void LoadStatesDropdown()
   2: {
   3:     var q = from s in ctx.states orderby s.abbrev select s;
   4:     cboChainState.DataSource = q;
   5:     cboChainState.ValueMember = "abbrev";
   6:     cboChainState.DisplayMember = "abbrev";
   7: }

TransitionToMode() enables or disables controls and sets up or clears data binding depending on the form’s mode (KeyMode, EditMode, or AddMode.)

Here’s the data binding code:

   1: private void SetDataBinding(bool setIt)
   2: {
   3:     if (setIt)
   4:     {
   5:         txtChainCode.DataBindings.Add(new Binding("Text", currentChn, "chaincode", false, DataSourceUpdateMode.OnPropertyChanged));
   6:         txtChainName1.DataBindings.Add(new Binding("Text", currentChn, "chainname1", false, DataSourceUpdateMode.OnPropertyChanged));
   7:         txtChainName2.DataBindings.Add(new Binding("Text", currentChn, "chainname2", false, DataSourceUpdateMode.OnPropertyChanged));
   8:         txtChainAddr.DataBindings.Add(new Binding("Text", currentChn, "chainaddr", false, DataSourceUpdateMode.OnPropertyChanged));
   9:         txtChainCity.DataBindings.Add(new Binding("Text", currentChn, "chaincity", false, DataSourceUpdateMode.OnPropertyChanged));
  10:         cboChainState.DataBindings.Add(new Binding("Value", currentChn, "chainstate", false, DataSourceUpdateMode.OnPropertyChanged));
  11:         txtChainZip.DataBindings.Add(new Binding("Text", currentChn, "chainzip", false, DataSourceUpdateMode.OnPropertyChanged));
  12:         txtChainPhone.DataBindings.Add(new Binding("Text", currentChn, "chainphone", false, DataSourceUpdateMode.OnPropertyChanged));
  13:     }
  14:     else
  15:     {
  16:         txtChainCode.DataBindings.Clear();
  17:         txtChainName1.DataBindings.Clear();
  18:         txtChainName2.DataBindings.Clear();
  19:         txtChainAddr.DataBindings.Clear();
  20:         txtChainCity.DataBindings.Clear();
  21:         cboChainState.DataBindings.Clear();
  22:         txtChainZip.DataBindings.Clear();
  23:         txtChainPhone.DataBindings.Clear();
  24:     }
  25: }

I spent a lot of time searching online for databinding examples. All the ones I found were in the context of binding a grid or a list. None of them illustrated binding other controls to entities. After I figured it out it was pretty simple (note that the chain table contains no foreign keys; more on that later.)

Here’s the code behind the Submit button:

   1: void HandleSubmitClick(object sender, EventArgs e)
   2: {
   3:     if (!String.IsNullOrEmpty(txtChainCode.Text))
   4:     {
   5:         string findChain = txtChainCode.Text.Trim().PadLeft(3, '0');
   6:  
   7:         currentChn = ctx.chains.Where(c => c.chaincode.Equals(findChain, StringComparison.OrdinalIgnoreCase)).FirstOrDefault<chain>();
   8:  
   9:         if (currentChn == null)
  10:         {
  11:             if (MessageBox.Show(this, String.Format("Chain '{0}' was not found; would you like to add it?", findChain), "Chains",
  12:                 MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes)
  13:             {
  14:                 currentChn = new chain();
  15:                 currentChn.chaincode = findChain;
  16:                 TransitionToMode(FormMode.AddMode);
  17:                 txtChainName1.Focus();
  18:             }
  19:             else
  20:             {
  21:                 txtChainCode.Text = String.Empty;
  22:                 txtChainCode.Focus();
  23:             }
  24:  
  25:         }
  26:         else
  27:         {
  28:             TransitionToMode(FormMode.EditMode);
  29:         }
  30:     }
  31: }

In the legacy system, things like a chain code are numeric only, right-justified, zero-filled. In the original system they were the primary key; I plan on changing that to use a Guid as the primary key and use the human-readable code as a surrogate key.

It took my a little while to figure out how to handle foreign key constraint violations and I’m still not sure it’s the right way to do it:

   1: void HandleDeleteClick(object sender, EventArgs e)
   2: {
   3:     // only edit mode
   4:     if (DialogResult.Yes == MessageBox.Show(this, "Are you sure you want to delete this chain?", "Delete Chain", 
   5:         MessageBoxButtons.YesNo, MessageBoxIcon.Question))
   6:     {
   7:         try
   8:         {
   9:             ctx.DeleteObject(currentChn);
  10:             ctx.SaveChanges();
  11:             currentChn = null;
  12:             TransitionToMode(FormMode.KeyMode);
  13:             txtChainCode.Focus();
  14:         }
  15:         catch (UpdateException ex)
  16:         {
  17:             // referential integrity exception
  18:             ctx.Refresh(RefreshMode.StoreWins, currentChn);
  19:             MessageBox.Show(this, "Unable to delete chain; the chain is currently referenced by another part of the system.", 
  20:                 "Delete Chain", MessageBoxButtons.OK, MessageBoxIcon.Information);
  21:         }
  22:     }
  23: }

Again, I couldn’t find any mention of how to handle foreign key constraint violations. There are plenty of example on how to handle optimistic concurrency collisions; that’s where RefreshMode.StoreWins comes in and it seems to work correctly here as well. Without the ctx.Refresh() line, the failed DeleteObject() stays in the object context and the framework will try to execute it each time you call SaveChanges().

One thing I’m still trying to figure out is how to databind related objects. In the diagram above there are foreign keys from the customer table to the route table, salesman table, and chain table. In other words, a customer is in a route, has a salesman, and is in a chain. Here’s a portion of the code in the customer form’s SetDataBinding() method:

   1: Binding b;
   2: txtAccount.DataBindings.Add(new Binding("Text", currentCust, "account", false, DataSourceUpdateMode.OnPropertyChanged));
   3: umeChain.DataBindings.Add(new Binding("Value", currentCust.chain, "chaincode", false, DataSourceUpdateMode.OnValidation));
   4:  
   5: b = new Binding("Value", currentCust, "wkscredit", true, DataSourceUpdateMode.OnPropertyChanged, 0);
   6: b.Format += new ConvertEventHandler(HandleFormatWeeksCredit);
   7: b.Parse += new ConvertEventHandler(HandleParseWeeksCredit);
   8: spnWeeksCredit.DataBindings.Add(b);
   9: umeSalesman.DataBindings.Add(new Binding("Value", currentCust.salesmen, "smancode", false, DataSourceUpdateMode.OnPropertyChanged));

umeChain is a masked edit control that displays the customer’s chain code.

image

If the user changes the value for the chain control I want to retrieve the matching chain entity, attach it to the customer entity, and update the display. If the user entered an invalid chain, I want to whine at them and change it back to what it was. Pretty basic stuff that’s been done since the year one.

Here’s the code attached to the chain control’s Validating event:

   1: void umeChain_Validating(object sender, CancelEventArgs e)
   2: {
   3:     //  Did it change?
   4:  
   5:     if (!String.IsNullOrEmpty(umeChain.Text))
   6:     {
   7:         string tempChn = umeChain.Text.Trim().PadLeft(3,'0');
   8:         if (tempChn.Equals(currentCust.chain.chaincode))    // if the chain didn't change, don't do the query.
   9:             return;
  10:  
  11:         // Have to return a chains entity here so we can attach it to the customer.
  12:         chain chn = ctx.chains.Where(c => c.chaincode.Equals(tempChn)).FirstOrDefault<chain>();
  13:         if (chn == null)
  14:         {
  15:             MessageBox.Show(this, "Invalid chain", "Customers", MessageBoxButtons.OK, MessageBoxIcon.Warning);
  16:             e.Cancel = true;
  17:             umeChain.Value = currentCust.chain.chaincode;
  18:         }
  19:         else
  20:         {
  21:             if (!currentCust.chain.Equals(chn))
  22:             {
  23:                 currentCust.chain = chn;
  24:                 umeChain.Value = currentCust.chain.chaincode;
  25:             }
  26:         }
  27:     }
  28: }

This is where I’m currently having trouble. If I change the chain to one that actually exists (for example, 079 instead of 078) it throws an exception on SaveChanges():

image

A duplicate key error. Opening a quick watch on the context object shows a ModifiedEntityCount of 1 and an AddedEntityCount of 1. Nothing should have been added; this example is editing an existing entity. Drilling down into the ObjectStateManager shows one item in the _addedRelationshipStore (the EntityKey for the chain we changed to,) one item in the _deletedRelationshipStore (the EntityKey for the chain we changed from) and one item in the _modifiedEntityStore (the EntityKey for the chain we changed from not the EntityKey for the customer we changed.) If this was a bug I can’t believe it would have gone undiscovered so obviously I’m doing something wrong.

More to come…

Hold the Popcorn

July 2nd, 2008

OK so maybe there won’t be blood but it’s still interesting that Vlad is turning out to be better at playing Microsoft’s game than Microsoft is.

He’s already offering the type of hosted services that Microsoft has only been talking about and is hosting them in privacy-friendly Europe.

Note to self: look into his backup services. Have a couple of clients that have more data than they can cost-effectively back up to tape.

Tomorrow he will talk about what he’s “got in store for her majesty’s prisoners.” Since it used to be a penal colony for England I’m guessing he means Australia.

SMB Summit Videos

July 1st, 2008

During SMB Summit 2008 in Dallas Microsoft recorded the presentations on the first two days. The first day was Small Business Server 2008 while the second day was Essential Business Server 2008 (both have since shipped public betas.)

The videos have been posted the the private partner portal (you have to be at least a registered partner and sign in to view them.) Easiest way to find them is from the SMB Summit page.

There Will Be Blood

July 1st, 2008

I make it a point to follow Vlad’s blog. Even though he tends to use a sledge hammer where a more “delicate” approach might be called for you usually end up thinking about his posts for days. The best ones are where he holds up a “business mirror” and you don’t necessarily like the reflection you see.

So the other day he posted this. As StaceyC said in the comments, time to make some popcorn (cheddar for me please.)

Small Business Server 2008 World Wide Community Survey

May 27th, 2008

This year’s survey is live. Please take the couple of minutes to give them your feedback.

It’s better than sitting around bitching and moaning about how Microsoft doesn’t listen to the community.

The Power of Two

May 27th, 2008

As I said in earlier posts concerning SMB Summit the best part of this conferences is connections and friends you make. Two of the best guys I know in this crazy IT business are joining forces to better serve their customers in the Dallas Metroplex area.

Bill Leeman of Leeman Consulting and Kim Triff of Trif Technologies have announced their merger. If you’re in the Dallas area these are the "go-to" guys for IT consulting.

And be sure to visit their new offices at 5757 Alpha Rd, Dallas, TX on June 11th 2008 because they will be hosting the Microsoft Across America truck from 10:00 AM to 2:00 PM. You can preregister here or just drop in.

Good luck guys!

SMB Summit Wrapup Part 2

April 28th, 2008

I had intended to add a part about Essential Business Server (EBS) but in the interim Susan posted a series of links to documents about EBS on Microsoft’s download site so I’ll just point you to her list of links.

In addition a couple of people pinged me that I missed some of the presenters in my first post so in the interest of completeness…

Also presenting the SBS portion of the conference were Damian Leibaschoff (a Support Escalation Engineer), Ron Martinsen (a Senior Development Lead), Rod White (a Support Engineer) and Chris Almida (a Program Manager.)

SMB Summit Wrapup Part 1

April 25th, 2008

 

This past weekend I was in Dallas for the 4th SMB Summit. These things are always exhausting but you always come away jazzed about what other partners are doing and how you can apply that to your practice. It’s also a great opportunity to renew old friendships and make new acquaintances. This was the first one I attended.

The main event on Thursday was a presentation of Small Business Server 2008 by Aanal Bhatt and Becky Ochs. Aanal is in Partner Marketing while Becky is a Program Manager on the SBS dev team. There are some very concise summaries of their presentations here and here. Most people were very enthusiastic about the new release but there were a few grumblings here and there about the removal of ISA (now called Forefront Thread Management Gateway) from the SBS Premium SKU (it will still be included/required in the EBS SKU) and the removal of support for tape backup in the box. Only backup to disk is supported by the native backup (NTBACKUP is history). If you want tape you will have to use a third party backup program. I have mixed feelings about this one:

  • Backup to USB hard drive is generally much faster than backup to tape. With remote access to servers and extended hours the window for backup is getting smaller while the quantity of data to backup is getting larger. While VSS makes it possible to do "hot" (or at least "warm") backups I’m sort of "old school" and prefer a nice, quiet server during backup.
  • Backup to USB hard drive is generally less expensive than backup to tape. With the price of hard drives falling it has become very easy to add a lot of storage space for not a lot of money. Unfortunately the cost of tape drives and media that can handle that much storage during our ever-shrinking backup window can sometimes exceed the cost of the server hardware, software, and additional licenses.
  • Tapes are more portable for taking offsite. Hard drives are bulkier, heavier and less tolerant of rough handling than tape cartridges. Like it or not, it many small businesses it is Suzy Secretary that takes the tape home each night. She tosses the tape cartridge in her purse or backpack then tosses that her car. I can envision that hard drive not going offsite because it’s just too heavy to throw in a purse. And I can envision the hard drive being damaged (Murphy says you won’t know until you need it to save your bacon.)

There were some other morsels of info from the first day that I haven’t seen posted elsewhere (Disclaimer: it’s still in beta. Anything and everything is subject to change as Microsoft sees fit.)

  • The premium SKU can run Terminal Services in Application mode on the second server.
  • There are different CAL’s for the Standard SKU and the Premium SKU, you can mix and match them, and they are available in single packs as well as the current 5 and 20 packs. Of course this means there are now even more Microsoft part numbers to deal with: Single CAL Standard FPP, Single CAL Premium FPP, Single CAL Standard OEM, Single CAL Premium OEM, Single CAL Standard OL, Single CAL Premium OL. Now multiply by 5 pack SKU’s; 20 pack SKU’s; 1st year, 2nd year, and 3rd year of multi-year SA agreements, etc. etc. Yeeesh!
  • The new Server Manager is task based (think, Office 2007 "Ribbon.")
  • Installation has the option to use an answer file for unattended installs (this is in addition to the unattended install option we always had for the base OS.)
  • The Security Center can include client PC anti-virus monitoring (I suspect this will require the cooperation of the third-party AV publishers.)
  • There is a new enhanced Remote Web Workplace (with Single Sign On,) a Vista sidebar gadget, and Office Live Integration.
  • Adding servers to the domain use the native tools (no connectcomputer) while the clients require .NET 2.0. There is no automatic profile migration, an "I-Worker" can set themselves up on the network, and users are no longer automatically setup as a local administrator.
  • The CEICW wizard is gone. Making their debut are the:
    • Connect to the Internet Wizard (CTIW)
    • Internet Address Management Wizard (IAMW)
    • Trusted Certificate Installation Wizard (TCIW?)
    • Configure VPN Wizard
    • Configure Internet Mail Wizard
    • Fix My Network Wizard
  • SBS 2008 does not support two NIC’s; only a single NIC with some other firewall device between the network and the Internet. This firewall device could be a hardware device or ISA/FFTMG on a second server.
  • SBS 2008 can manage Internet domains as long as the domain is registered with "supported partners." The two partners Microsoft mentioned are eNom Central and Register.com; presumably more are on the way.
  • SBS 2008 can now handle Dynamic DNS on its own. I think that should still be a function of a hardware firewall although SBS handling it should eliminate the need for software such as the TZO agent.
  • Company Web over RWW now uses a different port: 987.
  • IPV6 is installed  along with IPV4 but you cannot remove IPV4; some things depend on it.
  • Setting up DNS no longer asks for forwarders; it now uses the root hints by default.
  • Users can be assigned to computers in RWW. When a user wants to connect to their computer they are connected directly to a particular client or can choose from a list. This will be useful when all the client computers are named WS1, WS2, etc.
  • Viewing user properties in the server manager no longer just drops you into the Active Directory property pages.
  • EMail can be archived to a SharePoint document library.
  • Wizards can be scripted with XML files. We don’t yet know whether it will be all wizards or just some and what level of scripting is available. My hunch is that PowerShell is going to become important here so if you haven’t spent much time looking at it it would probably be a good idea to start.
  • Users created in native AD tools will not show in the SBS console but there is an way to add them after the fact.

A few notes from the Messaging side of things:

  • Exchange 2007 is completely configured by the SBS wizards.
  • SmartHost will use TLS/SSL if available.
  • Forefront Security for Exchange anti-virus is pre-configured to scan well-known extensions at the transport and store levels. Anti-spam is set to quarantine.
  • The POP3 Connector lives on and has been completely re-written. E-mail is now delivered through SMTP so all spam checks are in place and work (of course, the best choice is still to use SMTP, filter mail through a service, and have SBS set up to only accept incoming mail from the service.) There’s no support for global mailboxes and the minimum check time has been reduced from 15 minutes to 5.

Storage Management is where things get really interesting:

  • NTBACKUP has been replaced with a new Windows Server Backup that was rebuilt from the ground up. The highlights:
    • Simplified management
    • Enhanced performance to allow multiple daily and incremental backups (more restore points.)
    • System Recovery functionality: can perform a bare metal restore to similar or different hardware in approximately 30 minutes.
    • Can recover data from SharePoint, Exchange and SQL (at least that’s how I interpret "Application Data Recovery" from the PowerPoint deck.)
    • System State Backup and Recovery (from the command line.)
    • Supports rotational scheme for backups between muliple disks.
    • Does not support tape.
    • Backs up to external USB or Firewire devices. The slide deck mentions using E-SATA drives but doesn’t say directly whether they are supported or not.) The target drive can only be used for backup and will be automatically formatted on first use and will not have an assigned drive letter.
  • There is a new "Move Data Folders" wizard that replaces these instructions with a wizard (if you are performing a SBS03 to SBS08 migration the recommendation is to move the data folders before migrating the data.) The tasks are transactional so if the transfer fails the data resets to its original configuration.

Next post I’ll talk a little about Essential Business Server for the mid-market.

Microsoft Office Outlook Team Blog : A Safety Net for the Send Button

April 14th, 2008

 

Microsoft Office Outlook Team Blog : A Safety Net for the Send Button

Works on Outlook 2003 as well.