Try fast search NHibernate

13 November 2008

NH with Multiple Assembly versions in GAC

I know that the title have something strange because NH don’t have nothing to do with GAC, but in NH-JIRA we have an issue with this title: “NHibernate does not support multiple versions of the same assembly for entity mapping”

Perhaps I don’t understand what that issue mean; I can’t understand what mean work we two mappings versions of the same entity… mean it work with two tables versions in the same DB at the same time ?

If the problem is not the mapping but the class implementation, NH are using, this is the solution.

Domain version 1.0.0.0

public class Person
{
public virtual int Id { get; set; }
public virtual string FirstName { get; set; }
public virtual string LastName { get; set; }

public virtual string FullName
{
get { return string.Concat(FirstName, "-", LastName); }
}
}

The mapping is:

<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="Company.Domain"
namespace="Company.Domain">
<
class name="Person">
<
id name="Id">
<
generator class="native"/>
</
id>
<
property name="FirstName"/>
<
property name="LastName"/>
</
class>
</
hibernate-mapping>

Note: In the mapping I don’t write anything about the version of the assembly.

Sign, and compile the domain, and add it to the GAC.

GACAdded

Domain version 1.1.0.0

public class Person
{
public virtual int Id { get; set; }
public virtual string FirstName { get; set; }
public virtual string LastName { get; set; }

public virtual string FullName
{
get { return string.Concat(LastName, "*", FirstName); }
}
}

As you can see there is a different implementation of the property FullName.

Compile and add the new version to the GAC.

GACAddedBoth

The Test

Without make a completely automated test…

[Test]
public void SomebodySaidIsABug()
{
var version = typeof (Person).Assembly.GetName().Version.ToString();
Console.WriteLine("Running version {0}", version);

object savedId;
using (ISession s = sessions.OpenSession())
using (ITransaction tx = s.BeginTransaction())
{
var a = new Person {FirstName = "Pasqual", LastName = "Angulo"};
savedId = s.Save(a);
tx.Commit();
}

using (ISession s = sessions.OpenSession())
using (ITransaction tx = s.BeginTransaction())
{
var a = s.Get<Person>(savedId);
if(version == "1.0.0.0")
Assert.That(a.FullName, Is.EqualTo("Pasqual-Angulo"));
else
Assert.That(a.FullName, Is.EqualTo("Angulo*Pasqual"));
tx.Commit();
}

using (ISession s = sessions.OpenSession())
using (ITransaction tx = s.BeginTransaction())
{
s.Delete("from Person");
tx.Commit();
}
}

I’m showing the loaded version used in tests (that, in this, case represent the assembly with the responsibility of DataAccess) and changing the expected value of FullName according with the assembly version.

The assembly referenced by the test project, at design time, is not important.

The App.config of the test is:

<configSections>
<
section name="hibernate-configuration" type="NHibernate.Cfg.ConfigurationSectionHandler, NHibernate"/>
</
configSections>

<
hibernate-configuration xmlns="urn:nhibernate-configuration-2.2">
<
session-factory name="Company">
<
property name="connection.provider">NHibernate.Connection.DriverConnectionProvider</property>
<
property name="connection.driver_class">NHibernate.Driver.SqlClientDriver</property>
<
property name="dialect">NHibernate.Dialect.MsSql2005Dialect</property>
<
property name="connection.connection_string">
Data Source=localhost\SQLEXPRESS;Initial Catalog=BlogSpot;Integrated Security=True
</property>
<
property name="show_sql">false</property>
<
property name="use_outer_join">true</property>
<
property name="query.substitutions">true 1, false 0, yes 'Y', no 'N'</property>
<
property name="command_timeout">60</property>
<
property name="proxyfactory.factory_class">NHibernate.ByteCode.LinFu.ProxyFactoryFactory, NHibernate.ByteCode.LinFu</property>

<
mapping assembly="Company.Domain"/>

</
session-factory>
</
hibernate-configuration>
I’m loading the assembly with all mappings, trough the session-factory configuration, another time without specify the assembly strong name.
Compile the test project and close VisualStudio. Now go to the output folder (bin/Debug) and delete the Company.Domain.dll file. Then open NUnit.Gui.
The situation at this point is:
  • Two version of Company.Domain.dll in the GAC.
  • Visual Studio Closed
  • The test compiled and the Company.Domain.dll removed
  • NUnit.GUI opened
Because I'm not using the strongly name of Company.Domain.dll, running the test what I expect is that it fail
NunitNoOk0  
Perfect!! Assembly not found. Now I can start to play with the compiled configuration file (DiffAssemblyVersion.dll.config) using the NotePad.
    <runtime>
<
assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1" applies-to="v2.0.50727">
<
qualifyAssembly partialName="Company.Domain"
fullName="Company.Domain, Version=1.0.0.0, Culture=neutral, PublicKeyToken=5c41dce254553643" />
</
assemblyBinding>
</
runtime>

Now, if I reload the assembly in NUnit.GUI, the assembly will be loaded from GAC.

NunitOk10

Now I want run the test but using the 1.1.0.0 version. Using NotePad again the new config is:

<runtime>
<
assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1" applies-to="v2.0.50727">
<
qualifyAssembly partialName="Company.Domain"
fullName="Company.Domain, Version=1.0.0.0, Culture=neutral, PublicKeyToken=5c41dce254553643" />
<
dependentAssembly>
<
assemblyIdentity name="Company.Domain"
publicKeyToken="5c41dce254553643"/>
<
bindingRedirect oldVersion="1.0.0.0"
newVersion="1.1.0.0"/>
</
dependentAssembly>
</
assemblyBinding>
</
runtime>

Reload the assembly in NUnit.GUI and re-run it:

NunitOk11

Work done.

Code available here.

Technorati Tags:

No comments:

Post a Comment