Try fast search NHibernate

26 February 2009

NH2.1.0: generators behavior explained

After this post and this other, perhaps is the moment to dive in generators behavior. In the last year we saw an increment of NHibernate’s users and sometimes they are ignoring some facts, by the way I'm hoping this post will be useful to “old” users too. I will try to show you what happen behind the scene and hopefully this will be one of the bases, of your decision, when you must choose a POID generator strategy.

The Domain

public class Person
{
private IList<Pet> pets = new List<Pet>();
public virtual string Name { get; set; }
public virtual IEnumerable<Pet> Pets
{
get { return pets; }
}

public virtual Person Partner { get; set; }
public virtual void Add(Pet pet)
{
pet.Owner = this;
pets.Add(pet);
}
}

public class Pet
{
public virtual Person Owner { get; set; }
public virtual string Description { get; set; }
}

One Full-mapping:
<class name="Person">
<id type="int">
<generator class="native"/>
</id>
<property name="Name"/>
<many-to-one name="Partner" class="Person"/>
<bag name="Pets" cascade="all" inverse="true" access="field.camelcase">
<key column="ownerId" on-delete="cascade"/>
<one-to-many class="Pet"/>
</bag>
</class>

<class name="Pet">
<id type="int">
<generator class="native"/>
</id>
<property name="Description"/>
<many-to-one name="Owner" class="Person" column="ownerId"/>
</class>
Resuming:
  • A class (Person) with two relationship
  • One relation (Partner) doesn't have cascade
  • One relation is a collection with all cascades
  • The POID is only part of the persistence and we don’t have the ID property in the domain. This is useful especially in this case because I want run the same code changing only the id-generator-strategy, and the ID-Type, in the mapping.

The Demo

Each demo will be ran in two environments:
  1. without set the adonet.batch_size property in the NHibernate configuration.
  2. setting the adonet.batch_size to 10
For all demos the the loggers I’m watching are : "NHibernate.SQL", "NHibernate.Id", "NHibernate.AdoNet.AbstractBatcher"
The main demo is the follow
using (ISession session = factory.OpenSession())
using (ITransaction tx = session.BeginTransaction())
{
log.Debug("Saving");

var person = new Person {Name = "Person"};
var patner = new Person {Name = "PatnerOfPerson"};
person.Partner = patner;
person.Add(new Pet {Description = "Reptile"});
person.Add(new Pet {Description = "Dog"});
person.Add(new Pet {Description = "Cat"});
session.Save(patner);
session.Save(person);

log.Debug("Saved");

log.Debug("Committing");
tx.Commit();
log.Debug("Committed");
}
In a session+transaction I’m creating a Person, its partner, 3 pets associated to the Person; I’m saving the first the Partner (because it don’t has cascade actions) and then the Person. The log is to understand when “things” happens.
Two additional demos showing a “not recommended” usage of NHibernate (without enclose actions in one transaction) but useful to understand the behavior of generators.
The first:
public void AllInOneSession()
{
var animal = new Pet();
using (ISession session = factory.OpenSession())
{
session.Save(animal);
animal.Description = "Boa";
session.Update(animal);
session.Delete(animal);
}
}
I’m creating a Pet and Save+Update+Delete it all in the same session.
The second:
public void SaveWithoutFlushShouldDontPersist()
{
object savedId;
using (ISession session = factory.OpenSession())
{
var animal = new Pet();
savedId=session.Save(animal);
animal.Description = "Boa";
}

using (ISession session = factory.OpenSession())
{
var found = session.Get<Pet>(savedId);
if(found != null)
log.Debug("A Pet was found even if the session was never flushed.");
}
}
In the first session I’m saving a Pet without flush the session and in the second session I’m going to DB to ensure that the Pet was not saved.
In the first demo I will explain the meaning of the messages in the follow, when you see the same sequence, you can apply the same explication.
Ok, now I’m ready to begin the dance.

Using guid.comb or "guid

The “id” mapping here is:
<id type="guid">
<generator class="guid.comb"/>
</id>
Main demo results without use batcher:
Saving
Saved

Committing
INSERT INTO Person ... 'PatnerOfPerson'
INSERT INTO Person ... 'Person'
INSERT INTO Pet ... 'Reptile'
INSERT INTO Pet ... 'Dog'
INSERT INTO Pet ... 'Cat'
Committed
Between “Saving” and “Saved” there is NO iteration with RDBMS, all thing happens during the nh-Transaction commit. In this case we are going five times to DB (one per command).
AllInOneSession results :
There is absolutely nothing nor a single log message.
Save without flush results:
Nothing.
Main demo results with BATCHER:
Saving
Saved

Committing
Opened new IDbCommand, open IDbCommands: 1
1)Building an IDbCommand object for the SqlString: INSERT INTO Person (Name, Partner, id) VALUES (?, ?, ?)
...
2)Executing batch
Batch command: INSERT INTO Person ... 'PatnerOfPerson'
Batch command: INSERT INTO Person ... 'Person'
Closed IDbCommand
Opened new IDbCommand, open IDbCommands: 1
3)Building an IDbCommand object for the SqlString: INSERT INTO Pet (Description, ownerId, id) VALUES (?, ?, ?)
...
4)Executing batch
Batch command: INSERT INTO Pet ... 'Reptile'
Batch command: INSERT INTO Pet ... 'Dog'
Batch command: INSERT INTO Pet ... 'Cat'
Closed IDbCommand
Committed
Another time all happen during nh-transaction commit and what happen is:
  1. A command to insert People (note the plural) is created and prepared.
  2. In only one command the two persons are inserted.
  3. A command to insert Pets (note the plural) is created and prepared.
  4. In only one command the three animals are inserted.
We are doing the same insertions but going to DB only two times instead of five.

Using “hilo”

The “id” mapping here is:
<idtype="int">
    <
generatorclass="hilo"/>
</
id>
Main demo results without use batcher:
Saving
Reading high value:select next_hi from hibernate_unique_key with (updlock, rowlock)
Updating high value:update hibernate_unique_key set next_hi = @p0 where next_hi = @p1
The New high value is: 2
Saved

Committing
INSERT INTO Person ... 'PatnerOfPerson'
INSERT INTO Person ... 'Person'
INSERT INTO Pet ... 'Reptile'
INSERT INTO Pet ... 'Dog'
INSERT INTO Pet ... 'Cat'
Committed
Between “Saving” and “Saved” there is one iteration with RDBMS to retrieve the High value. Note that in a fresh new RDBMS you will see one more iteration to initialize “hibernate_unique_key” table with the first high-value. If you repeat the demo inside the same session-factory instance nothing happen between “Saving”-“Saved”  for long time (if I well remember the default max-low is Int16.MaxValue).
AllInOneSession results :
Nothing.
Save without flush results:
Nothing.
Main demo results with BATCHER:
Saving
Reading high value:select next_hi from hibernate_unique_key with (updlock, rowlock)
Updating high value:update hibernate_unique_key set next_hi = @p0 where next_hi = @p1
The New high value is: 2
Saved

Committing
Opened new IDbCommand, open IDbCommands: 1
Building an IDbCommand object for the SqlString: INSERT INTO Person (Name, Partner, id) VALUES (?, ?, ?)
...
Executing batch
Batch command: INSERT INTO Person ... 'PatnerOfPerson'
Batch command: INSERT INTO Person ... 'Person'
Closed IDbCommand
Opened new IDbCommand, open IDbCommands: 1
Building an IDbCommand object for the SqlString: INSERT INTO Pet (Description, ownerId, id) VALUES (?, ?, ?)
...
Executing batch
Batch command: INSERT INTO Pet ... 'Reptile'
Batch command: INSERT INTO Pet ... 'Dog'
Batch command: INSERT INTO Pet ... 'Cat'
Closed IDbCommand
Committed
The same behavior of “guid” (only two round-trips).

Using “identity”

As last your best friend Identity (or “native” if you are using Microsoft SQL server).
The “id” mapping here is:
<id type="int">
<generator class="identity"/>
</id>

And now, my friend, fasten your seat belt because I’m going to show you what happen in the roller-coaster.
Main demo results without use batcher:
Saving
INSERT INTO Person ...; select SCOPE_IDENTITY(); … 'PatnerOfPerson'
INSERT INTO Person ... ; select SCOPE_IDENTITY(); … 'Person'
INSERT INTO Pet ... ; select SCOPE_IDENTITY();… 'Reptile'
INSERT INTO Pet ... ; select SCOPE_IDENTITY(); … 'Dog'
INSERT INTO Pet ... ; select SCOPE_IDENTITY(); … 'Cat'
Saved

Committing
Committed
As you can see the behavior is exactly the inverse of what you see using “guid”: here all happen during saving instead during commit. Each “INSERT INTO” ran in the same moment of session.Save(object) even if we don’t close the UnitOfWork and, to be more exactly, even if we don’t know what we will do with the UnitOfWork (we don’t know if we will close with commit or rollback). Five iteration with RDBMS are happening during UnitOfWork usage instead at the end of the UnitOfWork (exactly what we want avoid using UoW pattern).
AllInOneSession results :
INSERT INTO Pet (Description, ownerId) VALUES (@p0, @p1); select SCOPE_IDENTITY.
Ups… something appear here and it is something really bad.
Save without flush results:
INSERT INTO Pet (Description, ownerId) VALUES (@p0, @p1); select SCOPE_IDENTITY()
A Pet was found even if the session was never flushed.
WOW! We are having a record in the DB even if we don’t flush the session ?
Yes, nothing so strange if you are using identity-style generators.
Main demo results with BATCHER:
And now let me show you how much is useful the batcher when you are working with identity-style generators.
Saving

Opened new IDbCommand, open IDbCommands: 1
Building an IDbCommand object for the SqlString: INSERT INTO Person (Name, Partner) VALUES (?, ?); select SCOPE_IDENTITY()
INSERT INTO Person ...; select SCOPE_IDENTITY(); ... 'PatnerOfPerson'
Opened IDataReader, open IDataReaders: 1
...
Closed IDbCommand
Opened new IDbCommand, open IDbCommands: 1
Building an IDbCommand object for the SqlString: INSERT INTO Person (Name, Partner) VALUES (?, ?); select SCOPE_IDENTITY()
INSERT INTO Person ...; select SCOPE_IDENTITY(); ...'Person'
Opened IDataReader, open IDataReaders: 1
...
Closed IDbCommand, open IDbCommands: 0
Opened new IDbCommand, open IDbCommands: 1
Building an IDbCommand object for the SqlString: INSERT INTO Pet (Description, ownerId) VALUES (?, ?); select SCOPE_IDENTITY()
INSERT INTO Pet ...; select SCOPE_IDENTITY(); ...'Reptile'
Opened IDataReader, open IDataReaders: 1
...
Closed IDbCommand, open IDbCommands: 0
Opened new IDbCommand, open IDbCommands: 1
Building an IDbCommand object for the SqlString: INSERT INTO Pet (Description, ownerId) VALUES (?, ?); select SCOPE_IDENTITY()
INSERT INTO Pet ...; select SCOPE_IDENTITY(); ... 'Dog'
Opened IDataReader, open IDataReaders: 1
...
Closed IDbCommand, open IDbCommands: 0
Opened new IDbCommand, open IDbCommands: 1
Building an IDbCommand object for the SqlString: INSERT INTO Pet (Description, ownerId) VALUES (?, ?); select SCOPE_IDENTITY()
INSERT INTO Pet ...; select SCOPE_IDENTITY(); ... 'Cat'
Opened IDataReader, open IDataReaders: 1
...
Closed IDbCommand, open IDbCommands: 0
Saved
Committing
Committed
Do you see ? … try to see again… Do you see ?
The target of the Batcher should be: Have better performance and less round-trips. Using identity-style generators, the benefits of the batcher are nullified and, obviously, don’t forget the rollback of the transaction because you don’t have another chance to nullify what happen in the session.Save().

Conclusions

This time I leave the conclusions to you; please let me know leaving a comment here ;)


kick it on DotNetKicks.com

20 February 2009

Diving in NHibernate.Validator

Surfing in the NET, to find some NHibernate.Validator (NHV) example, I saw that there are various things not so clear about how NHV is working. In this post I’ll try to give you a more deep explication.

Class validation definition

In these examples I will use the follow simple class :

public class Customer
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string CreditCard { get; set; }
}

Using Attributes the definition look like the follow:

public class Customer
{
[Length(Min = 3, Max = 20)]
public string FirstName { get; set; }

[Length(Min=3, Max = 60)]
public string LastName { get; set; }

[CreditCardNumber]
public string CreditCard { get; set; }
}

Using XML mapping the configuration is :

<class name="Customer">
<
property name="FirstName">
<
length min="3" max="20"/>
</
property>
<
property name="LastName">
<
length min="3" max="60"/>
</
property>
<
property name="CreditCard">
<
creditcardnumber/>
</
property>
</
class>

NOTE: In this example I will use the NHV convention for XML Validation-Definition that mean the mapping file is an embedded resource, is in the same folder (namespace) of the class and its name is the name of the class followed by “.nhv.xml” (in this case Customer.nhv.xml). 

Using fluent-interface configuration :

public class CustomerDef: ValidationDef<Customer>
{
public CustomerDef()
{
Define(x => x.FirstName).LengthBetween(3, 20);
Define(x => x.LastName).LengthBetween(3, 60);
Define(x => x.CreditCard).IsCreditCardNumber();
}
}

As you can see you have 3 ways to define validation constraints for a class. For each class, you must use at least one validation definition and at most two; this mean that you can even mix the “Attribute way” with one of the “External” ways (here “external” mean that the validation is defined out-side the class).

The ValidatorEngine

At first, the ValidatorEngine, is your entry-point. If you are using Attributes, you can do something like this:

public void WithOutConfigureTheEngine()
{
var customer = new Customer { FirstName = "F", LastName = "Fermani" };
var ve = new ValidatorEngine();
Assert.That(ve.IsValid(customer), Is.False);
}

What happen behind the scene is:

JITClassMappingFactory:Reflection applied for Customer
ReflectionClassMapping:For class Customer Adding member FirstName to dictionary with attribute LengthAttribute
ReflectionClassMapping:For class Customer Adding member LastName to dictionary with attribute LengthAttribute
ReflectionClassMapping:For class Customer Adding member CreditCard to dictionary with attribute CreditCardNumberAttribute

As you can see NHV is investigating the class to know all attributes. Now the same example using two instances of ValidatorEngine

var customer = new Customer { FirstName = "F", LastName = "Fermani" };
var ve1 = new ValidatorEngine();
Assert.That(ve1.IsValid(customer), Is.False);
var ve2 = new ValidatorEngine();
Assert.That(ve2.IsValid(customer), Is.False);

What happen behind is:

JITClassMappingFactory:Reflection applied for Customer
ReflectionClassMapping:For class Customer Adding member FirstName to dictionary with attribute LengthAttribute
ReflectionClassMapping:For class Customer Adding member LastName to dictionary with attribute LengthAttribute
ReflectionClassMapping:For class Customer Adding member CreditCard to dictionary with attribute CreditCardNumberAttribute

JITClassMappingFactory:Reflection applied for Customer
ReflectionClassMapping:For class Customer Adding member FirstName to dictionary with attribute LengthAttribute
ReflectionClassMapping:For class Customer Adding member LastName to dictionary with attribute LengthAttribute
ReflectionClassMapping:For class Customer Adding member CreditCard to dictionary with attribute CreditCardNumberAttribute

Ups… NHV is investigating the same class two times.

Now again the same example but using only one ValidatorEngine instance:

var customer1 = new Customer { FirstName = "F", LastName = "Fermani" };
var ve = new ValidatorEngine();
Assert.That(ve.IsValid(customer1), Is.False);
var customer2 = new Customer { FirstName = "Fabio", LastName = "Fermani" };
Assert.That(ve.IsValid(customer2), Is.True);

Here we are validating two instances of Customer class using the same ValidatorEngine and what happen behind is:

JITClassMappingFactory:Reflection applied for Customer
ReflectionClassMapping:For class Customer Adding member FirstName to dictionary with attribute LengthAttribute
ReflectionClassMapping:For class Customer Adding member LastName to dictionary with attribute LengthAttribute
ReflectionClassMapping:For class Customer Adding member CreditCard to dictionary with attribute CreditCardNumberAttribute

As you can see, the class Customer, was investigated only one time, NHV are using reflection only one time.

Conclusion: For performance issue, the ValidatorEngine instance should have the same lifecycle of your application.

The XML convention

As you probably know, I like, very much, all framework complying with rule “DON’T TOUCH MY CODE” (more quite “no invasive framework”). With NHV you can define an “external” XML file as validation definition. The convention, come in place, when you configure the ValidatorEngine to use an “External” source for validation-definitions. The configuration in the application config file is:

    <nhv-configuration xmlns='urn:nhv-configuration-1.0'>
<
property name='default_validator_mode'>UseExternal</property>
</
nhv-configuration>

Given the above first class and embedding the file Customer.nhv.xml in the same namespace, I’m going to run the follow test:

var ve = new ValidatorEngine();
ve.Configure();

var customer1 = new Customer { FirstName = "F", LastName = "Fermani" };
Assert.That(ve.IsValid(customer1), Is.False);
var customer2 = new Customer { FirstName = "Fabio", LastName = "Fermani" };
Assert.That(ve.IsValid(customer2), Is.True);

What happen behind the scene is:

JITClassMappingFactory: - XML convention applied for Customer
XmlClassMapping: - Looking for rules for property : FirstName
XmlClassMapping: - Adding member FirstName to dictionary with attribute LengthAttribute
XmlClassMapping: - Looking for rules for property : LastName
XmlClassMapping: - Adding member LastName to dictionary with attribute LengthAttribute
XmlClassMapping: - Looking for rules for property : CreditCard
XmlClassMapping: - Adding member CreditCard to dictionary with attribute CreditCardNumberAttribute

As you can see, this time, the JITClassMappingFactory (JIT = Just In Time), are using XmlClassMapping instead ReflectionClassMapping. Again the validation definition investigation will be done only one time per ValidatorEngine instance.

If you use XML you can even work without use the convention; in this case you must provide a more complete nhv-configuration section declaring where are mappings files.

The Fluent-Interface

Details about configuration via fluent-interface are available here. To complete the example series, the test is this:

var config = new FluentConfiguration();
config
.SetDefaultValidatorMode(ValidatorMode.UseExternal)
.Register<CustomerDef, Customer>();
var ve = new ValidatorEngine();
ve.Configure(config);

var customer1 = new Customer { FirstName = "F", LastName = "Fermani" };
Assert.That(ve.IsValid(customer1), Is.False);
var customer2 = new Customer { FirstName = "Fabio", LastName = "Fermani" };
Assert.That(ve.IsValid(customer2), Is.True);

What happen behind is:

OpenClassMapping:- For class Customer Adding member FirstName to dictionary with attribute LengthAttribute
OpenClassMapping:- For class Customer Adding member LastName to dictionary with attribute LengthAttribute
OpenClassMapping:- For class Customer Adding member CreditCard to dictionary with attribute CreditCardNumberAttribute
StateFullClassMappingFactory:- Adding external definition for Customer

Here the JITClassMappingFactory don’t is working; NHV is using the StateFullClassMappingFactory during configuration, the JITClassMappingFactory will come in play only after configuration-time. Again the validation definition investigation will be done only one time per ValidatorEngine instance.

The SharedEngineProvider

Perhaps this is the real motivation of this post. As I said above, the ValidatorEngine, should have the same lifecycle of your application, the validation is a cross-cutting-concern and you need to use it from different tiers. In my opinion the better definition of SharedEngineProvider is :

The SharedEngineProvider is the service locator for the ValidatorEngine.

If you are using NHibernate.Validator, especially with its integration with NHibernate, you should define an implementation of ISharedEngineProvider to ensure that, in all your tiers, you are using exactly the same constraints and to avoid more than one ValidatorEngine instances.

The interface is really trivial:

/// <summary>
///
Contract for Shared Engine Provider
/// </summary>
public interface ISharedEngineProvider
{
/// <summary>
///
Provide the shared engine instance.
/// </summary>
/// <returns>
The validator engine.</returns>
ValidatorEngine GetEngine();
}

To configure the SharedEngineProvider you can use the application config or the NHibernate.Validator.Cfg.Environment class before any other task (regarding NHV). Any other configuration will be ignored (in fact you don’t have anything to configure the SharedEngineProvider trough FluentConfiguration).

A good way to implements a SharedEngineProvider is using your preferred IoC container, or using CommonServiceLocator. The SharedEngineProvider is used, where available, by the two listeners for NHibernate integration. A generic configuration look like:

    <nhv-configuration xmlns='urn:nhv-configuration-1.0'>
<
shared_engine_provider class='NHibernate.Validator.Event.NHibernateSharedEngineProvider, NHibernate.Validator'/>
</
nhv-configuration>

You should change the class if you want use your own implementation of ISharedEngineProvider. The usage of your own implementation is strongly recommended especially in WEB and even more if you are using an IoC container.



kick it on DotNetKicks.com

15 February 2009

The light… working

Do you remember this post ?

Well… somebody else saw the light!!!

Now you have AjGenesis and the new prj using MGrammar to generate all artifacts.

NHibernate.Validator: fluent-interface configuration

As announced in the previous post NHibernate.Validator (NHV for friends) has its own embedded configuration based on fluent-interface (Loquacious for friends). This new feature will be general-available with the next version NHV1.2.0.

The NHV configuration

As test and because the new configuration is “Loquacious” enough I’m going to expose some examples without explication.

Example 1:

var configure = new FluentConfiguration();
configure.Register(
Assembly.GetExecutingAssembly().GetTypes()
.Where(t => t.Namespace.Equals("Company.Product.YourNamespace"))
.ValidationDefinitions())
.SetDefaultValidatorMode(ValidatorMode.UseExternal);

ve = new ValidatorEngine();
ve.Configure(configure);

Example 2:

var configure = new FluentConfiguration();
configure.Register(
Assembly.Load("Company.Product")
.ValidationDefinitions()
.Where(t => t.Namespace.Equals("Company.Product.YourNamespace"))
)
.SetDefaultValidatorMode(ValidatorMode.UseExternal)
.IntegrateWithNHibernate.ApplyingDDLConstraints().And.RegisteringListeners();

ve = new ValidatorEngine();
ve.Configure(configure);

The only thing you can’t configure, using FluentConfiguration, is the SharedEngineProvider because it is configurable only trough application config (by the way, from what I saw on the NET, an explication about what is the SharedEngineProvider is needed).

For the configuration, I have add two extensions methods, both named ValidationDefinitions(), to Assembly and to IEnumerable<System.Type>.

The configuration of class validation

Example v1:

public AddressDef()
{
Define(x => x.Country)
.MaxLength(20).And
.NotNullable();
Define(x => x.floor)
.IncludedBetween(-2, 50).WithMessage("{floor.out.of.range}");
Define(x => x.Id)
.IncludedBetween(1, 2000);
Define(x => x.Line1)
.NotNullable();
Define(x => x.State)
.NotNullable().And
.MaxLength(3);
Define(x => x.Zip)
.NotNullable().And
.MaxLength(5).WithMessage("{long}").And
.MatchWith("[0-9]+");
Define(x => x.InternalValid)
.IsTrue();
}

What happen at design-time ? Some images are more clear than 100 words…

DateTimePropVSIntegerPropVS  DecimalPropVS StrPropVSEnumerablePropVSEntityRelationPropVS  

The first advantage of fluent-interface-configuration appear clear: a very little example is that you can’t define an integer property as not-nullable.

For instance validators here is the example using Attributes and its equivalent using fluent-configuration:

Example v2:
[AssertAnimal]
public class Suricato
{
}

public class SuricatoDef:ValidationDef<Suricato>
{
public SuricatoDef()
{
ValidateInstance.Using(new AssertAnimalAttribute());
}
}

Extending constraints configuration

Who know NHV know that we have some country-specific validators. Country-specific-validator is a clear example about how extend the framework.

The follow is the implementation of NHibernate.Validator.Specific.It (validators available for Italy):

public static class ItLoquaciousExtensions
{
public static IRuleArgsOptions IsCodiceFiscale(this IStringConstraints definition)
{
return ((IConstraints)definition).AddWithFinalRuleArgOptions(new CodiceFiscaleAttribute());
}

public static IRuleArgsOptions IsPartitaIva(this IStringConstraints definition)
{
return ((IConstraints)definition).AddWithFinalRuleArgOptions(new PartitaIvaAttribute());
}

public static IRuleArgsOptions IsPartitaIva(this IIntegerConstraints definition)
{
return ((IConstraints)definition).AddWithFinalRuleArgOptions(new PartitaIvaAttribute());
}
}

And obviously, at design-time, it appear like this:

It_IntegerPropVS

Conclusions

Sometimes NHV is not so well know by NHibernate users (NHV has 5% of NH downloads). If you are using some other validation framework, in applications where you are using NHibernate, you should check if the validator are initializing collections and relations (proxy)… perhaps we should write something else about how NHV work together with NHibernate.



kick it on DotNetKicks.com

13 February 2009

NHV.Loquacious : fluent-configuration for NHV

Loquacious is the name of the new fluent-configuration embedded in NHibernate.Validator.

public class AddressValidationDef: ValidationDef<Address>
{
public AddressValidationDef()
{
Define(x => x.Country)
.NotNullable().And
.MaxLength(5);
Define(x => x.Zip)
.MaxLength(5).WithMessage("{long}").And
.MatchWith("[0-9]+");
}
}

The thread about the API still open so, if you have some advise you can leave your opinion here or there.

One peculiar characteristic of NHibernate.Validator.Cfg.Loquacious is that was implemented avoiding XML generation; this mean that we are using NHV’s APIs to create all metadata needed by the ValidatorEngine.

I start this work only for fun and now I can make public my considerations about fluent-configuration.

IMO

“people use XML because they can’t use something else”: FALSE. I use XML because it is the more flexible and less intrusive than other “conf-ways”. A modern developer can’t avoid the knowledge of XML.

“fluent-configuration support Refactoring when XML don’t”: FALSE. Refactoring is not a matter of the language you are using to configure; the Refactoring is an IDE functionality (Eclipse users know this fact). The problem in .NET world is that, for us, is more easy the implementation of “fluent-configuration” than a VisualStudio/Resharper plug-in (check this or this for example).

“fluent-configuration is more readable”: Well… let me say that some fluent-configuration APIs are absolutely not more readable than XML.

“implements fluent-configuration is a big challenge”: basing the opinion in this experience I can say that is FALSE; it was a work of few hours (around a 5% or 6% than XML configuration).

Conclusions

That said, fluent-configuration is a useful tool but it is absolutely not indispensable to evaluate a framework usability.


kick it on DotNetKicks.com

07 February 2009

NH2.1.0: New generators

This post is an recognition that we had lost the fight with Identity's fans. As you probably know I don’t like Identity, but as a NHibernate-developer I can’t ignore ours users requests.
This is the complete list of POID (Persistent Object IDentifier) generators provided by NHibernate2.1.0:
  • native
  • identity (improved)
  • sequence
  • hilo
  • seqhilo
  • assigned
  • guid
  • guid.comb
  • guid.native (new)
  • select (new)
  • sequence-identity (new)
  • trigger-identity (new)
  • uuid.hex
  • uuid.string
  • counter
  • increment
  • foreign

guid.native

Is a new generator that allow to use the RDBMS native function to generate GUID. The behavior is similar to the “sequence” generator: when a new object is saved NH run two query; the first to retrieve the GUID value and the second to insert the entity using the Guid retrieved from RDBMS. The type in your entity is System.Guid and the SQLtype depend from the dialect (RAW(16) in Oracle, UniqueIdentifier in MsSQL for example).
Queries that runs in ORACLE are:
  1. select rawtohex(sys_guid()) from dual
  2. INSERT INTO MyEntityTable (Id, Description) VALUES (:p0, :p1)
The  parameter “:p0” has the value retrieved in the first query.

sequence-identity

The “sequence-identity” is based on “sequence” but work as an “identity”. The POID values is retrieved with the INSERT query. The types, in your entity, maybe are System.Int32 or System.Int64 depending on your RDBMS sequence generator.
The query that run in ORACLE is:
INSERT INTO my_entity (id, name) VALUES (hibernate_sequence.nextval, :p0) returning id into :nhIdOutParam
The “hibernate_sequence” is the default name for a sequence where no alternative name is provided trough the mapping. As you can see, in this case, the “sequence” are working like “identity”, the value of the POID is retrieved immediately and the generator has the same problem of “identity”.

trigger-identity

The “trigger-identity” is a NHibernate specific feature where the POID is generated by the RDBMS at the INSERT query through a BEFORE INSERT trigger. In this case you can use any supported type, including custom type, with the limitation of “single-column” (so far).
The query in ORACLE is:
INSERT INTO my_entity (Name) VALUES (:p0) returning Id into :nhIdOutParam
As you can see the query is very similar to the query used to work with “identity”; the “Id” field is not present in the FieldsNameList nor in VALUES list and the value of the POID is retrieved immediately. What the trigger are doing to generate the “Id” field value is out-side of NH scope.

select

The “select” generator is a deviation of the “trigger-identity”. This generator work together with natural-id feature. The difference “trigger-identity” is that the POID value is retrieved by a SELECT using the natural-id fields as filter. In practice giving
<class name="MyEntity" table="my_entity">
<id name="id">
<generator class="select"/>
</id>
<natural-id>
<property name="name"/>
</natural-id>
</class>

and having a trigger to generate the POID, the queries runs in ORACLE are:
  1. INSERT INTO my_entity (name) VALUES (:p0)
  2. SELECT id FROM my_entity WHERE name = :p0
The POID still retrieved immediately.

identity

The “identity” generator is well known by NH<->MsSQL users but, before NH2.1.0, can’t be used for others RDBMS if the RDBMS don’t support native identity-generator. What happen if you have one multi-RDBMS-application and your DBA want use an identity-style generator in each RDBMS ? Which is your work with mappings files for NHibernate ? Well… we have changed the meaning of “identity”. In NH2.1.0 defining <generator class="identity"/> your are saying : “I want work with an identity-style generator; check my dialect to know which is the correct generator for identity”.
By default, when you specify “identity”, NH run the follow:
if (SupportsIdentityColumns)
{
return typeof(IdentityGenerator);
}
else if (SupportsSequences)
{
return typeof(SequenceIdentityGenerator);
}
else
{
return typeof(TriggerIdentityGenerator);
}
If you need a different behavior you can inherit from the default dialect, for your RDBMS, and override the property IdentityStyleIdentifierGeneratorClass.

Conclusion

Now you have a more easy way to break the unit-of-work pattern, and to nullify the batcher, for all NH’s supported dialects: specify “identity” as your identifier generator.


kick it on DotNetKicks.com