Friday 19 December 2008

The best IT blog of 2008

End of each year is a great moment for all sort of reviews, people tend to publish various different rankings like "The Best of ..." in all possible categories. This year I will add my 2 cents in a category which I find the most interesting: "The best IT blog of 2008".

I follow a few blogs as well as sites aggregating articles in some particular area (Agile Development, .Net, EPiServer). During this year I found lots of interesting and usefull posts there so choosing the best blog was difficult. My intention was to avoid nominating blog talking about one particular area or product. Main reason behind that decision was that people move from one project to another, from one job to another, I wanted to find a blog which can be relevant for us all the time.

Okey, let's move to the point ... in my opinion the best IT blog of 2008 is Jeff Atwood's Coding Horror!

There are a few reasons:

  1. New posts every 2-3 days
  2. Vast number of comments, Jeff's posts are just a start for a discussion.
  3. And at last but not least .. lots of great, thought-provoking posts ... my personal top 5:
Which blog is your "The best of 2008" ?

Wednesday 17 December 2008

Fluent NHibernate - Integration Tests

In last two posts I have covered the Flunet NHibernate introduction and Coneventions together with AutoPersistenceModel. In this part I would like to show how Fluent NHibernate can speed up writing integration tests which should guarantee that mappings are correct. If there is a problem with mappings then for sure that is something you would like to want know about as fast as possible therefore some sort of automated tests are necessary.

Lets check what Fluent NHibernate has to offer:

1) SessionSource class which helps to deal with NHibernate configuration, session management and recreating database schema. Why is it useful? "Just" to check if mappings are correct you don't need to have any test data, you need only clear schema and database to connect with. You can use SQLite to create in-memory database. This is a neat and robust solution and moreover it eliminates external dependency -- we don't need any external database any more. Check the example:



   1:  [SetUp]
   2:  public void SetUp()
   3:  {
   4:      // create and configure persistance model including changes in Conventions
   5:      var persistenceModel = new PersistenceModel();
   6:      persistenceModel.Conventions.GetTableName =
   7:          type =>
   8:          String.Format("[{0}.{1}]", type.Namespace.Substring(type.Namespace.LastIndexOf('.') + 1), type.Name);
   9:      persistenceModel.Conventions.GetPrimaryKeyNameFromType = type => type.Name + "ID";
  10:      persistenceModel.Conventions.GetForeignKeyNameOfParent = type => type.Name + "ID";
  11:      persistenceModel.Conventions.GetForeignKeyName = prop => prop.Name + "ID";
  12:  
  13:      // add mappings
  14:      persistenceModel.addMappingsFromAssembly(typeof (Product).Assembly);
  15:  
  16:      // configure nhibernate using SQLite database
  17:      var config = new SQLiteConfiguration()
  18:          .InMemory()
  19:          .ShowSql();
  20:  
  21:      sessionSource = new SessionSource(config.ToProperties(), persistenceModel);
  22:  
  23:      // create NHibernate session
  24:      session = sessionSource.CreateSession();
  25:  
  26:      // recreate schema
  27:      sessionSource.BuildSchema(session);
  28:  }


Here is an implementation of BuildSchema(...) method:



   1:  public void BuildSchema(ISession session)
   2:  {
   3:      IDbConnection connection = session.Connection;
   4:  
   5:      string[] drops = _configuration.GenerateDropSchemaScript(_dialect);
   6:      executeScripts(drops, connection);
   7:  
   8:      string[] scripts = _configuration.GenerateSchemaCreationScript(_dialect);
   9:      executeScripts(scripts, connection);
  10:  }


As you can see, it uses NHibernate methods to generate DDL for dropping and creating tables for selected dialect (SQLite in this case). One thing worth noticing is that DDLs will be as accurate as mappings. So if you skip some information (like nullable fields) then don't expect to see it there!

2) Thanks to the SessionSource class we can query in-memory database and we have access to NHibernate Session ... it's time to check our mappings, we can use PersistenceSpecification class to do that:



   1:  [Test]
   2:  public void ProductReviewTest()
   3:  {
   4:      new PersistenceSpecification<ProductReview>(session)
   5:          .CheckProperty(x => x.Comments, "some nice comment")
   6:          .CheckProperty(x => x.EmailAddress, "test@test.com")
   7:          .CheckProperty(x => x.ModifiedDate, DateTime.Today)
   8:          .CheckProperty(x => x.Rating, 4)
   9:          .CheckProperty(x => x.ReviewDate, DateTime.Today)
  10:          .CheckProperty(x => x.ReviewDate, DateTime.Today)
  11:          .CheckProperty(x => x.ReviewerName, "test name")
  12:          .CheckReference(x => x.Product, CreateNewProduct())
  13:          .VerifyTheMappings();
  14:  }

Under the hood PersistenceSpecification class will save the object (ProductReview) to the database and then using another connection it will fetch the object back to make sure that all properties have correct values set. It's not a revolution but for sure it can save lots of time and thanks to neat and readable code it will increase maintainability.

Source Code

As always you can download the source code, and the whole sample web application. You will find there separate project for tests. I have created an AbstractTestBase class which is responsible for configuration and it also exposes NHibernate session. There are also tests for mappings, which use exposed NHibernate session and at least on my machine all tests pass ;)


(EDIT: Examples in this post have been updated on 8.02.2009 to reflect changes in Fluent NHibernate API)

Useful Links:
Other related posts:

Monday 15 December 2008

Fluent NHibernate - Conventions and AutoPersistenceModel

Last time I have introduced the Fluent NHibernate, this time I would like to move further and show you how to use Conventions and AutoPersistenceModel.

But before we move to the point lets take a quick look on things that have changed in our sample web application:

1) Global.asax

NHibernate SessionFactory is created only once, on application start, then it is reused to create Sessions for each request. This approach allows us to use NHibernate Session in code behind of ASP.NET pages.

2) To demonstrate more interesting things I had to add one more table, so now we are playing with two tables:


I am, of course, still using AdventureWorks database. With a new table and relations between those two tables, our mappings have changed:

  • Product:


   1:  public class ProductMap : ClassMap<Product>
   2:  {
   3:      public ProductMap()
   4:      {
   5:          WithTable("Production.Product");        
   6:          Id(x => x.Id, "ProductID");
   7:   
   8:          Map(x => x.SellEndDate);
   9:          .
  10:          .
  11:          .
  12:          Map(x => x.ModifiedDate).Not.Nullable();
  13:   
  14:          HasMany<ProductReview>(x => x.ProductReview).WithKeyColumn("ProductID").AsBag().Inverse();
  15:      }
  16:  }

  • ProductReview:



   1:  public class ProductReviewMap : ClassMap<ProductReview>
   2:  {
   3:      public ProductReviewMap ()
   4:      {
   5:          WithTable("Production.ProductReview");
   6:   
   7:          Id(x => x.Id, "ProductReviewID");
   8:   
   9:          Map(x => x.Comments).WithLengthOf(3850);
  10:          Map(x => x.EmailAddress).Not.Nullable().WithLengthOf(50);
  11:          Map(x => x.Rating).Not.Nullable();
  12:          Map(x => x.ReviewDate).Not.Nullable();
  13:          Map(x => x.ReviewerName).Not.Nullable().WithLengthOf(50);
  14:          Map(x => x.ModifiedDate).Not.Nullable();
  15:   
  16:          References(x => x.Product, "ProductID");
  17:      }
  18:  }


Please note that I have skipped some irrelevant parts of the mappings but you can download the sample project to get source code. A bit of clarification:
  • Product can have 0 or multiple reviews. From the code point of view, Product class has additional IList property.
  • Review is about a product, therefore there is a not null foreign key in the ProductReview table.


Conventions

Now we can move to the point ... as you can see in above mappings, in some places it's required to specify column name or table name. This is because AdventureWorks database doesn't follow the default convention. (check Convention over Configuration design pattern) Differences:
  • table name is different then class name (Product vs Production.Product)
  • id property has different name then primary key column (Id vs ProductID)
  • properties representing links between tables have different names then foreign key column names (Product vs ProductID)
Luckily for us we don't have to repeat the same changes for all our mappings ... we can change the default convention ... here is how it can be done:



   1:  var models = new PersistenceModel();
   2:   
   3:  // table name = "Production." + class name
   4:  models.Conventions.GetTableName = type => String.Format("{0}.{1}", "Production", type.Name);
   5:   
   6:  // primary key = class name + "ID"
   7:  models.Conventions.GetPrimaryKeyNameFromType = type => type.Name + "ID";
   8:   
   9:  // foreign key column name = class name + "ID" 
  10:  //
  11:  // it will be used to set key column in example like this:
  12:  //
  13:  // <bag name="ProductReview" inverse="true">
  14:  //   <key column="ProductID" />
  15:  //   <one-to-many class="AdventureWorksPlayground...ProductReview, AdventureWorksPlayground, ..." />
  16:  // </bag>
  17:  models.Conventions.GetForeignKeyNameOfParent = type => type.Name + "ID";
  18:   
  19:  // foreign key column name = property name + "ID"
  20:  //
  21:  // it will be used in case like this:
  22:  // <many-to-one name="Product" column="ProductID" />
  23:  models.Conventions.GetForeignKeyName = prop => prop.Name + "ID";
  24:   
  25:  models.addMappingsFromAssembly(typeof(Product).Assembly);
  26:  models.Configure(config); 


and our mapping can be simplified to this:



   1:  public class ProductReviewMap : ClassMap<ProductReview>
   2:  {
   3:      public ProductReviewMap()
   4:      {
   5:          Id(x => x.Id);
   6:   
   7:          Map(x => x.Comments).WithLengthOf(3850);
   8:          Map(x => x.EmailAddress).Not.Nullable().WithLengthOf(50);
   9:          Map(x => x.Rating).Not.Nullable();
  10:          Map(x => x.ReviewDate).Not.Nullable();
  11:          Map(x => x.ReviewerName).Not.Nullable().WithLengthOf(50);
  12:          Map(x => x.ModifiedDate).Not.Nullable();
  13:   
  14:          References(x => x.Product);
  15:      }
  16:  }

EDIT: API for conventions was changed completely therefore code which is above is no longer valid, you can find update in this post - Conventions After Rewrite


AutoPersistenceModel

In fact ... in our mappings, there is not much left ... for sure you won't find there anything particularly creative therefore why not get rid of it completely? Yes ... it's possible ... if everything is 100% in accordance with the convention then you can simply use the following code to get NHibernate configured:



   1:  var models = AutoPersistenceModel
   2:      .MapEntitiesFromAssemblyOf<ProductReview>()
   3:      .Where(t => t.Namespace == "AdventureWorksPlayground.Domain.Production" );
   4:   
   5:  models.Conventions.GetTableName = prop => String.Format("{0}.{1}", "Production", prop.Name);
   6:  models.Conventions.GetPrimaryKeyNameFromType = type => type.Name + "ID";
   7:  models.Conventions.GetForeignKeyNameOfParent = type => type.Name + "ID";
   8:  models.Conventions.GetForeignKeyName = prop => prop.Name + "ID";
   9:   
  10:  models.Configure(config);


And that is all what you need ... a few POCO objects representing database tables, AutoPersistenceModel and you are ready to go. For sure it allows you to start very fast with development but what worries me is that there is no way to say that some properties are mandatory or have length limit. Specifying those additional data may help you to discover data related problems faster and moreover, it should increase performance of the NHibernate ... but is it worth it? What do YOU think?

(EDIT: Examples in this post have been updated on 6.02.2009 to reflect changes in Fluent NHibernate API)

Links:
Other interesting posts about Conventions and AutoPersistenceModel:

Thursday 11 December 2008

Fluent NHibernate introduction and quick start guide

I'm sure that lots of people is familiar with NHibernate and had a chance to work with it. I had a chance and I have to admit that it was a good time. But there are certain things which I hate about NHibernate like ... XML files, configuration, mappings ... to get it running tons of XML has to be produced.

Right, it's true that once this is done you don't have to go back there very often ... unless you want to do some refactoring. If you would like to rename some classes, move them to a different namespace or maybe rename some of the properties then you are in a big trouble ... Visual Studio and ReSharper will help you with refactoring of your code but you have to maintain XML files on your own.

Introduction to Fluent NHibernate

Apparently someone finally got pissed off and decided to change it ... goal was to get rid of all that maddening XML files. In other words, idea was to replace this:


   1:  <?xml version="1.0" encoding="utf-8" ?>
   2:  <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
   3:      namespace="Eg" assembly="Eg">
   4:  
   5:      <class name="Customer" table="Customers">
   6:          <id name="ID">
   7:              <generator class="identity" />
   8:          </id>
   9:  
  10:          <property name="Name" />
  11:          <property name="Credit" />
  12:  
  13:          <bag name="Products" table="Products">
  14:              <key column="CustomerID"/>
  15:              <one-to-many class="Eg.Product, Eg"/>
  16:          </bag>
  17:  
  18:          <component name="Address" class="Eg.Address, Eg">
  19:              <property name="AddressLine1" />
  20:              <property name="AddressLine2" />
  21:              <property name="CityName" />
  22:              <property name="CountryName" />
  23:          </component>
  24:      </class>
  25:  </hibernate-mapping>


with that:


   1:  public CustomerMap : ClassMap<Customer>
   2:  {
   3:      public CustomerMap()
   4:      {
   5:          Id(x => x.ID);
   6:          Map(x => x.Name);
   7:          Map(x => x.Credit);
   8:          HasMany<Product>(x => x.Products)
   9:              .AsBag();
  10:          Component<Address>(x => x.Address, m =>
  11:                 {
  12:                     m.Map(x => x.AddressLine1);
  13:                     m.Map(x => x.AddressLine2);
  14:                     m.Map(x => x.CityName);
  15:                     m.Map(x => x.CountryName);
  16:                 });
  17:      }
  18:  }

(example from James Gregory's blog)

In this form you can refactor your code as much as you like, it won't break your mappings. Your application is much more readable and maintainable. Thanks to Fluent NHibernate (FN) you can forget about XML files.

Quick Start Guide

We are still waiting for the first official release of Fluent NHibernate (FN) so at the moment the only option to get it is to use SVN client, check out the project and build dll on your own.

Having FluentNHibernate.dll you can simply add it to your project and start using it.

To make it easier I have prepared very basic web application which has FN set up, you can download it here. It uses popular AdventureWorks database.

Steps to get FN working:
  1. First of all you need to configure NHibernate which includes general configuration (connection string, dialect, driver etc) and mappings. In terms of general configuration, you have two options:
    • configure NHibernate without any XML files


         1:  // of course, in real life, connection string should be externalized
         2:  const string connectionString =
         3:  @"Data Source=MAREK-PC\SQLEXPRESS;Database=AdventureWorks;User Id=marek;Password=marek;Network Library=DBMSSOCN;Max Pool Size=400;";
         4:  
         5:  // configure nhibernate
         6:  Configuration config = MsSqlConfiguration.MsSql2005
         7:    .ConnectionString.Is(connectionString)
         8:    .UseReflectionOptimizer()
         9:    .ShowSql()
        10:    .ConfigureProperties(new Configuration());
        11:  
        12:  // load mappings from this assembly
        13:  config.AddMappingsFromAssembly(Assembly.GetExecutingAssembly());
        14:  
        15:  // build factory
        16:  ISessionFactory sessionfactory = config.BuildSessionFactory();


    • or you can decide to keep nhibernate.cfg.xml:


         1:  // read hibernate.cfg.xml
         2:  Configuration config = new Configuration().Configure();
         3:  
         4:  // load mappings from this assembly
         5:  config.AddMappingsFromAssembly(Assembly.GetExecutingAssembly());
         6:  
         7:  // build factory
         8:  ISessionFactory sessionfactory = config.BuildSessionFactory();


  2. Mappings, in this case my goal was to keep this example as simple as possible and elaborate it in future therefore for now only one table is mapped:
    • this is a POCO object representing a product:


         1:  public class Product
         2:  {
         3:      public virtual int Id { get; set; }
         4:      public virtual string Name { get; set; }
         5:      public virtual string ProductNumber { get; set; }
         6:      public virtual bool MakeFlag { get; set; }
         7:      public virtual bool FinishedGoodsFlag { get; set; }
         8:      public virtual string Color { get; set; }
         9:      public virtual int SafetyStockLevel { get; set; }
        10:      public virtual int ReorderPoint { get; set; }
        11:      public virtual int StandardCost { get; set; }
        12:      public virtual int ListPrice { get; set; }
        13:      public virtual String Size { get; set; }
        14:      public virtual int DaysToManufacture { get; set; }
        15:      public virtual String ProductLine { get; set; }
        16:      public virtual String Class { get; set; }
        17:      public virtual String Style { get; set; }
        18:      public virtual DateTime SellStartDate { get; set; }
        19:      public virtual DateTime SellEndDate { get; set; }
        20:      public virtual DateTime ModifiedDate { get; set; }
        21:  }
    • and mapping:


         1:  public ProductMap()
         2:  {
         3:      // table name is different then class name
         4:      WithTable("Production.Product");
         5:  
         6:      Id(x => x.Id, "ProductID");
         7:  
         8:      Map(x => x.SellEndDate);
         9:      Map(x => x.ReorderPoint);
        10:  
        11:      Map(x => x.Name).WithLengthOf(50).Not.Nullable();
        12:      Map(x => x.ProductNumber).WithLengthOf(25).Not.Nullable();
        13:      Map(x => x.MakeFlag).Not.Nullable();
        14:      Map(x => x.FinishedGoodsFlag).Not.Nullable();
        15:      Map(x => x.Color).WithLengthOf(15);
        16:      Map(x => x.SafetyStockLevel).Not.Nullable();
        17:      Map(x => x.StandardCost).CanNotBeNull().CustomSqlTypeIs("money");
        18:      Map(x => x.ListPrice).CanNotBeNull().CustomSqlTypeIs("money");
        19:      Map(x => x.Size).WithLengthOf(5).Not.Nullable();
        20:      Map(x => x.DaysToManufacture).Not.Nullable();
        21:      Map(x => x.ProductLine).WithLengthOf(2);
        22:      Map(x => x.Class).WithLengthOf(2);
        23:      Map(x => x.Style).WithLengthOf(2);
        24:      Map(x => x.SellStartDate).Not.Nullable();
        25:      Map(x => x.ModifiedDate).Not.Nullable();
        26:  }

      In this case table name is different then class name that is why I had to specify it but in general FN assumes that class name equals table name. The same approach is applied for properties and columns. Again you can specify that a column has different name then a corresponding property if necessary. Also it's not required to specify generator type for primary key column. FN checks property type and uses default one.

  3. Testability - significant advantage is that you don't have to remember about keeping XML files up-to-date with your code and database schema, but also FN offers number of ways to facilitate creation of unit tests and integration test. I will cover that in a next post. (edit: post regarding integration tests)

I encourage you to keep an eye on this project. In a meantime you can start playing with the Fluent NHibernate by checking the sample web application.

The AdventureWorks database you can download from Codeplex.

(EDIT: Examples in this post have been updated on 8.02.2009 to reflect changes in Fluent NHibernate API)

Related articles:

Thursday 4 December 2008

The Story of the Ribbon

Yesterday I had a chance to attend the 14th Poznan .Net User Group meeting. One of the guests was Mariusz Jarzębowski from Microsoft with his presentation "The Story of the Ribbon".


Microsoft Word 2007 and the Ribbon user interface

This excellent presentation was demonstrated for the first time during the MIX 2008 by Jensen Harris. (Edit: Presentation which we saw was inspired by both Jensen Harris' blog post and his presentation during the MIX08) Thanks to Mariusz we got a chance to see it live and "experience" evolution of Microsoft Office user interface (UI) from the very beginning - version 1.0 to the latest one - Office 2007. I think it's safe nowadays to say that The Ribbon started new era in UI design world. Mariusz gave us lots of insights regarding the process and decisions which have changed the way we think about the UI. Microsoft has invested years of work to check number of concepts, build prototypes, analyse data ... very detailed data.

For instance there are at least three ways to save document in MS Word
  1. Ctrl+S,
  2. save on toolbar and,
  3. menu File->Save.

Based on gathered data Microsoft realized that roughly 80% of people use Toolbar to save documents, 20% use Ctrl+S and almost noone uses the File->Save option. It was even more interesting that the same rule applies to other commands, hence that they can get rid of the old-fashion menu as people don't use it anyway!

Even though the numbers (exact number of clicks for each option) were convincing, still changing approach and switching from ordinary menu to the Ribbon was a risky decision. Microsoft took that risk and released the Ribbon which in my opinion (and not only my) makes them one of the most innovative company in the IT industry.

Mariusz also presented some other cool and relatively new concepts/features like:
  • Deep Zoom which allows users to explore collection of (usually) high resolution pictures without downloading them all at once to the client. Microsoft released Deep Zoom Composer which users can use to create presentation of images and then preview it with Silverlight 2 and Deep Zoom feature. check Seadragon project for more details and demos. Also check Hard Rock website for great example of Deep Zoom in action.
  • Photosynth is yet another cool concept which allows users to generate 3D model based on the pictures taken with any ordinary digital camera. Great stuff and very spectacular, it also uses Deep Zoom to enrich user experience. Check this presentation of Blaise Aguera y Arcas showing Deep Zoom and Photosynth.



  • pptPlex - Deep Zoom can also be used to change your Powerpoint presentations, this plug-in enables this feature.

For me this meeting was very interesting and I hope that Mariusz will visit us again at some point in the future. In a meantime let's enjoy all that new tools and keep eyes open for next innovative ideas as I'm sure that they will come.

Monday 1 December 2008

Problems with EPiServer 5 R2 and MS Vista

Here are a few problems which I have encountered while installing the latest EPiServer 5 R2 on MS Vista. It's nothing major but hopefully it will help someone.

So what can possibly go wrong?

1) In my case whole installation went smoothly, the first problem came out when I was expecting to see the normal start page of public templates. Error message was:

Compiler Error Message: CS0016: Could not write to output file 'c:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\Temporary ASP.NET Files\xxxxxx\xxxx


Luckily problem wasn't difficult to solve, it was a matter of permissions ... detailed solution can be found here.


2) Second problem was a bit strange -- I coudn't access edit and admin modes even though I was one hundred percent sure that credentials were okey and I was able to log in.

If you were playing with EPiServer before then probably you know that only certain roles can access edit/admin modes and it's configured in web.config. By default WebAdmins, Administrators can access both admin and edit modes; for WebEditors only edit mode is accessible. So the obvious thing to check was if my user belonged to any of those groups.

Surprisingly, I didn't have on my Vista group called Administrators ... why? Because I use localized version of Vista and group Administrators is localized and in my case called "Administratorzy" :)

Solution in such case is modifying the web.config by adding/updating the Administrator group name.

And that was it ... EPiServer 5 R2 was running fine :)

3) In the end I wanted to open Public Templates project to take a look if everything is as it used to be ... and yet another surprise:



Well, unfortunately Windows Vista Home Premium can't have Windows Authentication installed. It's a big shame and it was stoping Visual Studio from opening the project. I just wanted to take a look, I didn't need to debug this application on IIS but still Visual Studio was determined to keep the project closed.

The fastest workaround is to open project file in the notepad and modify the UseIIS property:



<WebProjectProperties>
<UseIIS>True</UseIIS>
<AutoAssignPort>True</AutoAssignPort>
<DevelopmentServerPort>50713</DevelopmentServerPort>
<DevelopmentServerVPath>/</DevelopmentServerVPath>
<IISUrl>http://localhost/</IISUrl>
<NTLMAuthentication>False</NTLMAuthentication>
<UseCustomServer>False</UseCustomServer>
<CustomServerUrl>
</CustomServerUrl>
<SaveServerSettingsInUserFile>False</SaveServerSettingsInUserFile>
</WebProjectProperties>



If you set UseIIS property to False, Visual Studio won't complain about missing Windows Authentication module anymore, easy?