Showing posts with label Software Development. Show all posts
Showing posts with label Software Development. Show all posts

Monday, January 17, 2011

Uninstalling Web Applications using WiX

I have recently been playing around with windows installers at work for our projects. I have never before had much success with this as I never put much effort into learning about creating installation files. So when a colleague turned up a Visual Studio project type called WiX i had a look.

Now using WiX is much the same as using MSBuild as it is all written in XML. I have never really liked XML based scripting as it is very verbose but WiX seems ok.

At work I we create installers for all of our web applications and have always had the problem of not being able to specify a custom name for the app in the installer as we were not able to uninstall it. So I reciently set about finding a solution.

After a few hours of googling I found this post on the WiX message board. The post was basically states that you store the custom name in a registry entry and call it back on uninstall. The example was a little bit unformatted for my liking but I managed to get the general idea of it.

After a bit of hacking I came up with this solution:


<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi"
xmlns:iis="http://schemas.microsoft.com/wix/IIsExtension">
<Product Id="new guid here" Name="Product Name" >

<Property id="WEBAPPNAME">
<RegistrySearch id="WebAppName" root="HKLM" key="Software\[Manufacturer]\[ProductName]" name="WebAppName" type="raw"></RegistrySearch>
</Property>
<SetProperty id="WEBAPPNAME" value="WebAppName" after="AppSearch">WEBAPPNAME=""</SetProperty>

... Any extra properties and conditions here ...

<Media id="1" cabinet="media1.cab" embedcab="yes"></Media>
<Directory id="TARGETDIR" name="SourceDir">
<Directory id="ProgramFilesFolder">
<Directory id="INSTALLLOCATION" name="[ProductName]">
.... Add Files using components here ...

<Component id="WebAppNameComponent" guid="new guid here">
<RegistryValue id="WebAppName" root="HKLM" key="Software\[Manufacturer]\[ProductName]" name="WebAppName" type="string" value="[WEBAPPNAME]"></RegistryValue>
</Component>
<Component id="WebAppSetupIIS" guid="new guild here" keypath="yes">
<iis:WebVirtualDir id="WebApplicationDirectory" alias="[WEBAPPNAME]" Directory="[ProductName]" website="DefaultWebSite">
<iis:WebApplication id="WebApplication" name="[WEBAPPNAME]">
... IIS Settings here ...
</iis:WebApplication>
</iis:WebVirtualDir>
</Component>
</Directory>
</Directory>
</Directory>

<iis:WebSite id="DefaultWebSite" description="Default Web Site">
<iis:WebAddress id="AllUnassigned" port="80"></iis:WebAddress>
</iis:WebSite>

<Feature id="Complete">
<ComponentRef id="WebAppSetupIIS"></ComponentRef>
<ComponentRef id="WebAppNameComponent"></ComponentRef>
</Feature>

... Any UI stuff here ...
</Product>
</Wix>


So Basically I start a new WiX file and add a Product as you normally would in WiX.

I added a new Property called WEBAPPNAME (All in caps for some reason in WiX I have yet to research). And in this Property I do a RegistrySearch (search in the Windows Registry) for a key that is defaulted to the product name with a value for the WebAppName.

And since this has not been set in the initial installation of this product I perform a SetProperty and give it a default value. However this will overwrite any value that may exist so I put a condition on it that specifies that WEBAPPNAME has no value. And tell it to execute after an event called AppSearch which is when the registry is searched.

I then set up the directory structure of the install and add some Components to the directory.

The first Component is the WebAppNameComponent and this creates a value in the registry to the value of WEBAPPNAME. This registry value will also be removed on uninstall but exists solely to provide the name of the application at uninstall time.

The next Component is the WebAppSetupIIS component and this creates the entry in IIS providing the current directory as a virtual directory in IIS and sets the name of the virtual directory to the value of WEBAPPNAME.

Next I specified the WebSite that the web application would reside in and finally added the two components above into the feature by using the ComponentRef tags.

The rest of the file was just setting up the user interface and other things.

The final thing worth mentioning is that I added an entry in the UI to be able to change the name of the Virtual Directory in the installer.

All I needed to do was add these lines of code to the Dialog file in my project:


<Control Id="WebAppNameLabel" Type="Text" Text="Web Application Name:" />
<Control Id="WebAppNameEdit" Type="Edit" Text="[WEBAPPNAME]" Property="WEBAPPNAME" />

NOTE: Positioning and Size information removed for brevity from these tags

Well that is about it for uninstalling web applications with custom names.

Cheers.

Tuesday, March 17, 2009

Layer Transparent Paging

I am currently working on an issue where I am processing large amounts of data. So I have decided to implement a paging strategy.

Now my issue was that I am wanting to pass this paging strategy from my Data Access Layer (DAL) to my Buisness Logic Layer (BLL) without exposing my underlying framework which happens to be NHibernate.

So I have devised the interface:

public interface IPagedResult<T> : IEnumerator<IList<T>>
{
int PageSize { get; }
IPageSession Session { get; }
}

This I can then pass through to my BLL to iterate through the results like so:

using (var pagedResults = DAL.GetPagedResultsForSomething())
{
while (pagedResults.MoveNext())
{
foreach (var item in pagedResults.Current)
{
doSomethingWithItem(item);
}
}
}


Allowing the PagedResult object to handle all session and transaction issues.

I also devised a way to put my query into the PagedResult object using Lambdas:

public class PagedResult<T> : IPagedResult<T>
{
private int currentResult = 0;
public delegate ICriteria QueryFunction(ISession session);

public PagedResult(int pageSize, ISession session, QueryFunction query)
{ ... snip ... }

public vool MoveNext()
{
try
{
Current = query(session)
.SetFirstResult(currentResult)
.setMaxResults(PageSize)
.List<Statement<T>();

if ((Current == null) (Current.Count == 0))
{
Current = null;
return false;
}

currentResult += PageSize;
return true;
}
catch
{
session.Transaction.RollBack();
throw;
}
}

... snip ...
}


This allows me to use it as such:

var statementId = 1;
var pageSize = 10;
new PagedResult<Statement>(
pageSize,
new Session(),
s =>
s.CreateCriteria(typeof(Statement), "statement")
.SetResultTransformer(CriteriaUtil.DistinctRootEntity)
.Add(Restrictions.Eq("statement.Id", statementId))
);
What do people think?

Cheers,

Erik

Thursday, March 5, 2009

New Findings: AOP & PostSharp

I was just browsing through the latest MSDN magazine today while waiting for a rather long integration test to run, and I found an article on Aspect-Oriented Programming (AOP). Now I have heard of AOP and have been told that it is a very effective programming paradigm, but have never fully looked into it before. In the article the author (Scott Michell) presents an open source framework called PostSharp. Now I spent a good while looking through the website and some examples and I am impressed. The power with PostSharp is that you define a custom attribute which extends from one of PostSharps base classes and you can write code for cross cutting concerns such as logging, security, etc into this attribute and place the attribute on the properties in your class and the PostSharp.Core puts your code into the class at compile time.

I'm looking forward to sitting down and having a play with this framework as it looks really positive. Anything that promises to improve my coding practices is worth a bit of my time.