Sign Up

Sign Up to our social questions and Answers Engine to ask questions, answer people’s questions, and connect with other people.

Have an account? Sign In

Have an account? Sign In Now

Sign In

Login to our social questions & Answers Engine to ask questions answer people’s questions & connect with other people.

Sign Up Here

Forgot Password?

Don't have account, Sign Up Here

Forgot Password

Lost your password? Please enter your email address. You will receive a link and will create a new password via email.

Have an account? Sign In Now

You must login to ask a question.

Forgot Password?

Need An Account, Sign Up Here

Please briefly explain why you feel this question should be reported.

Please briefly explain why you feel this answer should be reported.

Please briefly explain why you feel this user should be reported.

Sign InSign Up

The Archive Base

The Archive Base Logo The Archive Base Logo

The Archive Base Navigation

  • SEARCH
  • Home
  • About Us
  • Blog
  • Contact Us
Search
Ask A Question

Mobile menu

Close
Ask a Question
  • Home
  • Add group
  • Groups page
  • Feed
  • User Profile
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Buy Points
  • Users
  • Help
  • Buy Theme
  • SEARCH
Home/ Questions/Q 7043179
In Process

The Archive Base Latest Questions

Editorial Team
  • 0
Editorial Team
Asked: May 28, 20262026-05-28T02:15:28+00:00 2026-05-28T02:15:28+00:00

First let me apologize a bit for the length of this post, it’s mostly

  • 0

First let me apologize a bit for the length of this post, it’s mostly code though so I hope you all bear with me!

I have a scenario in dealing with a legacy database, where I needed to write an IUserType using NHibernate 3.2 to take a 2 character “status” field and return a Boolean value from it. The status field can hold 3 possible values:

* 'DI'     // 'Disabled', return false
* '  '     // blank or NULL, return true
* NULL     

Here is what I have simplified.

Table Definition:

CREATE TABLE [dbo].[Client](
    [clnID] [int] IDENTITY(1,1) NOT NULL,
    [clnStatus] [char](2) NULL,
    [clnComment] [varchar](250) NULL,
    [clnDescription] [varchar](150) NULL,
    [Version] [int] NOT NULL
)

Fluent Mapping:

public class ClientMapping : CoreEntityMapping<Client>
{
    public ClientMapping()
    {
        SchemaAction.All().Table("Client");
        LazyLoad();

        Id(x => x.Id, "clnId").GeneratedBy.Identity(); 
        Version(x => x.Version).Column("Version").Generated.Never().UnsavedValue("0").Not.Nullable();
        OptimisticLock.Version();

        Map(x => x.Comment, "clnComment").Length(250).Nullable();
        Map(x => x.Description, "clnDescription").Length(250).Nullable();
        Map(x => x.IsActive, "clnStatus").Nullable().CustomType<StatusToBoolType>();
    }
}

My IUserType Implementation:

public class StatusToBoolType : IUserType
{
    public bool IsMutable { get { return false; } }
    public Type ReturnedType { get { return typeof(bool); } }
    public SqlType[] SqlTypes { get {  return new[] { NHibernateUtil.String.SqlType }; } }

    public object DeepCopy(object value)
    {
        return value;
    }
    public object Replace(object original, object target, object owner)
    {
        return original;
    }
    public object Assemble(object cached, object owner)
    {
        return cached;
    }
    public object Disassemble(object value)
    {
        return value;
    }

    public new bool Equals(object x, object y)
    {
        if (ReferenceEquals(x, y)) return true;
        if (x == null || y == null) return false;
           return x.Equals(y);
    }
    public int GetHashCode(object x)
    {
        return x == null ? typeof(bool).GetHashCode() + 473 : x.GetHashCode();
    }

    public object NullSafeGet(IDataReader rs, string[] names, object owner)
    {
        var obj = NHibernateUtil.String.NullSafeGet(rs, names[0]);
        if (obj == null) return true;

        var status = (string)obj;
        if (status == "  ") return true;
        if (status == "DI") return false;
        throw new Exception(string.Format("Expected data to be either empty or 'DI' but was '{0}'.", status));
    }

    public void NullSafeSet(IDbCommand cmd, object value, int index)
    {
        var parameter = ((IDataParameter) cmd.Parameters[index]);
        var active = value == null || (bool) value;
        if (active)
            parameter.Value = "  ";
        else
            parameter.Value = "DI";
    }
}

However this doesn’t work. This unit test fails with an inaccurate count.

[TestMethod]
public void GetAllActiveClientsTest()
{
    //ACT
    var count = Session.QueryOver<Client>()
        .Where(x => x.IsActive)
        .SelectList(l => l.SelectCount(x => x.Id))
        .FutureValue<int>().Value;

    //ASSERT
    Assert.AreNotEqual(0, count);
    Assert.AreEqual(1721, count);
}

The reason it fails is because it generates the following SQL:

SELECT count(this_.clnID) as y0_ FROM Client this_ WHERE this_.clnstatus = @p0;
/* @p0 = '  ' [Type: String (0)] */

But I need it to generate this instead:

SELECT count(this_.clnID) as y0_ FROM Client this_ WHERE (this_.clnstatus = @p0 <b> OR this_.clnstatus IS NULL);</b>

After some debugging I saw that the NullSafeSet() method in my StatusToBoolType class is invoked before the query is generated, so I was able to get around this by writing some hackish code in that method to manipulate the SQL in the cmd.CommandText property.

...
public void NullSafeSet(IDbCommand cmd, object value, int index)
{
    var parameter = ((IDataParameter) cmd.Parameters[index]);
    var active = value == null || (bool) value;
    if (active)
    {
        parameter.Value = "  ";

        if (cmd.CommandText.ToUpper().StartsWith("SELECT") == false) return;
        var paramindex = cmd.CommandText.IndexOf(parameter.ParameterName);
        if (paramindex > 0)
        {
            // Purpose: change [columnName] = @p0  ==> ([columnName] = @p0 OR [columnName] IS NULL) 
            paramindex += parameter.ParameterName.Length;
            var before = cmd.CommandText.Substring(0, paramindex);
            var after = cmd.CommandText.Substring(paramindex);

            //look at the text before the '= @p0' and find the column name...
            var columnSection = before.Split(new[] {"= " + parameter.ParameterName}, StringSplitOptions.RemoveEmptyEntries).Reverse().First();
            var column = columnSection.Substring(columnSection.Trim().LastIndexOf(' ')).Replace("(", "");
            var myCommand = string.Format("({0} = {1} OR {0} IS NULL)", column.Trim(), parameter.ParameterName);

            paramindex -= (parameter.ParameterName.Length + column.Length + 1);
            var orig = before.Substring(0, paramindex);
            cmd.CommandText = orig + myCommand + after;
        }
    }
    else
        parameter.Value = "DI";
}

But this is NHibernate!!! Hacking the sql statement like this can’t possibly be the correct way to handle this? Right?

Because it is a shared legacy database, I can’t change the table schema to NOT NULL otherwise I would have just done that, and avoided this scenario.

So finally after all this prelude my question is simply this, where can I tell NHibernate to generate a custom SQL criteria statement for this IUserType?

Thank you all in advance!

  • 1 1 Answer
  • 0 Views
  • 0 Followers
  • 0
Share
  • Facebook
  • Report

Leave an answer
Cancel reply

You must login to add an answer.

Forgot Password?

Need An Account, Sign Up Here

1 Answer

  • Voted
  • Oldest
  • Recent
  • Random
  1. Editorial Team
    Editorial Team
    2026-05-28T02:15:28+00:00Added an answer on May 28, 2026 at 2:15 am

    Solved it!

    After I posted my question I went back to the drawing board, and I came up with a solution that doesn’t require hacking the generated SQL in the IUserType implementation. In fact this solution doesn’t need the IUserType at all!

    Here is what I did.

    First, I changed the IsActive column to use a formula to handle the null checking. This fixed my issue with the QueryOver failing, because now everytime NHibernate deals with IsActive property it injects my sql formula to handle null.

    The downside to this approach was that after I put in the formula all of my save tests failed. It turns out that formula properties are effectively ReadOnly properties.

    So to get around this issue, I added a protected property to the entity to hold the status value from the database.

    Next, I changed the IsActive property to set the protected status property to ” ” or “DI”. And finally I changed the FluentMapping to Reveal the protected Status property to NHibernate so that NHibernate can track it. Now that NHibernate is aware of Status it can include it on its INSERT/UPDATE statements.

    I am going to include my solution below in case anyone else is interested.

    Client class

    public class Client 
    {
        ...
    
        protected virtual string Status { get; set; }
        private bool _isActive;
        public virtual bool IsActive
        {
            get { return _isActive; }
            set
            {
                _isActive = value;
                Status = (_isActive) ? "  " : "DI";
            }
        }
    }
    

    Changes to Fluent Mapping

    public class ClientMapping : CoreEntityMapping<Client>
    {
        public ClientMapping()
        {
            ....
    
            Map(Reveal.Member<E>("Status"), colName).Length(2);
            Map(x => x.IsActive).Formula("case when clnStatus is null then '  ' else clnStatus end");
        }
    }
    
    • 0
    • Reply
    • Share
      Share
      • Share on Facebook
      • Share on Twitter
      • Share on LinkedIn
      • Share on WhatsApp
      • Report

Sidebar

Related Questions

First of all let me apologize for the wall of code. Basically, I have
First of all, let me apologize if this question is too broad. I'm looking
First of all I have to apologize for my poor English, Please let me
First let me say I have read this useful article thoroughly and am using
First off, let me apologize if this has been asked already, but I can’t
Let me first apologize if this question could sound perhaps sort of amateurish for
Ok. First let me apologize profusely if this question has been covered. I did
First of all, I apologize for yet another interface question. I thought that this
First, let me apologize for asking a question that may seem a bit vague
First let me apologize for the scale of this problem but I'm really trying

Explore

  • Home
  • Add group
  • Groups page
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Users
  • Help
  • SEARCH

Footer

© 2021 The Archive Base. All Rights Reserved
With Love by The Archive Base

Insert/edit link

Enter the destination URL

Or link to existing content

    No search term specified. Showing recent items. Search or use up and down arrow keys to select an item.