Logging in NHibernat 3.0

With the release of NHibernate 3.0 the way NHibernate handles logging is changed. Up to 2.1.2 NHibernate used log4net exclusive for logging. The usage of log4net was directly tied into each class of NHibernate.

Now this has been decoupled. NHibernate 3.0 introduces a LoggingProvider. So instead of

private static readonly ILog log = LogManager.GetLogger(typeof (Loader));

a new logger is created using

private static readonly IInternalLogger log = LoggerProvider.LoggerFor(typeof (Loader));

Even though this might not seem like a big deal, there is more to the LoggingProvider. The logging provider currently only supports log4net (well, and a NoLoggingLoggerFactory). In order to determine whether log4net is available for logging, the LoggingProvider looks in the current SearchPath of BaseDirectory of the AppDomain like this:

// look for log4net.dll
string baseDir = AppDomain.CurrentDomain.BaseDirectory;
string relativeSearchPath = AppDomain.CurrentDomain.RelativeSearchPath;
string binPath = relativeSearchPath == null ? baseDir : Path.Combine(baseDir, relativeSearchPath);
var log4NetDllPath = Path.Combine(binPath, "log4net.dll");

What go me started was the fact, the my log4net configuration wasn’t creating any log-output, and I was extremely puzzled on why. I checked my config a dozen times without any error. This was working perfectly fine when working with NHibernate 2.1.2.

After looking at the LoggingProvider and the way logging is initialized it struck me: my log4net assembly is located in the GAC – the the lookup for log4net isn’t detecting my log4net assembly! Changing the properties of the reference to log4net to Copy Local resolved this issue.

Localization using NHibernate and filters

After I received some feedback on my post on filtering with NHibernate, I starting thinking on how to leverage the filter capabilities of NHibernate to create a system for creating localized applications. While the previous post already considered using filters for multi-language apps, there was some criticism.

When I want to localize a pizza I had to create a new record in the database. One of the first drawbacks was the fact, that the ID of the pizza is not unique anymore, since each pizza exists for each language. Well, this is actually just a matter of “adjusting” the data model a little bit. You could just create some internal ID for each pizza as the primary key or you create a composite key based on the ID and the locale-code.

A bigger problem is the fact, that you will have to duplicate all the data, which isn’t actually changing for each pizza, such as the price.

The new model

A better data model could look like this:

PizzaModel

Some explanation: a pizza has always a list of toppings. Pizzas as well as toppings have name, which should be localized. This name can exists in various languages for a pizza. The class LocalizableBase adds a list of strings, which contains all localized strings.

This way we don’t have to store language invariant data redundant in the database.

The usage

OK, since we have our revised model, the actual usage is straight-forward.

// create toppings
var pilze = new Entities.Topping { InternalName = "Pilze" };
pilze.AddString("de","Pilze");
pilze.AddString("en","Mushrooms");
_session.Save(pilze);
var salami = new Entities.Topping() {  InternalName = "Salami" };
salami.AddString("de","Salami");
salami.AddString("en","Pepperoni");
_session.Save(salami);
var kaese = new Entities.Topping() {InternalName = "Käse"};
kaese.AddString("de","Käse");
kaese.AddString("en","Cheese");
// create pizza
var funghi = new Entities.Pizza()
{
    Price = 10,Costs = 5,Toppings = {pilze}
};
funghi.AddString("de", "Funghi");
funghi.AddString("en", "Mushroom Heaven");
_session.Save(funghi);
var peperoni = new Entities.Pizza()
{
    Price = 11,
    Costs = 6.5,
    Toppings = { salami,kaese }
};
peperoni.AddString("de", "Salami++");
peperoni.AddString("en", "Peperoni++");
_session.Save(peperoni);

At first we create some toppings. Each topping has an internal, language-invariant name. Using AddString (from LocalizeableBase) we can add strings for a certain language. The same applies for pizza.

In order to get a pizza, we just create a simple query.

[Test]
public void CanGetLocalizedPizza()
{
    _session.EnableFilter("PizzaLocaleFilter").SetParameter("myLocale", "en");
    var pizzaList = _session.CreateCriteria<Pizza.Entities.Pizza>()
        .List<Entities.Pizza>();
    Assert.That(pizzaList,Has.Count.EqualTo(2),"number of pizza");
    Assert.That(pizzaList[0].Name, Is.EqualTo("Mushroom Heaven"));
    Assert.That(pizzaList[1].Name, Is.EqualTo("Peperoni++"));
    Assert.That(pizzaList[0].Toppings, Has.Count.EqualTo(1),"number of toppings");
    Assert.That(pizzaList[0].Toppings[0].Name,Is.EqualTo("Mushrooms"),"name of topping");
}

As the above sample shows, we just have to define our filter once (!). In the actual query we don’t have to deal with the filtering anymore, this is all being handled by NHibernate.

The implementation

Finally I would like who this is all accomplished. At first we store all localizable strings in a separate table. This table contains strings for toppings as well as for pizzas. This table is being mapped to a class, which is used by LocalizableBase.

<class name="LocalicedString" table="LocalicedStrings">
  <id name="Id">
    <generator class="identity"/>
  </id>
  <property name="ObjectType"/>
  <property name="Locale"/>
  <property name="Text"/>
</class>

The property ObjectType stores to which entity this string actually belongs to. The value for this property is being determined in the method AddString in LocalizableBase. This way the pizza entity doesn’t have to take care about setting the property to the correct value. Besides the object-type, we also store the id of the actual object. Since this id is not directly being used, we don’t need to map it to the database (at least not at this point; we’ll see that later).

/// <summary>
/// base class to supply localizablity to entities
/// </summary>
public abstract class LocalizableBase
{
    /// <summary>
    /// Initializes a new instance of the <see cref="LocalizableBase"/> class.
    /// </summary>
    public LocalizableBase()
    {
        Strings = new List<LocalicedString>();
    }
    /// <summary>
    /// a list of all localized strings
    /// </summary>
    public virtual IList<LocalicedStrings> Strings { get; private set; }
    /// <summary>
    /// adds a new string to the list of <see cref="Strings"/>
    /// </summary>
    /// <remarks>along with the text and the locale, the object to which
    /// this text is associated is also being stored</remarks>
    /// <param name="locale">the locale of the text</param>
    /// <param name="text">the text to store</param>
    public virtual void AddString(string locale, string text)
    {
        Strings.Add(new LocalicedString
                        {
                            Locale = locale,
                            Text = text,
                            ObjectType = GetType().ToString()
                        });
    }
}

OK, now we need the entities for pizza and topping as well. This is actually nothing earth-rocking. The property Name returns always the first localized string that was found for the entity. If no localized string could be found, we just return an empty string.

/// <summary>
/// Business Entity representing a pizza
/// </summary>
public class Pizza : LocalizableBase
{
    /// <summary>
    /// Initializes a new instance of the <see cref="Pizza_old"/> class
    /// </summary>
    public Pizza()
    {
        Toppings = new List<Topping>();
    }
    /// <summary>
    /// gets the internal, unique id of this pizza
    /// </summary>
    /// <value>The id.</value>
    public virtual int Id { get; private set; }
    /// <summary>
    /// gets the localized name of the pizza
    /// </summary>
    public virtual string Name
    {
        get { return Strings.Count >= 1 ? Strings[0].Text : string.Empty; }
    }
    /// <summary>
    /// gets or sets the toppings, which are being used by the pizza
    /// </summary>
    public virtual IList<Topping> Toppings { get; private set; }
    /// <summary>
    /// selling price for this pizza
    /// </summary>
    public virtual double Price { get; set; }
    /// <summary>
    /// gets or sets the costs to produce this pizza
    /// </summary>
    public virtual double Costs { get; set; }
}
/// <summary>
/// Business Entity representing a pizza-topping
/// </summary>
public class Topping : LocalizableBase
{
    /// <summary>
    /// gets the internal, unique id of this topping
    /// </summary>
    /// <value>The id.</value>
    public virtual int Id { get; private set; }
    /// <summary>
    /// gets or sets internal name of this topping - this is language invariant!
    /// </summary>
    public virtual string InternalName { get; set; }
    /// <summary>
    /// gets the localized name of the topping
    /// </summary>
    /// <value>The name.</value>
    public virtual string Name
    {
        get { return Strings.Count >= 1 ? Strings[0].Text : string.Empty; }
    }
    /// <summary>
    /// supplier, from where this topping is being bought
    /// </summary>
    public virtual string Supplier { get; set; }
}

The clue to this solution is again the usage of NHibernate filters, in order to get the strings for the desired language. For that purpose we filter for the desired locale. In this mapping we also find the ItemId property, which was kinda missing in the LocalizedStrings mapping. The ItemId refers to the Id of the item, to which this string actually belongs. In combination with the ObjectType this should be unique.

<class name="Pizza" table="Pizzas">
  <id name="Id">
    <generator class="identity" />
  </id>
  <property name="Price"/>
  <property name="Costs"/>
  <bag name="Strings" table="LocalicedStrings"
       cascade="all" fetch="join"
       where="ObjectType='Pizza.Entities.Pizza'">
    <key column="ItemId" />
    <one-to-many class="LocalicedString"/>
    <filter name="PizzaLocaleFilter" condition=":myLocale = Locale"/>
  </bag>
  <bag name="Toppings" table="PizzaToppings" cascade="all">
    <key column="PizzaId" />
    <many-to-many column="ToppingId" class="Topping" />
  </bag>
</class>

Besides the filtering to a certain language, we also only want to get strings for the current ObjectType. This is done, by specifying a where-clause in the bag-definition. This way we only get strings for pizza.

While this removes the actual need to deal with the ObjectType in the query, we have to specify it in the mapping – well nothing’s perfect. If you already have an application with a lot of existing queries, this might be a better approach.

Just for the hack of it: this is the SQL being send to the database for the little UnitTest query:

SELECT this_.Id as Id4_1_, this_.Price as Price4_1_, this_.Costs as Costs4_1_, strings2_.ItemId as ItemId3_, strings2_.Id as Id3_, strings2_.Id as Id7_0_, strings2_.ObjectType as ObjectType7_0_, strings2_.Locale as Locale7_0_, strings2_.Text as Text7_0_
FROM Pizzas this_ left outer join
    LocalicedStrings strings2_ on
        this_.Id=strings2_.ItemId and
        @p0 = strings2_.Locale and
        (strings2_.ObjectType='Pizza.Entities.Pizza')
;@p0 = 'en'
SELECT toppings0_.PizzaId as PizzaId1_, toppings0_.ToppingId as ToppingId1_, topping1_.Id as Id6_0_, topping1_.InternalName as Internal2_6_0_
FROM PizzaToppings toppings0_ left outer join
    Toppings topping1_ on
        toppings0_.ToppingId=topping1_.Id
WHERE toppings0_.PizzaId=@p0
;@p0 = 1
SELECT strings0_.ItemId as ItemId1_, strings0_.Id as Id1_, strings0_.Id as Id7_0_, strings0_.ObjectType as ObjectType7_0_, strings0_.Locale as Locale7_0_, strings0_.Text as Text7_0_
FROM LocalicedStrings strings0_
WHERE  @p0 = strings0_.Locale and
    (strings0_.ObjectType='Pizza.Entities.Topping') and
    strings0_.ItemId=@p1
;@p0 = 'en', @p1 = 1

Painless filtering in NHibernate

The Stage

Let’s assume, there exists an application, which is already heavily relying on NHibernate. This app is supposed to be extended. All wordings are currently available in a single locale and re stored in the database. This is matter to change, since we want to introduce multi-language capabilities for our wordings.

The Plan

The simplest way to achieve the requirement would be to extend the domain-model in such a way, that a new column would be added to the tables in the database. This column would contain the locale of the text. Instead of a single row for each text, we would end up with a single row for each locale for each text.

In order to retrieve a certain text from the wording-table of the database, we not only need to know which text to get, but we also need to know which local of the text we need to get. So this would end up in an additional criteria when retrieving data from the database. This is the point where NHibernates comes into play. NHibernate offers global filters, which can be used in conjunction with regular queries.

I will demonstrate how to make use of these filter by showing a little example.

The Implementation

Let’s take a small app, which is persisting pizza and toppings using NHibernate. Therefore we have POCOs and according mappings.

namespace Pizza.Entities
{
    /// <summary>
    /// Business Entity representing a pizza-topping
    /// </summary>
    public class Topping
    {
        /// <summary>
        /// The internal, unique id of this topping
        /// </summary>
        /// <value>The id.</value>
        public virtual int Id { get; set; }
        /// <summary>
        /// the name of the topping
        /// </summary>
        /// <value>The name.</value>
        public virtual string Name { get; set; }
    }
    /// <summary>
    /// Business Entity representing a pizza
    /// </summary>
    public class Pizza
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="Pizza"/> class
        /// </summary>
        public Pizza()
        {
            Toppings = new List<Topping>();
        }
        /// <summary>
        /// The internal, unique id of this pizza
        /// </summary>
        /// <value>The id.</value>
        public virtual int Id { get; set; }
        /// <summary>
        /// the name of the pizza
        /// </summary>
        public virtual string Name { get; set; }
        /// <summary>
        /// the toppings, which are being used by the pizza
        /// </summary>
        public virtual IList<Topping> Toppings { get; private set; }
    }
}
<class name="Topping" table="Toppings">
  <id name="Id">
    <generator class="identity"/>
  </id>
  <property name="Name"/>
</class>
<class name="Pizza" table="Pizzas">
  <id name="Id">
    <generator class="identity"/>
  </id>
  <property name="Name"/>
  <list name="Toppings" table="PizzaToppings" cascade="all">
    <key column="PizzaId" />
    <index column="Position"/>
    <many-to-many column="ToppingId" class="Topping" />
  </list>
</class>

As you can see, pizza as well as toppings each have a name. A pizza also has a set of toppings.

So far, so good. To support multiple locales we could go several ways. We could introduces a property called Name_de as well as Name_en to support german (DE) and englisch (EN) locales. An alternative approach would be, to add a property called Locale, which hold the locale of the name. If I would like to get english pizzas I would need to filter for pizza entities, which have a locale of “en”.

OK, let’s go with the second alternative. But how do I restrict my queries to only fetch pizza entities with locale=en? With NHibernate-Filter!

First of all we need to define a filter.

<filter-def name="localized">
  <filter-param name="myLocale" type="System.String"/>
</filter-def>

The next step is, to include this newly created filter in the mappings.

<class name="Topping" table="Toppings">
  <id name="Id">
    <generator class="identity"/>
  </id>
  <property name="Name"/>
  <property name="Locale"/>
  <filter name="localized" condition=":myLocale = Locale"/>
</class>
<class name="Pizza" table="Pizzas">
  <id name="Id">
    <generator class="identity"/>
  </id>
  <property name="Name"/>
  <property name="Locale"/>
  <list name="Toppings" table="PizzaToppings" cascade="all">
    <key column="PizzaId" />
    <index column="Position"/>
    <many-to-many column="ToppingId" class="Topping">
      <filter name="localized" condition=":myLocale = Locale"/>
    </many-to-many>
  </list>
  <filter name="localized" condition=":myLocale = Locale"/>
</class>

This way I specify that by using the filter I want to restrict my results by the column Locale. It’s important to notice, that this will only happen when I’m actually using this filter. When I’m not using the filter this restriction is being ignored. Thus: the filter is optional!

OK, so how does this actually work in my application – how do I use such a filter? What I don’t want: I don’t want to change all my existing queries to include a filter for the locale column. Instead I’m just activating the previously defined filter and assign a certain value.

// enable the filter
_session.EnableFilter("localized").SetParameter("myLocale", "de");
// do some plain ol’ querying
var pizzaList = _session.CreateCriteria<Entities.Pizza>().List<Entities.Pizza>();

That’s it. There is nothing more to it. I can leave all my queries the way they are and just execute them like before. Let’s take a look at the SQL statements that are being created, because they will show the usage of the filter.

SELECT this_.Id as Id2_0_, this_.Name as Name2_0_, this_.Locale as Locale2_0_
    FROM Pizzas this_
    WHERE @p0 = this_.Locale;@p0 = 'de'

This shows the strength of NHibernate-Filters. Besides the modification of the mappings and the activation of the filter I don’t need to modify anything. I don’t need to alter my queries. The filter is being applied by NHibernate behind the scenes.

This also works in more complex scenarios. Let’s assume I have a pizza with several toppings. I only want to get a pizza in german – and of course I also want to get german toppings as well. This works as too.

var multiPizza = new Entities.Pizza {Name = "Multikulti", Locale = "de"};
multiPizza.Toppings.Add(new Topping {Name = "Käse", Locale = "de"});
multiPizza.Toppings.Add(new Topping {Name = "Mushrooms", Locale = "en"});
_session.Save(multiPizza);
_session.Flush();
_session.Clear();
var fromDb = _session.CreateCriteria<Entities.Pizza>().Add(Expression.Eq("Id", multiPizza.Id)).List<Entities.Pizza>()[0];
Assert.That(fromDb.Locale, Is.EqualTo("de"),"pizza locale");
Assert.That(fromDb.Toppings,Has.Count.EqualTo(1));
foreach (var topping in fromDb.Toppings)
{
    Assert.That(topping.Locale, Is.EqualTo("de"),"topping locale");
}

I already activated and set the filter in the constructor of my class – so I don’t need to do this over again. At least not as long as I reuse the same session. Let’s take a look at the SQL send tot eh database – this is just impressive!

SELECT this_.Id as Id2_0_, this_.Name as Name2_0_, this_.Locale as Locale2_0_
    FROM Pizzas this_
    WHERE @p0 = this_.Locale
        AND this_.Id = @p1;@p0 = 'de', @p1 = 4
SELECT toppings0_.PizzaId as PizzaId1_, toppings0_.ToppingId as ToppingId1_, toppings0_.Position as Position1_, topping1_.Id as Id1_0_, topping1_.Name as Name1_0_, topping1_.Locale as Locale1_0_
    FROM PizzaToppings toppings0_ left outer join Toppings topping1_ on
        toppings0_.ToppingId=topping1_.Id
    WHERE  @p0 = topping1_.Locale
        and toppings0_.PizzaId=@p1;@p0 = 'de', @p1 = 4

Conclusion

The use of NHibernate-Filters introduces a very transparent way to extend the applicationlogic to include additional filter criteria.

  • Instead of storing a newly localized text in a seperate column, this doesn’t require any schema-changes.
  • Only the rows to a certain locale are being retrieved, this might reduced bandwith.

Ready to get fluent

Wow – it’s been quite a while, but I recently had some spare time to continue to play a little with the fluent NHibernate stuff.

It was, when I figured, that Fluent NHibernate has went RTM – which actually solves the version mismatch when working with the NHibernateIntergrationFacility of Castle Windsor, because Fluent NHibernate is now also based on 2.1.0.4000 instead of 2.0.1.4000.

So – happy fluent NHibernating!

Getting fluent

The case

OK, so I worked around the first issues with the setup of the NHibernateIntagration facility and fluent NHibernate, and now are new issues just showing up.

So I wrote my first mapping:

public class AccountMap : ClassMap<Account>
{
public AccountMap()
{
Id(x => x.Number).GeneratedBy.Assigned();
Map(x => x.Name);
}
}

This looks pretty slick – in fact this is looking so darn cool, I want it to work!

But what is showing up in my testrunner is not very re-assuring: No persister found for Entity Account.

What the hell?!

The setup

So what happend so far? After getting my solution ready to compile, the next step is to add the fluent mapping to the current NHibernate configuration. Since the configuration is being created within the integration facility this doesn’t seem trivial. But actually, taking a closer look, this isn’t that complicated:

<facility id="nhibernate"
isWeb="false"
type="Castle.Facilities.NHibernateIntegration.NHibernateFacility, Castle.Facilities.NHibernateIntegration"
configurationBuilder="FluentConfigurationBuilder, Core">

The key is the configurationBuilder. This offers a hook in the configuration-building-process of the facility.

Next step is to implement the configuration-builder:

public class FluentConfigurationBuilder : IConfigurationBuilder
{
public Configuration GetConfiguration(IConfiguration facilityConfiguration)
{
var defaultConfigurationBuilder = new DefaultConfigurationBuilder();
var configuration = defaultConfigurationBuilder.GetConfigutation(facilityConfiguration);
configuration.AddMappingsFromAssembly(Assembly.LoadFrom("Core.dll"));
return configuration;
}
}

This is the recommended route to read the fluent mappings from the assembly and to add them to the NHibernate configuration. The key is the AddMappingsFromAssembly extension-method.

So, this is where I stand right now – and this is failing.

The solution

Looking at the source of the fluent NHibernate extension-method AddMappingsFromAssembly reveals some truth:

public static Configuration AddMappingsFromAssembly(this Configuration configuration, Assembly assembly)
{
var models = new PersistenceModel();
//models.AddMappingsFromAssembly(assembly);
models.Configure(configuration);
return configuration;
}

So there it is – black-on-white: the actual mapping is commented out – and no-one knows really why.

Ok, so my current (and working) code looks like this:

public class FluentConfigurationBuilder : IConfigurationBuilder
{
public Configuration GetConfiguration(IConfiguration facilityConfiguration)
{
var defaultConfigurationBuilder = new DefaultConfigurationBuilder();
var configuration = defaultConfigurationBuilder.GetConfigutation(facilityConfiguration);
var model = new PersistenceModel();
model.AddMappingsFromAssembly(Assembly.LoadFrom("Core.dll"));
model.Configure(configuration);
return configuration;
}
}

Everything is fluent

Since fluent is just everywhere, I thought that I would have to go the fluent NHibernate road as well. Turned out to be quite wacky.

First of – I just wrapped up all my referenced assemblies: Castle Windsor 2.0 (which is actually internally labeled as 1.1) as well as all the other stuff and NHibernate 2.1 GA. Then adding the new kind in town: Fluent NHibernate.

And this is where the trouble begins … Fluent NHibernate is build against 2.1.0.4000 of NHibernate (= 2.1 GA). This is good, since I just downloaded this exact version. But wait – what’s my most favorite NHIntegrationFacility doing there – it’s actually linked against 2.1.0.1003. So this screws me off.

OK – don’t panic! Fortunatly .Net has the ability to redirect assembly bindings. Just a little bit of tweaking in the app.config does the trick:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="NHibernate" publicKeyToken="aa95f207798dfdb4" culture="neutral" />
<bindingRedirect oldVersion="2.1.0.1003" newVersion="2.1.0.4000"/>
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>

Batching Statements in NHibernate

Picture the case where you want to insert/manipulate a mass of data using your already in place NHibernate infrastructure.

Usually you would do something like this:

for (int i = 0; i < 100; i++)
{
MyEntity entity = new MyEntity(i);
session.Save(entity);
}

This has the drawback of producing a single INSERT per save call, and thus creating 100 roundtrips to the database; one per insert. Further more the actual INSERT command will be re-constructed for each single Save call.

To circumwent some of these shortcummings you should start a new transaction and wrap all Save calls in this transaction:

ITransaction tx session.BeginTransaction();
for (int i = 0; i < 100; i++)
{
MyEntity entity = new MyEntity(i);
session.Save(entity);
}
tx.Commit();

This will at least reuse the command-object created for the first Save call.

If your’re using MS-SQL you can benefit from further enhancements. NHibernate offers the capability of batching statements. By adding a configuration setting for hibernate.adonet.batch_size with a value greater than 0 will enable batching. In conjunction with the transaction only after the in batch_size specified commands have been recorded this batch is send to the DB. So with a hibernate.adonet.batch_size of 10 the above code would result in 10 roundtrips instead of the original 100.

Remember the cicle of life!

I thought – hey, let’s use the WindsorContainer deal with all my dependencies, that exist in my presenter-class. So I don’t have to worry about all the kinds of repositories and other stuff I might need. So I added my presenter:

_nestedContainer.AddComponent(
"SelectionPresenter",
typeof (SelectionPresenter));

But when I accessed my presenter from a ASP.Net webpart, registered some eventhandler on the presenter, which pointed back to the WebPart:

public class MyWebPart : WebPart
{
protected override void CreateChildControls()
{
var presenter = Container.Instance.Resolve<SelectionPresenter>();
presenter.OnItemSelected += HandleOnItemSelected;
}
[...]
}

Then I raised the event (in a postback!!) which sould be handled within the WebPart – boom! NullReferenceException while accessing the WebPart from the presenter!

After an enourmous amount of time I finally figured it out … the default behavior of the WindsorContainer is to provide singleton instances of objects. So in my case the presenter is being created the first time the WebPart is executed, and when the postback occurs the same instance of the presenter is being returned – this is wrong!

OK, so changing the component-registration did take care of this problem:

_nestedContainer.AddComponentWithLifestyle(
"SelectionPresenter",
typeof (SelectionPresenter),
LifestyleType.Transient);

Having a one-to-one with nhibernate objects

So after getting my latest project up and running I wanted to look behind the scenes, and to do so I took a deep look into the sql-statement which are being executed against the database – yes I know, this might seem a little geeky.

So I figured, that I have two objects A and B which have a one-to-one association. And since I declared B to be lazy loaded from A I expected only to hit the ‘A’-table when fetching all ‘A’s from the DB. But I was far off – A and B where both completely scanned 🙁

After some quick google-ing I figured – this is just the way a one-to-one association works; there is no lazy loading! 🙁

OK, so I might switch to use something else instead …

NHibernatin' composite ids

So I tryed my way with NHibernate today, just doing some plain-old CRUD stuff. So I hooked up ye ol’ Norhtwind Database and tryed to create some objects. Ok, so I came up with Customers, Orders, OrderItems (called Order Details in the database) and Products. But wait – there is a slight problem: OrderItems has a so called composite-id. Although this is covered by NHibernate, this took quite a while to figure out. First of all I had to create the correct mapping file:

<?xml version='1.0' encoding='utf-8'?>
<hibernate-mapping
assembly=Business'
namespace=Business'
xmlns='urn:nhibernate-mapping-2.2'>
<class name='OrderItem'
table='[Order Details]'>
<composite-id>
<key-many-to-one name='Order'
column='OrderID'
class='Business.Order, business' />
<key-many-to-one name='Product'
column='ProductID'
class=Business.Product, business'/>
</composite-id>
<property name='UnitPrice'/>
<property name='Quantity'/>
<property name='Discount'/>
</class>
</hibernate-mapping>

After compiling this, I got a nice little error, telling me, that I should override the Equals() method. Ok, so I did how I was told, and came up with:

public override bool Equals(object obj)
{
//return base.Equals(obj);
OrderItem other = obj as OrderItem;
if (other != null) return (order.Id == other.order.Id) && (product.Id == other.product.Id);
else return false;
}
public override int GetHashCode()
{
//return base.GetHashCode();
return order.Id.GetHashCode() & product.Id.GetHashCode();
}

This finally did it 🙂