Custom Actions on Vista with visual studio.net 2003

One handy feature of Visual Studio.net 2003 Deployment projects is support for Custom Actions. These can be either executables or libraries that can be invoked during an install (or uninstall).

There are several predefined Installation components, derived from System.Configuration.Install.Installer, for handling things like installing services. You can implement custom actions as Installation Components (by deriving from Installer, specifying a few attributes, etc...) to get built-in support from both the VS.net 2003 Deployment project and the .NET SDK installutil.exe program.

The problem I ran into on Vista was that the install was being cancelled by Vista's Data Execution Prevention (DEP). Changing from a dll custom action to an EXE still resulted in the execution being cancelled (though, not by DEP). It turns out that there is a bug in the way VS.Net 2003 deployment projects construct the MSI install package.

To fix this, an application manifest needs to be embedded in the custom action executable. This manifest specifies a requiredPrivileges token (we're using "asInvoker") that is needed for the custom action executable to run during an install. Unfortunately, on VS.Net 2003, I haven't found an easy way to embed the application manifest in the custom action executable. One way of embedding this manifest in the executable is to open the executable in visual studio and manually import the manifest. An example, with steps, follows.

The manifest itself is taken from the "Vista Developer Story" (step 6).

To embed this manifest in the executable:

  1. Build the executable

  2. In visual studio use File - Open to open up the executable.

  3. Right click the executable and choose "Add Resource"

  4. Choose "Import"

  5. enter RT_MANIFEST then click ok

  6. Change the ID property to 1

  7. Save the executable.

  8. Leave the executable open so that the deployment project can't overwrite the executable.

This procedure will need to be done each time to update the embedded resource

Musings on transitioning from Unix to Windows

From 1996 until 2002 I primarily worked in Unix environments.  Mostly Solaris but also AIX and Linux.  There are a lot of things to be said about the Unix world; great text utilities (grep, PERL, sed, awk, etc..), a very powerful command line environment (bash, ksh).  When Java came along things started to improve on the UI front; NetBeans (now eclipse) was a world of improvement over e-macs or vi/vim.

 

One oddity was that even when the production/deployment environment was a flavor of Unix, almost all of the development was done on Windows.  One company standardized on what was then called Visual Age for Java (from IBM).  Visual Age was an amazing UI - loaded with tons of productivity enhancing features, a first class object browser, a first class forms designer, etc...  It even had rudimentary support for drag and drop programming via graphical components (JavaBeans) that represented basic programming constructs (loops, conditionals).

 

After finishing grad school (2006) I made a conscious decision to try to find employment in a Windows development shop.  Most of my work during grad school was done in C++ using Visual Studio and I had grown to like the level of integration I found in Visual Studio.  C# was gaining greater acceptance and, coming from Java, I was naturally attracted to it.  Over the years, a few things have stood out to me regarding Unix vs Windows development.


  1. Visual Studio is one of the best IDEs around.
  2. The database tools in Visual Studio make it possible to do round trip development entirely within the IDE.  Stored procedure debugger, Server Explorer, Database Design view all greatly reduce the amount of work involved in building database-centric apps.
  3. Documentation in the Microsoft world is incredibly robust.  Beyond syntax the documentation often includes tons of sample code and examples.  Sometimes a short code sample is much better at illustrating a simple concept than pages of well-formed BNF.  Don't get me wrong, syntax is important but I've come to appreciate the additional code that I find, in one place, in MSDN.

 

More about datasets

The convenience of being able to visually design the queries and DML statements, while wonderful, is not without its cost.  So far what I'm discovering is:


  • Datasets introduce another state layer between the data model and the database. 

    • They must therefore be initialized.
    • Concurrent access must be serialized/synchronized to prevent corruption.
    • Rolled back transactions need to be accompanied by RejectChanges().

  • Since a DataAdapter is usually necessary to manage each table/query, changing the table structure means changing/updating the corresponding DataAdapter.

    • The "Configure DataAdapter wizard" makes this pretty easy.

It would be nice to have the benefits of the visual query designer w/o having to create a DataSet... Dragging the SqlCommand onto the component/form surface perhaps?

Why strongly typed datasets are cool

Coming from a Java/JDBC background, I was reluctant to use strongly typed datasets.  For one, as far as I know, there wasn't anything comparably generic with the same level of IDE support.  This was around 2000-2001 so things have probably changed in the Java world.

 

Anyway, beyond being an unfamiliar piece of IDE functionality, I was concerned about memory and computational overhead.  Retrieving an entire table just to manipulate a few of its rows struck me as gratuitous.  In a web environment with large tables on the back end it wouldn't be too hard to use up all available memory in a few requests if each request filled a dataset from one of the larger tables.

 

I've been converted.  Strongly typed datasets + IDE design support are an awesome combination.  They're cool because:


  • They reduce typing and typos.  The database adapter wizard generates the select/insert/update and delete statements for you.
  • They have built in support for parameters.
  • Each of the DataAdapter commands is optional.  If you only want to insert or update, delete the other commands.
  • Strongly typed datasets take advantage of command completion (Intellisense); very handy for assigning values to rows in tables with lots of fields.
  • They simplify iterative database design.  Changing a column name, adding or dropping columns are a piece of cake; just refresh server explorer, update the XSD (via the XML schema designer), regenerate the dataset and possibly regenerate the DataAdapter and you're set.  Beyond that, the compiler will let you know of any values that no longer apply.  Without the visual database and dataset tools changes to the database design are a lot more work and error prone (e.g., forgetting to change an insert statement or parameter name).

My work flow so far is:


  1. Sketch out a rough design of the table structure on paper.  This could be done inside Database Diagrams but I'm accustomed to working on paper for this part of the design.
  2. Use server explorer to create the tables. 
  3. Generate a database diagram with the relevant tables.
  4. Create a single dataset for each related group of tables by dropping all of the tables on the Schema Designer.  Save to generate the dataset (make sure generate dataset is checked).

    1. I usually manually create the DataRelations but only if I'm going to find it useful to traverse the tables via GetParent() or GetChildRows()...

  5. Create a separate data adapter for each table within the dataset.  Only leave "refresh dataset" option for tables that have an autonumber/identity column.

    1. I also uncheck "use optimistic concurrency" because I think concurrency should be handled at the app level but this is optional.

  6. Use the strongly typed NewRow() and AddNewRow() methods to create the rows.
  7. Fill() and Update() as needed.  Be sure to use the right data adapter to update the dataset.

 

When RDBMS theory hits practice

There's a project I'm working on that uses MSDE to store its data. I inherited a schema that was totally non-relational; pretty much all of the data model objects are stored in binary serialized IMAGE fields. While this probably made it much easier, initially, to save data it rules out any meaningful querying via SQL.



So I've finally gotten the go ahead to "refactor" this monstrosity. With notions of Relations, Entities (both strong and weak) and Joins in mind I merrily produce a schema that's totally normalized, heavily query-able and makes for quite pretty ER diagrams.



What I neglected to consider was that part of the model relied on data in another part of the model but needed to survive mutations/deletions to that data. Well, I didn't fully consider it. A first cut at the schema wasn't foreign keyed to the data in the other part of the model but, as my boss pointed out, that wasn't sufficient. What good is an ID that refers to a row that no longer exists (even if the deletion didn't cascade)? It's a dangling reference. What I needed was to duplicate some of the data from the other part of the model.



I think the general principle here is that duplication of data can be necessary if some part of the model needs that data to survive mutations/deletions in the source. Probably applies to logging (where strings usually handle the duplication) but also to any derived data objects that need to exist apart from their source model components.