Try fast search NHibernate

15 February 2009

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