I'm stuck in a simple problem, I'd like to add a new custom restriction in NHibernate. I want to write a simple QueryOver with a fulltext index, an example with a Projections is here How to use Full Text Search for any property with QueryOver API
But I need a more flexibility so I'd like something like
criteria = criteria.WhereRestrictionOn(() => table.COLUMN_WITHFULLTEXTINDEX).Contains(valueToCheck);
Is it possible? I'm trying in the latest to days surfing over the NHibernate source code but I could't get anything usefull.
Thanks
CodePudding user response:
Since I must manage two different dbs dialect (SQL SERVER and ORACLE) I made the following.
A class that has all the custom criterions, now only one for full text
/// <summary>
/// Full custom criterions
/// </summary>
public static class CustomCriterions
{
static CustomCriterions()
{
ExpressionProcessor.RegisterCustomMethodCall(() => FullTextSearch(null, ""), ProcessFullTextSearch);
}
/// <summary>
/// Only a dummy method to force the static constructor <see cref="CustomCriterions"/>
/// </summary>
public static void Register()
{
}
public static bool FullTextSearch(this string objectProperty, string valueToCheck)
{
throw new Exception("Not to be used directly - use inside QueryOver expression");
}
private static ICriterion ProcessFullTextSearch(MethodCallExpression mce)
{
var arg0 = ExpressionProcessor.FindMemberProjection(mce.Arguments[0]).AsProjection();
var arg1 = ExpressionProcessor.FindMemberProjection(mce.Arguments[1]).AsProjection();
var projection = Projections.SqlFunction("contains",
NHibernateUtil.Boolean,
arg0,
arg1);
return new FullTextCriterion(projection);
}
}
Then a class who have to manage custom criterion
/// <summary>
/// Custom criterion to have a full text search
/// </summary>
public class FullTextCriterion : AbstractCriterion
{
private readonly IProjection _projection;
public FullTextCriterion(IProjection projection)
{
_projection = projection;
}
public override TypedValue[] GetTypedValues(ICriteria criteria, ICriteriaQuery criteriaQuery)
{
var typedValues = new List<TypedValue>();
if (_projection != null)
{
typedValues.AddRange(_projection.GetTypedValues(criteria, criteriaQuery));
}
typedValues.Add(GetParameterTypedValue(criteria, criteriaQuery));
return typedValues.ToArray();
}
private TypedValue GetParameterTypedValue(ICriteria criteria, ICriteriaQuery criteriaQuery)
{
return CriterionUtil.GetTypedValues(criteriaQuery, criteria, _projection, null).Single();
}
public override IProjection[] GetProjections()
{
return new[] { _projection };
}
public override string ToString()
{
return _projection.ToString();
}
public override SqlString ToSqlString(ICriteria criteria, ICriteriaQuery criteriaQuery)
{
var columnNames = CriterionUtil.GetColumnNamesForSimpleExpression(
null, _projection, criteriaQuery, criteria, this, string.Empty);
return DbUtil.GetFullText(columnNames);
}
}
FullTextCriterion isn't strictly necessary but the ORACLE syntax is
CONTAINS (a, b)>0
So I must to add the ">0".
DbUtil build the syntax by the dialect, e.g. in ORACLE
public SqlString GetFullText(SqlString[] columnNames) {
var sqlBuilder = new SqlStringBuilder(4 * columnNames.Length);
sqlBuilder.Add(columnNames[0]);
sqlBuilder.Add("> 0");
return sqlBuilder.ToSqlString();
}
Without using FullTextCriterion for ORACLE dialect I could use more simplest solutions that use custom projection instead custom criterion: NHibernate QueryOver Coalesce a property to another property
CodePudding user response:
You can do something like below:
Expression<Func<ProductsEntity, bool>> where = (x => x.MyProperty == "my value"));
...
...
session.Query<ProductsEntity>().Where(where)....
Alternatively, you can build the Junction
conditionally like below:
Junction where = Restrictions.Conjunction();
if(string.IsNullOrEmpty(myInputValue) == false)
where.Add(Restrictions.Eq(Projections.Property<ProductsEntity>(x => x.MyProperty), myInputValue));