July, 2010 Archives

6
Jul

Merge assemblies with ILMerge

by Mikael Lundin in Programming

ILMerge is a tool for merging assemblies together, which is very useful for easy deployments. Instead of deploying 10 dlls you can deploy one. When distributing applications out to customers you really want to make the application as simple as possible, and the most simple application is one that consist of only one exe.

ILMerge in .NET 4

I had some problems using ILMerge with .NET 4 dlls, but managed to get around my problems with some extra parameters. Here’s how you merge dlls into a single DLL using ILMerge.

ilmerge.exe
	/lib:"C:\Windows\Microsoft.NET\Framework\v4.0.30319"
	/lib:"C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\PublicAssemblies"
	/t:dll
	/targetplatform:v4,C:\Windows\Microsoft.NET\Framework\v4.0.30319
	/out:MyApplication.merged.dll
	MyApplication.dll
	ReferenceAssembly1.dll ReferenceAssembly2.dll ReferenceAssembly3.dll

It’s easy to merge dlls into an exe, just by changing target type. Use your original exe as the first argument after option parameters.

ilmerge.exe
	/lib:"C:\Windows\Microsoft.NET\Framework\v4.0.30319"
	/lib:"C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\PublicAssemblies"
	/t:exe
	/targetplatform:v4,C:\Windows\Microsoft.NET\Framework\v4.0.30319
	/out:MyApplication.merged.exe
	MyApplication.exe
	ReferenceAssembly1.dll ReferenceAssembly2.dll ReferenceAssembly3.dll

In your build process

You can easily apply this in your build process.

  1. Copy ilmerge.exe to somewhere in your project path. I placed mine in a folder called External Tools.
  2. Right click your main project in Visual Studio and select Properties / Build Events.
  3. I use the following to merge assemblies for TogglChart into one dll.
    "$(SolutionDir)External Tools\ilmerge"
    	/lib:"C:\Windows\Microsoft.NET\Framework\v4.0.30319"
    	/lib:"D:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\PublicAssemblies"
    	/t:dll
    	/targetplatform:v4,C:\Windows\Microsoft.NET\Framework\v4.0.30319
    	/out:"$(TargetDir)$(SolutionName).merged.dll"
    	"$(TargetDir)TogglChart.Lib.dll"
    	"$(TargetDir)Newtonsoft.Json.dll" "$(TargetDir)ZedGraph.dll"
4
Jul

TogglChart – a Toggl charting solution

by Mikael Lundin in Programming, Technicalities

I’ve studied before how to connect to the Toggl API both here and here. Toggl is a tool for easily tracking time put in a project. This is necessary for billing customers, but also useful for tracking your own effort to a project.

I’ve written a command line tool that will extract data from Toggl API and create a chart that will show amount of hours spent per week. A chart can look like this.

All you need to do, is to call the executable with your API Token as required argument.

TogglChart.exe -apiToken=ec3223d0919b45ec2267826fc0954db0

There are some posibilities to customize the output. Here’s the full usage for this command line tool.

      --apiToken=VALUE       Your API token from Toggl. Easily find in "My
                             settings" https://www.toggl.com/user/edit
      --imageWidth=VALUE     Width of the result chart in pixels (image will
                             scale to fit width)
      --imageHeight=VALUE    Height of the result chart in pixels (image will
                             scale to fit height)
      --imageTitle=VALUE     The main title of the chart that will be shown
                             at the top.
      --outputFile=VALUE     Path and filename to the output file. Example:
                             toggle.jpg
      --imageFormat=VALUE    Filetype of the result image. Legal values are
                               jpg, png, bmp and gif
      --weeks=VALUE          Number of weeks backwards that tasks should be
                             fetched from Toggl.
      --projectName=VALUE    The name of the project in Toggl. This is useful
                             to specify when you have several different
                             projects that you register time on and only want

                             to create chart for one of them. If you don't
                             specify project name, chart will be created from

                             all projects combined.
      --dynamic              If you set this value to true, empty weeks at
                             beginning of the period will be removed.
      --help, -h, -?         Displays this message.

MsBuild Task

I’ve also included an MSBuild task. Instead of using the TogglChart.exe directly you can call the MSBuild task inside the TogglChart.dll.

<!-- TOGGLCHART -->
<UsingTask TaskName="TogglChart.MsBuild.TogglChart" AssemblyFile="$(MSBuildProjectDirectory)\ExternalTools\TogglChart.dll" />

<Target Name="TogglChart">
	<TogglChart
		ApiToken="ec3223d0919b45ec2259296fc0954db0"
		ImageWidth="320"
		ImageHeight="240"
		ImageTitle="Time spent on Hippo"
		OutputFile="$(ArtifactDirectory)\togglchart.jpg"
		ImageFormat="jpg"
		Weeks="10"
		ProjectName="Hippo"
		Dynamic="true" />
</Target>

Both TogglChart.exe and TogglChart.dll are stand-alone. You do not need the DLL for the EXE to run, nor the other way around.

Download

License: You are free to use and distribute this application.

2
Jul

Introduction to NHibernate

by Mikael Lundin in Programming

I held this introduction to NHibernate yesterday and thought it might be nice to share this with you. It is just a basic NHibernate application without any fancy schmancy, to familiarize yourself with the concepts.

Download and install the Northwind database

This example builds upon the Northwind database. Please download and install it from here. Once installed you need to find the database install SQL script that should be located in C:\SQL Server 2000 Sample Databases\instnwnd.sql and run it on a database that is available to you.

Create a domain model

Next thing you create a new Visual Studio console project/solution. This will make it easy for you to run and debug your mappings later.

As you can see I’ve created a namespace for our OR-mappings and the domain model. Please download NHibernate and reference it to your project. I’ve used version 2.1.2.GA in my solution. Don’t forget to include binaries for lazy loading. I’m using Castle in this example.

We keep the domain simple for this example. Here’s what the code looks like for these two domain classes. An important aspect here is to keep the properties virtual.

namespace NHibernateExample.Model
{
    public class Product
    {
        public Product()
        {
        }

        public Product(string name, double price)
        {
            Name = name;
            Price = price;
        }

        public virtual int ID { get; set; }

        public virtual string Name { get; set; }

        public virtual double Price { get; set; }

        public virtual Category Category { get; set; }
    }

    public class Category
    {
        public virtual int ID { get; set; }

        public virtual string Name { get; set; }

        public virtual string Description { get; set; }
    }
}

The object relational mapping

You map the database tables to your entity object by writing hbm.xml-mapping files. There are nicer ways to do the mapping through code, but I will go through the most common way of mappings here instead.

<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="NHibernateExample" namespace="NHibernateExample.Model">
  <class name="NHibernateExample.Model.Product, NHibernateExample" table="Products">
    <id name="ID" column="ProductID">
      <generator class="identity" />
    </id>

    <property name="Name" column="ProductName" />
    <property name="Price" column="UnitPrice" />
    <many-to-one name="Category" class="NHibernateExample.Model.Category, NHibernateExample" column="CategoryID" />
  </class>
</hibernate-mapping>

Notice that the ID is specified to be generated as “identity”. This means that ID is an identity column in the table, and that the SQL server will generate the value for us on insert.

You can also see how Product is related to Category with a many-to-one relationship. We specify the foreign key column name in the column attribute.

<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="NHibernateExample" namespace="NHibernateExample.Model">
  <class name="NHibernateExample.Model.Category, NHibernateExample" table="Categories">
    <id name="ID" column="CategoryID">
      <generator class="identity" />
    </id>

    <property name="Name" column="CategoryName" />
    <property name="Description" />
  </class>
</hibernate-mapping>

Description does not need to specify column name, because it is the same as property name. This is one of the defaults of NHibernate.

You’ll have to mark the hbm.xml-files as Embedded Resources for NHibernate to find them.

NHibernate configuration

The configuration for NHibernate specifies what database provider to use, SQL dialect and connection string to the database. If you want NHibernate to be verbose about it’s SQL you can set show_sql to true.

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-configuration xmlns="urn:nhibernate-configuration-2.2">
  <session-factory>
    <property name="connection.provider">NHibernate.Connection.DriverConnectionProvider</property>
    <property name="dialect">NHibernate.Dialect.MsSql2000Dialect</property>
    <property name="connection.driver_class">NHibernate.Driver.SqlClientDriver</property>
    <property name="connection.connection_string">Data Source=(local);Initial Catalog=Northwind;Integrated Security=SSPI</property>
    <property name='proxyfactory.factory_class'>NHibernate.ByteCode.Castle.ProxyFactoryFactory, NHibernate.ByteCode.Castle</property>
    <property name="show_sql">true</property>
  </session-factory>
</hibernate-configuration>

This xml should be placed in a hibernate.cfg.xml that should be placed in the root of your project. Make sure to mark it as Copy to Output Directory: Copy Always. This file must be present in the output bin directory, unless you choose to specify the path while building the SessionFactory.

Session Management

If you’re writing a web application, you might want to consider using one session for each and every request. You can read more about how to accomplish that here and here. In this simple example we will open one session for every database call. That should not be a problem since ISession is considered to be a lightweight object, in contrary to ISessionFactory that should be built only once per application.

public class SessionManager
{
	private ISessionFactory globalSessionFactory;

	private static readonly object PadLock = new object();
	private static SessionManager instance;

	public static SessionManager Current
	{
		get
		{
			lock (PadLock)
			{
				return instance ?? (instance = new SessionManager());
			}
		}
	}

	public ISession OpenSession()
	{
		if (globalSessionFactory == null)
		{
			globalSessionFactory = CreateSessionFactory();
		}

		return globalSessionFactory.OpenSession();
	}

	private ISessionFactory CreateSessionFactory()
	{
		return new Configuration()
			.Configure()
			.AddAssembly(typeof(SessionManager).Assembly)
			.BuildSessionFactory();
	}

	private SessionManager()
	{
	}
}

In CreateSessionFactory we do Configure() to load the hibernate.cfg.xml file, and AddAssembly will search for and load our Product.hbm.xml and Category.hbm.xml mapping files.

RepositoryBase pattern

Now when we easily can get an instance of ISession from our SessionManager, we can figure out how to do simple CRUD operations on our entities. For this we create a base class for our ProductRepository and CategoryRepository to derive from later.

public abstract class RepositoryBase<TEntity>
	where TEntity : class
{
	protected SessionManager SessionManager;

	public RepositoryBase(SessionManager sessionManager)
	{
		SessionManager = sessionManager;
	}

	public TEntity GetById(object id)
	{
		using (var session = SessionManager.OpenSession())
		{
			return session.Get<TEntity>(id);
		}
	}

	public void Insert(TEntity TEntity)
	{
		using (var session = SessionManager.OpenSession())
		using (var transaction = session.BeginTransaction())
		{
			session.Save(TEntity);
			transaction.Commit();
		}
	}

	public void Update(TEntity TEntity)
	{
		using (var session = SessionManager.OpenSession())
		using (var transaction = session.BeginTransaction())
		{
			session.Update(TEntity);
			transaction.Commit();
		}
	}

	public void Delete(TEntity TEntity)
	{
		using (var session = SessionManager.OpenSession())
		using (var transaction = session.BeginTransaction())
		{
			session.Delete(TEntity);
			transaction.Commit();
		}
	}
}

We need transactions for all operations that changes the datasource. That’s because we can’t know if our Insert operation will have to insert data into more than one table, and if it conflicts somewhere along the way we would like the operation to be atomic, and rollback to the state before we started our insert.

Here’s how we implement ProductRepository and CategoryRepository now.

public class ProductRepository : RepositoryBase<Product>
{
	public ProductRepository(SessionManager sessionManager)
		: base(sessionManager)
	{
	}

	 public IEnumerable<Product> GetByCategory(Category category)
		{
			using (var session = sessionManager.OpenSession())
			{
				return session.CreateQuery("FROM Product as product WHERE product.Category = :category")
					.SetEntity("category", category)
					.List<Product>();

			}
		}
}

public class CategoryRepository : RepositoryBase<Category>
{
	public CategoryRepository(SessionManager sessionManager)
		: base(sessionManager)
	{
	}
}

I’ve also extended the ProductRepository with a database call to get all Products within a specfied category. A query that is specific for the Product entity and not part of the base repository.

An example application

Here’s an example of how we can use the framework.

public class Program
{
	public static void Main(string[] args)
	{
		var beverages = new CategoryRepository(SessionManager.Current).GetById(1);
		var product = new Product("Juice", 12d) { Category = beverages };

		var repository = new ProductRepository(SessionManager.Current);

		repository.Insert(product);
		product.Price = 15d;

		repository.Update(product);
		repository.Delete(product);

		foreach (var beverage in repository.GetByCategory(beverages))
		{
			Console.WriteLine("{0}, {1} {2:c}", beverage.ID, beverage.Name, beverage.Price);
		}

		Console.ReadLine();
	}
}

The whole example can be downloaded from here. (Visual Studio 2008 solution)