NHibernate: come si chiama quell’Alias?
e che questo dominio sta dietro un servizio WCF che espone un set di operazioni tra cui una di questo genere:
L’inghippo è che il vostro scopo è quello di permettere al client di esprimere una possibile query un po’ come se manipolasse direttamente gli “ICriteria” di NHibernate che però ovviamente non possono, e non avrebbe senso, varcare i confini del servizio.///
/// Finds all the branches that satisfies the given query.
///
/// "culture">The culture.
/// "queryDto">The query dto.
///
/// A list of branches that satisfies the given criteria.
///
[OperationContract( Name = "FindBranchesByQuery" )]
BranchDto[] FindBranches( String culture, BranchesSearchQueryDto queryDto );
Quello che potete fare è modellare un dominio per esprimere le query, naturalmente semplificato e pensato per il caso d’uso, fatto ad esempio in questo modo:
nulla di trascendentale, triviale direi. Quello che si può notare è che tutti implementano l’interfaccia ICriterionBuilder che è definita così:
Il metodo Build prende in pasto i Criteria correnti e ritorna un ICriterion che rappresenta il “Criteria” che ha viaggiato attraverso i confini del servizio.interface ICriterionBuilder
{
///
/// Builds the NHibernate ICriterion that maps this query criteria.
///
/// "criteria">The criteria.
///The new ICriterion.
IEnumerableICriterion> Build( ICriteria criteria );
}
Perchè Build() ha bisogno di un ICriteria?
per un motivo abbastanza semplice, un elemento della nostra query potrebbe aver bisogno di un “association path” di esprimere cioè una query ad esempio su un elemento di una collection esposta da un’entità, una join insomma… questo è ovviamente un problema:
Voglio cercare tutte le “branch” tra i cui attributi ce ne siano uno che si chiama “Services” e che abbia valore “foo” o valore “bar”Per il client questa cosa si esprime così:
Quando vi arriva al servizio quello che avete bisogno di fare è trasformare il tutto in un ICriteria la cosa è però complicata dalla necessità di risalire all’eventuale Alias che è stato attribuito all’association path espresso dagli AttributesCriteria.using( var svc = new SearchServiceClient() )
{
var query = new BranchesSearchQueryDto()
{
AttributesCriteria = new Criteria[]
{
new DisjunctionCriteria()
{
OrCriteria = new Criteria[]
{
new AttributeCriteria()
{
Name="Services",
QueryText="foo"
},
new AttributeCriteria()
{
Name="Services",
QueryText="bar"
}
}
}
}
};
var result = svc.FindBranchesByQuery( "it-IT", query );
}
Si può fare…
In maniera anche decisamente semplice:
Il giochetto sta semplicemente nel chiedere al criteria corrente un “sub criteria” sulla base di un path e se non esiste crearlo e infine recuperarne l’alias.public override IEnumerableICriterion> Build( ICriteria criteria )
{
var alias = this.GetAttributesAlias( criteria );
var junction = new Disjunction();
junction.Add( Restrictions.Eq( alias + ".Name", this.Name ) );
if( this.Behavior == CompareBehavior.ExactMatch )
{
junction.Add( Restrictions.Eq( alias + ".Value", this.QueryText ) );
}
else
{
junction.Add( Restrictions.Like( alias + ".Value", this.QueryText.AsSqlServerKeyword(), MatchMode.Exact ) );
}
yield return junction;
}
String GetAttributesAlias( ICriteria criteria )
{
const String alias = "attributes";
const String path = "Attributes";
var attributeCriteria = criteria.GetCriteriaByPath( path );
if( attributeCriteria == null )
{
criteria.CreateAlias( "Attributes", alias );
attributeCriteria = criteria.GetCriteriaByPath( path );
}
return attributeCriteria.Alias;
}
Ogni volta che ho a che fare con NHibernate sono sempre più affascinato, prossima tappa i Future
.m