October 22, 2009

September 15, 2009

Transactions and .NETs TransactionScope, what and why?

Transactions are used to add the Rollback functionality to calls, Rollback meaning the if an exception or something is thrown your call will not be executed (Commited is the term used for this).
Another benefit of using Transactions is that you can group calls together, if somethign happens to one call then all of calls in that group can also be rolled back. This type of feature is particularly useful when taking user input and saving to DB, you may not want anything to get written to the DB is some error occurs on the client.

.NET provides us with the System.Transaction assembly, it's not completly straight forward to use but it does hide alot of the underlying complexities.
A couple of things I've learned about using System.Transactions or more specifically the System.Transactions.TransactionScope class:
  • The safest way to use it is with the using clause.
  • Always call Commit() before the end of the using.
  • The optional TransactionScopeOptions enum parameter in the constructor defaults to TransactionScopeOption.Required meaning this new Transaction will join an existing one, the existing one is one that has already been created and is know as the "Ambiant Transaction".

TransactionOptions options = new TransactionOptions();
options.IsolationLevel = IsolationLevel.ReadCommitted;
options.Timeout = TimeSpan.FromMinutes(1);

using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required, options))
{
//do something
scope.Complete();
}

The different States of the Transaction
If the Transaction is not Completed it will rollback in code this means if you do not explicitly call TransactionScope.Complete() on the Transaction it will rollback.

So if you detect something wrong has happened in your code then do not call Complete() on it. You must dispose of the Transaction too, if you have your instance wrapped in a Using statement then this is done for you, else you can explicitly call it with the Dispose() method.

If Commit() has not been called before Dispose() then the Transaction goes into a state of Aborted. This indicates something has gone wrong inside the Transaction as Dispose() was called without a Complete().
You may not call Complete() after this, else you'll get a TransactionAbortedException.
So the rule is always either explicitly call Dispose() or let the Using block take care of it and if your code has not thrown an error then call Complete() before Dispose() is called else the functionality inside the Transaction will be rolled back.



What can be put inside a TransactionScope for commital/rollback?
You can put any code you like inside the using of the TransactionScope but only this object that support Transactions will be Rolledback or committed. Objects that support Transactions are usually objects that inherit from System.Transaction e.g.
System.Data.Common.DbConnection.

The official wording from MS indicates that the object must be be able to support transaction promotion i.e. in .NET 2.0 there is something known as the the Lightweight Transaction Manager (LTM) which runs the transactions, it only manages objects that support transaction promotion, I think this actually means the objects implement a set of interfaces or inherit form System.Transaction.Transaction.

from msdn:
"Developers get access to this capability through new classes and interfaces within the System.Transactions namespace. For example, to explicitly start a transaction, an application can instantiate a new TransactionScope. If the application code for that transaction runs inside a single app domain, and if it involves at most a single durable resource, and if that resource supports transaction promotion, then the LTM can coordinate the transaction. For a transaction that involves application code that runs in multiple app domains (including multi-process and multi-machine scenarios), or for any transaction that involves more than one durable resource, even when all application code resides in a single app domain, or when a single transactional resource is involved but the resource does not support transaction promotion, the distributed (OleTx) transaction manager will be used. The application code itself need not concern itself with these optimizations—they just work."

What manages the Transactions?
The Lightweight Transaction Manager (LTM) manages the transactions.



September 03, 2009

Loading Assemblies, Assembly.Load, Assembly.LoadFrom, Assembly.LoadFile, appDomain.Load

I always find this topic a great source of confusion each time I get back to it.
I've just across various Exceptions being thrown in my application, FileNotFoundException and AccessViolationException.
The first caused because the application cannot location a Type that one of my assemblies references and lives in another assembly.
The second is caused by that same file being found but it's locked by another process i.e. something else is reading or has read and not released the assembly.
Both of the above were caused by messy code which used a simple Assembly.LoadFile(...). Looks to me like you should never use this method, I'll explain more below.

There are many ways to create an Assembly instance.
Assembly.Load(assemblyName);
Assembly.LoadFile(assemblyName);
Assembly.LoadFrom(assemblyName);

or even
AppDomain.Load(assemblyName);

There are some subtelties however, some consequences which may later cause issues e.g. the assembly you've loaded is locked.

Assembly.LoadFile is the simplest but most crude solution, it simple reads an assembly, the assembly should be standalone, no references to Types other than the .NET. The file may have issues later too as it maybe locked until the application is shut down.

Assembly.LoadFrom is a bit more intelligent, it uses the current AppDomains settings, if there's an issue with references then the current AppDomain will try to find the Types using the it's settings.


The most complete solution but not the easiest to debug is the use of the AppDomain.Load. This in my experience is tricky until you get used to what all the properties on the AppDomain mean.
What you do is setup a new AppDomain, until you unload that AppDomain everthing that happens in the meantime is in the context of your new AppDomain's settings.
This will help you resolve references to your assembly in code, you can configure where the AppDomain should look when the Load fails to locate an Type.



August 27, 2009

Installer CustomAction, Debugging the CustomAction, InstallState

Custom Action

The Custom Action is added to the Setup Project, select the Project node and hit the Custom Action button. This allows you add an Action to a particular phase in the Installation. But first you must create the Custom Action.

To Add a Custom Action you must first have a Custom Action created, this is usually in the form of a Installer Class, this should be created in a seperate project, the Installer Class is actually one of the File Templates in the C# Projects. So it's File->New Project and select Visual C# Projects. Then add a Class Library, this will prompt you for the Class Library Types , select "Installer Class".

Walkthrough - Creating Custom Action (msdn).

Also here's a more comprehensive document on Setup/Installer implementations, it delves into the Registry etc
Getting Started with Setup Projects (SimpleTalk).

Visual Studio Setup Projects and Custom Actions (Simple Talk).

Create your Installer Class and then add it as a Custom Action to the Setup Project.

What happens now is that you can access the Installers data using what are called Installer Properties. How this is done is a bit messy, after you've added your Installer Class as a Custom Action to the Setup Project you must specify arguments to be passed to the Installer Class at runtime, apparantly this is the only way to references installer data after the installation has completed.

Here's an example of how to pass the name of the installation directory to the Custom Action assembly:

This is done in your Setup Project's Custom Action Tab.

Set the property of the Custom Action you have just added to

        /InstallLoc="[TARGETDIR]\"

NOTE: the extra \" at the end is required to return a directory.

This tells the Custom Action dll that it can access the TARGETDIR (which is the installation location) with the InstallLoc arg, here's how it's done in the Custom Action class:

            this.Context.Parameters["InstallLoc"];

see CustomEventInstaller.Installer

private void AfterInstallEventHandler(object sender, InstallEventArgs e)
{
//Copy the assemblies to the location _installationLoc
//From the command line (passed in by the Setup)
//_installationLoc is also used in the generated .targets file
//InstallLoc="[TARGETDIR]""
_installationLoc = this.Context.Parameters["InstallLoc"];


For multiple Custom Action parameters use a single space between each parameter.



/appsetting1=[EDITA1] /appsetting2=[EDITA2] /installLoc="[TARGETDIR]\"

accessible in your Custom Action code with

_installLoc = Context.Parameters["installLoc"];
_appsetting1 = Context.Parameters["appsetting1"];//first edit
_appsetting2 = Context.Parameters["appsetting2"];//second edit


For Common Properties used in installer applications and other like TARGETDIR see
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/property_reference.asp

http://msdn2.microsoft.com/en-us/library/9cdb5eda.aspx



Debugging the CustomAction

Debugging the CustomAction can be tricky.

To attach the Visual Studio Debugger to the installation process, msiexec.exe is nigh on impossible. The best solution is to add a

Debugger.Break();

or a

System.Windows.Forms.MessageBox.Show(............);

The Debugger.Break() will force the runtime to prompt the installer to attach a Debugger this is your opportunity. Attach to a Visual Studio session that has the CustomAction project source code open and debug as normal once attached.

You may however find that the Debugger.Break() or other code you've just added to your CustomAction does not get picked up even though you've rebuilt, this has caught me out a few times, usually when I'm trying to debug the UnInstall. The usual reason is that the code the installer is using as the CustomAction is not the one you've just built. To resolve this drop the newly built assembly into the Target installation location, e.g. if your CustomAction is built to a dll named MyCustomAction.dll and you've just installed to C:\Program Files\MyCompany\MyApp then drop the newly built dll and it's pdb into here and then try to uninstall. The uninstall picks up the CustomAction from the target location (this is also why you see you CustomAction's assembly in the target installation directory after install).


InstallState, stateSaver and savedState IDictionary

In your CustomAction code you'll see an IDictionary being passed around the Install and UnInstall overrides. This IDictionary is a HashTable. Because the Install and UnInstall are 2 completely different instances the UnInstall knows nothing of member variables set in the Install, instead it relies on an IDictionary, this IDictionary uses a text file to store it's data, thsi is how the Install and the UnInstall can comunicate i.e. the actual have no communication but the Install writes to a file and later the Uninstall reads from that same file. The IDictionary contains paths to the components that the CustomAction has installed (these also get written to a file in the Target installation location which has an extension ".InstallState").

At install time the IDictionary, "stateSaver", records the component names and their paths that were installed, this gets serialized in the file ".InstallState". At UnInstall time, this .InstallState file is deserialized into the IDictionary, "stateSaved", which the Uninstall override uses.

public override void Uninstall(IDictionary savedState)

{

base.Uninstall(savedState);

}

With the call to baseUninstall(stateSaved) it can uninstall all that was installed.

In the Install override you're free to add content yourself, the key can be any string really, remember the key must be unique, the value however should be a path to a file or the full path to a registry entry. The base.Uninstall(savedState) goes through each of these entries and attempts to remove them. The IDictionary can also be used to store values you'd like to explicitly uninstall yourself, remember you cannot depend on member variables you must have some persitant storage to store variables and that is this IDictionary.

Here's an example of a registry entry which was created by the CustomAction on Install and so must be deleted by the CustomAction on UnInstall

public override void Install(IDictionary stateSaver)

{

RegistryKey expressionEvaluatorPackageVersionKey = expressionEvaluatorInProcKey.CreateSubKey(packageVersion);

add the entry to the IDictionary with unique name for the Key and full registry path for the Value

stateSaver.Add("expressionEvaluatorPackageVersionKey" + guidString + packageVersion, expressionEvaluatorInProcKey.OpenSubKey(packageVersion).ToString());

}

At UnInstall pull the entry from the IDictionary and Explictly uninstall

It's unusual to have to explicitly do this, this should be done by the call to base.Uninstall(savedState).

public override void Uninstall(IDictionary savedState)

{

RegistryKey hKeyClassesRootCLSID = Registry.ClassesRoot.OpenSubKey("CLSID", true) as RegistryKey;

foreach (object key in savedState.Keys)

{

if (key.ToString().Contains("expressionEvaluatorPackageVersionKey"))

{

if (savedState[key] != null)

{

int index = savedState[key].ToString().IndexOf(@"\");

string keyStringWithoutHKEY_CLASSES_ROOT = savedState[key].ToString().Substring(index + 1);

RegistryKey expressionEvaluatorPackageVersionKey = Registry.ClassesRoot.OpenSubKey(keyStringWithoutHKEY_CLASSES_ROOT, true) as RegistryKey;

if(Registry.ClassesRoot.OpenSubKey(keyStringWithoutHKEY_CLASSES_ROOT, true) != null) Registry.ClassesRoot.DeleteSubKeyTree(keyStringWithoutHKEY_CLASSES_ROOT);

}


August 10, 2009

TransactionScope, Transactions in .NET, A very basic level introduction

Transactions are used to prevent your application from getting into an unknown state.
Transactions work on services which register with the Transaction service such as Database calls.
It's useful where you cannot be sure that the resource (DB) will remain available while some data is changing and if the resource for some reason is made unavailable during a call then what happens to the data, well the Transaction should Rollback your Data so the app remains in the state it was before the data was sent.

Transactions in .NET are implemented with System.Transaction.TransactionScope class. In the background this uses COM+.

Wrap your DB call in a new TransactionScope instance, when the functionality is finished call TransactionScope.Complete(), then TransactionScope.Dispose(). If the execution makes it to these calls then your changes will be applied, if not your changes will be Rolled-back by the Transaction and you app will remain the same as before the call started.


MSDN Video

July 31, 2009

Windows Installer Tips, problem installations


With installer issues it's always difficult debug problems. If it's the Installer itself i.e. work done in the setup.vdprj itself then you maybe in a spot of bother. There are some logging options which are available to you.The other problem you may have is with your Custom Action if you have one. This maybe easier to diagnose, asyou can use the Visual Studio Debugger to attach but this in itself is not always easy todo.
If you have problems with your installer itself and not the Custom Action then try this:You can also Log installer actions by activating the installer logging, this is done through a registry entry, http://support.microsoft.com/kb/223300. All your doing is adding a "String Value" named "Logging" and a value "voicewarmupx" to the registry entry HKEY_LOCAL_MACHINE\Software\Policies\Microsoft\Windows\Installer
I've done this and found some useful info but it's not that easy to read, at least it tells you the command that was ran and where it got the installer from.
If you have problems with your Custom Action then try this:
In your Custom Action's code add a Debugger.Break() or a MessageBox.Show() , build your projet in Debug, launch your msi and when the messag appears you can then attach to msiexec.exe.
I've found that the Debugger.Break does not always work, the MessageBox.Show should always work, just gives you a chance to attach your debugger.
Common problems with the Custom Action
Common problems with the Custom Action are the use of the SavedState IDictionary, trying to use entries that do not exist. I recommend wrapping everthing in a try/catch and then rethrow everthing in a new InstallerException, I usually add one for each method in my Custom Action.
try
{
}
catch(Exception ex)
{
throw new InstallerException("There's a problem with the installation", ex);
}

Also check for nulls everywhere, unit tests are the best way to find these.

Cannot uninstall
Another problem I've found with the Custom Action is if your installer has a bug that doesn't allow you to uninstall then your in a bind, how can you fix it if you cannot uninstall it. The solution is usually to find the bug by attaching the debugger and then fix it, but you still cannot use it, yes you can but you must copy the fixed Custom Actions dll into the installation location. The uninstall actually calls the dll and not just the msi so by updating it with the fixed on you can fix your uninstall.

To Force an uninstall
try this
MsiExec.exe /I installer.msi REINSTALLMODE=voums REINSTALL=ALL
installer.msi is the name of your installer.
The Repair/Remove dialog will appear and you can then Remove.Got this info from http://labnol.blogspot.com/2007/06/windows-installer-issue-when-programs.html
There's also this Windows Registry Cleanup tool, I don't like this however as it cleans the registry making your application disappear from the users point of view but the files etc still remain, it's not really a solution if your about to reinstall.

 


April 09, 2009

ProjectAggregator

The ProjectAggregator is a dll that is required in order to use Packages developed with Visual Studio's 2005 Extensibility SDK.

The projectaggregator.dll must be dropped into the C:\Program Files\Microsoft Visual Studio 8\Common7\IDE directory, if it doesn't exist then the end user will not be able to open projects in Visual Studio 2005 that are associated with your Package.

Microsoft provide an installer ProjectAggregator.msi which does this work for you.

The problem with this is you either tell all your clients to run the projectaggregator.msi themselves after or before installing your Package or else you include the installation of it in your own installer.

The second option is of course the right one, as who would expect customers to run more than one installer. To include this in your installer however you need to create what is known as a BootStrapper project. The Bootstrapper is another installer that wraps the installer you actually wish to run. The idea is you include the Prequisites in the BootStrapper, they get installed first and then the main installer runs.
The Bootstrapper is created in the form of a setup.exe and has it's own installation dialogs. My recommendation is to use this as infrequently as possible. The scenario to include them in your projects is fraught with danger.