in

 

Ray Houston

March 2008 - Posts

  • Creating a Timestamp Interceptor in NHibernate

    In a previous post, I gave an example of a Timestamp class and how one might create an ICompositeUserType to map it within NHibernate. Here I want to show of an example of an IInterceptor which will automatically populate the values for my Timestamp class. OnSave is for the inserts, and OnFlushDirty is for the updates. There are a bunch of other methods that you can tap into for different things, so check out the NHibernate docs.
     
    public class TimestampInterceptor : EmptyInterceptor
    {
        private const string TIMESTAMP_PROPERTY = "Timestamp";
    
        private readonly IDomainContext domainContext;
        private readonly ISystemClock clock;
    
        public TimestampInterceptor(IDomainContext domainContext, ISystemClock clock)
        {
            this.domainContext = domainContext;
            this.clock = clock;
        }
    
        public override bool OnSave(object entity, object id, object[] state, 
            string[] propertyNames, IType[] types)
        {
            var timestampable = entity as ITimestampable;
    
            if(timestampable == null)
                return false;
    
            var timestamp = GetTimestamp(state, propertyNames);
    
            timestamp.CreatedDateTime = clock.Now();
    
            if (domainContext.DomainUser != null)
                timestamp.CreatedByStaff = domainContext.DomainUser.StaffName;
    
            return true;
        }
    
        public override bool OnFlushDirty(object entity, object id, object[] currentState, 
            object[] previousState, string[] propertyNames, IType[] types)
        {
            var timestampable = entity as ITimestampable;
    
            if (timestampable == null)
                return false;
    
            var timestamp = GetTimestamp(currentState, propertyNames);
    
            timestamp.UpdatedDateTime = clock.Now();
    
            if(domainContext.DomainUser != null)
                timestamp.UpdatedByStaff = domainContext.DomainUser.StaffName;
    
            return true;
        }
    
        private static Timestamp GetTimestamp(object[] state, string[] propertyNames)
        {
            var timestamp = state[Array.IndexOf(propertyNames, TIMESTAMP_PROPERTY)] as Timestamp;
    
            if( timestamp == null )
            {
                timestamp = new Timestamp();
                state[Array.IndexOf(propertyNames, TIMESTAMP_PROPERTY)] = timestamp;
            }
    
            return timestamp;
        }
    }
     
    I haven't run this through the ringers yet, so let me know if you spot some problems.
    Technorati Tags: ,
  • A Simple Closure To Handle Try/Catch Around Transactions

    (Updated: I moved the begin transaction outside of the try as Chad suggested in the comments.)

    If you're like me, you're lazy and hate putting try/catch around your transaction handling in your code. It has to be there, but it's just a pain. You may have code that looks something like:

    domainContext.BeginTransaction();
    
    try
    {
        historicalPwdService.RecordHistoricalPassword(user.UserProfileID, currentPassword);
    
        user.Password = newPassword;
        user.PasswordCreateDate = systemClock.Now();
    
        userProfileRepo.Save(user);
    
        domainContext.CommitTransaction();
    }
    catch
    {
        domainContext.RollbackTransaction();
        throw;
    }
    

    Well here's a little helper class that can ease the pain a bit:

    public static class WorkUnit
    {
        public static void Do(IDomainContext context, Action workUnit)
        {
            context.BeginTransaction();
    
            try
            {
                workUnit();
                    
                context.CommitTransaction();
            }
            catch
            {
                context.RollbackTransaction();
    
                throw;
            }
        }
    }
    

    now you can use it with an anonymous method:

    WorkUnit.Do(domainContext, ()=>
        {
            historicalPwdService.RecordHistoricalPassword(user.UserProfileID, currentPassword);
    
            user.Password = newPassword;
            user.PasswordCreateDate = systemClock.Now();
    
            userProfileRepo.Save(user);
        });
    

    Side note for Jimmy: You should be proud. I went back and switched out my custom delegate for Action. ;)

    Technorati Tags: ,
  • Mapping Timestamp Data Using NHibernate's ICompositeUserType

    In my previous post, I took some string data and mapped it directly to a boolean property on an entity. That was pretty simple, but I wanted to try it out on a little more complex object..

    In our projects, most of the entities have a Timestamp property which is of type Timestamp:

    public class User
    {
        public string UserID { get; set; }
        public string Username { get; set; }
        public string Password { get; set; }
        public Timestamp Timestamp { get; set; }
    }

    and here's the Timestamp class:

    public class Timestamp
    {
        public string CreatedByStaff { get; set; }
        public string UpdatedByStaff { get; set; }
        public DateTime? CreatedDateTime { get; set; }
        public DateTime? UpdatedDateTime { get; set; }
    }
    

    In the database, the USER table would have the obvious columns for UserID, Username, and Password, but also have the columns for the Timestamp which in my case are CREATED_BY_STAFF, UPDATED_BY_STAFF, CREATED_DATETIME, and UPDATED_DATETIME. Now this can easily be mapped as it is by using a component like so:

    <component name="Timestamp" class="XYZ.Core.Timestamp, XYZ.Core">
      <property name="CreatedByStaff" type="String" column="CREATED_BY_STAFF" not-null="true"/>
      <property name="UpdatedByStaff" type="String" column="UPDATED_BY_STAFF" not-null="false"/>
      <property name="CreatedDateTime" type="DateTime" column="CREATED_DATETIME" not-null="true"/>
      <property name="UpdatedDateTime" type="DateTime" column="UPDATED_DATETIME" not-null="false"/>
    </component>
    

    That works well, but I hate having to repeat that all over the place. I would rather just have one single line that maps a Timestamp. I experimented with creating a custom mapping type using the ICompositeUserType and got a little closer to the goal. Here's the class:

    public class TimestampMappingType : ICompositeUserType
    {
        public bool IsMutable
        {
            get { return true; }
        }
    
        public Type ReturnedClass
        {
            get { return typeof(Timestamp); }
        }
    
        public string[] PropertyNames
        {
            get { return new[] { "CreatedByStaff", "UpdatedByStaff", "CreatedDateTime", "UpdatedDateTime" }; }
        }
    
        public IType[] PropertyTypes
        {
            get { return new[] { NHibernateUtil.String, NHibernateUtil.String, NHibernateUtil.DateTime, NHibernateUtil.DateTime}; }
        }
    
        public object Assemble(object cached, ISessionImplementor session, object owner)
        {
            return DeepCopy(cached);
        }
    
        public object GetPropertyValue(object component, int property)
        {
            var timestamp = AsTimestamp(component);
    
            switch(property)
            {
                case 0:
                    return timestamp.CreatedByStaff;
                case 1:
                    return timestamp.UpdatedByStaff;
                case 2:
                    return timestamp.CreatedDateTime;
                case 3:
                    return timestamp.UpdatedDateTime;
                default:
                    throw new YourException("No implementation for property index of '{0}'.", property);
            }
        }
    
        public void SetPropertyValue(object component, int property, object value)
        {
            if (component == null)
                throw new ArgumentNullException("component");
    
            var timestamp = AsTimestamp(component);
            switch (property)
            {
                case 0:
                    timestamp.CreatedByStaff = (string)value;
                    break;
                case 1:
                    timestamp.UpdatedByStaff = (string)value;
                    break;
                case 2:
                    timestamp.CreatedDateTime = (DateTime?)value;
                    break;
                case 3:
                    timestamp.UpdatedDateTime = (DateTime?)value;
                    break;
                default:
                    throw new YourException("No implementation for property index of '{0}'.", property);
            }           
        }
    
        public object NullSafeGet(IDataReader dr, string[] names, ISessionImplementor session, object owner)
        {
            var createdByStaff = NHibernateUtil.String.NullSafeGet(dr, names[0]);
            var updatedByStaff = NHibernateUtil.String.NullSafeGet(dr, names[1]);
            var createdDateTime = NHibernateUtil.DateTime.NullSafeGet(dr, names[2]);
            var updatedDateTime = NHibernateUtil.DateTime.NullSafeGet(dr, names[3]);
    
            return new Timestamp
            {
                CreatedByStaff = (string)createdByStaff,
                UpdatedByStaff = (string)updatedByStaff,
                CreatedDateTime = (DateTime?)createdDateTime,
                UpdatedDateTime = (DateTime?)updatedDateTime
            };
        }
    
        public void NullSafeSet(IDbCommand cmd, object value, int index, ISessionImplementor session)
        {
            if (value == null)
            {
                ((IDataParameter)cmd.Parameters[index]).Value = DBNull.Value;
                ((IDataParameter)cmd.Parameters[index+1]).Value = DBNull.Value;
                ((IDataParameter)cmd.Parameters[index+2]).Value = DBNull.Value;
                ((IDataParameter)cmd.Parameters[index+3]).Value = DBNull.Value;
            }
            else
            {
                var timestamp = AsTimestamp(value);
    
                ((IDataParameter)cmd.Parameters[index]).Value = (object)timestamp.CreatedByStaff ?? DBNull.Value;
                ((IDataParameter)cmd.Parameters[index + 1]).Value = (object)timestamp.UpdatedByStaff ?? DBNull.Value;
                ((IDataParameter)cmd.Parameters[index + 2]).Value = (object)timestamp.CreatedDateTime ?? DBNull.Value;
                ((IDataParameter)cmd.Parameters[index + 3]).Value = (object)timestamp.UpdatedDateTime ?? DBNull.Value;
            }
        }
    
        public object DeepCopy(object value)
        {
            if(value == null) return null;
    
            var original = AsTimestamp(value);
    
            return new Timestamp
                       {
                            CreatedByStaff = original.CreatedByStaff, 
                            UpdatedByStaff = original.UpdatedByStaff,
                            CreatedDateTime = original.CreatedDateTime,
                            UpdatedDateTime = original.UpdatedDateTime
                        };
        }
    
        public object Disassemble(object value, ISessionImplementor session)
        {
            return DeepCopy(value);
        }
    
        public object Replace(object original, object target, ISessionImplementor session, object owner)
        {
            return DeepCopy(original);
        }
    
        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(Timestamp).GetHashCode() + 321 : x.GetHashCode();
        }
    
        private static Timestamp AsTimestamp(object value)
        {
            if (value == null) return null;
    
            var ts = value as Timestamp;
    
            if(ts == null)
                throw new YourException("Expected '{0}' but recieved '{1}'.", typeof(Timestamp), value.GetType());
    
            return ts;
        }
    }

    Using this class in my mapping, I can now shorten the Timestamp mapping to:

    <property name="Timestamp" type="XYZ.DataAccess.TimestampMappingType, XYZ.DataAccess">
      <column name="CREATED_BY_STAFF" />
      <column name="UPDATED_BY_STAFF" />
      <column name="CREATED_DATETIME" />
      <column name="UPDATED_DATETIME" />
    </property>
    

    That's a little better but I was hoping I could get away with not having to define the columns (they're the same on every table) but I don't see a way to set defaults (maybe I'm in the wrong place?). In the end, I'm not sure this really buys me much more than just mapping Timestamp as a component, but I'll poke around a little more to see if I can figure it out.

    Technorati Tags: ,
  • Mapping Strings to Booleans Using NHibernate's IUserType

    Update: I failed to realized that the functionality of converting "Y" or "N" to a boolean is already built into NHibernate by doing type="YesNo" (see comments). I'll leave this post up just for the academics of creating a IUserType. Go figure that I would come up with something that's built in!

    We have some legacy data where boolean values are stored as strings. "Y" is true and "N" is no. We're also creating entities which have properties that map to this data. It would be just plain evil to have to create all the boolean properties as strings! We previously solved this problem by creating our own .NET type that had a string property for the "Y" or "N" value. This type had an implicit cast defined as a bool, so we could treat it as a boolean in expressions and assignments. We mapped this in NHibernate as a component with the single string property and this worked pretty well, but I suspected that there was a better way. Today I bought the e-book version of NHibernate in Action and quickly read through the section on creating an implementation of IUserType. I then came up with the following class that allows me to keep my types as true booleans in the code.

    public class YesNoType : IUserType
    {
        public bool IsMutable
        {
            get { return false; }
        }
    
        public Type ReturnedType
        {
            get { return typeof(YesNoType); }
        }
    
        public SqlType[] SqlTypes
        {
            get { return new[]{NHibernateUtil.String.SqlType}; }
        }
    
        public object NullSafeGet(IDataReader rs, string[] names, object owner)
        {
            var obj = NHibernateUtil.String.NullSafeGet(rs, names[0]);
    
            if(obj == null ) return null;
    
            var yesNo = (string) obj;
    
            if( yesNo != "Y" && yesNo != "N" )
                throw new YourException("Expected data to be 'Y' or 'N' but was '{0}'.", yesNo);
    
            return yesNo == "Y";
        }
    
        public void NullSafeSet(IDbCommand cmd, object value, int index)
        {
            if(value == null)
            {
                ((IDataParameter) cmd.Parameters[index]).Value = DBNull.Value;
            }
            else
            {
                var yes = (bool) value;
                ((IDataParameter)cmd.Parameters[index]).Value = yes ? "Y" : "N";
            }
        }
    
        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();
        }
    }
    

    The mapping looks something like:

    <property name="Active" type="NHTypes.YesNoType, NHTypes" column="ACTIVE_YN" not-null="false" />

    I'm not going to go into detail and explain what all the parts are and do (see link), but I thought that I would share the class. Please let me know if you spot any problems.

    Technorati Tags: ,
  • PTOM: The Interface Segregation Principle

    In following suite with the The Los Techies Pablo's Topic of the Month - March: SOLID Principles, I chose to write a little about the The Interface Segregation Principle (ISP). As Chad pointed out with LSP, the ISP is also one of Robert 'Uncle Bob' Martin's S.O.L.I.D design principles.

    Basically ISP tells us that clients shouldn't be forced to implement interfaces they don't use. In other words, if you have an abstract class or an interface, then the implementers should not be forced to implement parts that they don't care about.

    I was having trouble thinking of a real world example for ISP but then was reminded about implementing a custom Membership Provider in ASP.NET 2.0. I had completely blocked that monstrosity out of my mind (for good reason).

    The Membership Provider was a way to integrate with some of the ASP.NET's built in management of users and its associated server controls. For me, it ended up being a lot more trouble than it was worth, but it turns out to be a good example of a fat interface. In order to implement your own Membership Provider you "simply" implement the abstract class MembershipProvider like so:

    public class CustomMembershipProvider : MembershipProvider
    {
        public override string ApplicationName
        {
            get
            {
                throw new Exception("The method or operation is not implemented.");
            }
            set
            {
                throw new Exception("The method or operation is not implemented.");
            }
        }
    
        public override bool ChangePassword(string username, string oldPassword, string newPassword)
        {
            throw new Exception("The method or operation is not implemented.");
        }
    
        public override bool ChangePasswordQuestionAndAnswer(string username, string password, 
            string newPasswordQuestion, string newPasswordAnswer)
        {
            throw new Exception("The method or operation is not implemented.");
        }
    
        public override MembershipUser CreateUser(string username, string password, string email, 
            string passwordQuestion, string passwordAnswer, bool isApproved, object providerUserKey, 
            out MembershipCreateStatus status)
        {
            throw new Exception("The method or operation is not implemented.");
        }
    
        public override bool DeleteUser(string username, bool deleteAllRelatedData)
        {
            throw new Exception("The method or operation is not implemented.");
        }
    
        public override bool EnablePasswordReset
        {
            get { throw new Exception("The method or operation is not implemented."); }
        }
    
        public override bool EnablePasswordRetrieval
        {
            get { throw new Exception("The method or operation is not implemented."); }
        }
    
        public override MembershipUserCollection FindUsersByEmail(string emailToMatch, int pageIndex, 
            int pageSize, out int totalRecords)
        {
            throw new Exception("The method or operation is not implemented.");
        }
    
        public override MembershipUserCollection FindUsersByName(string usernameToMatch, int pageIndex, 
            int pageSize, out int totalRecords)
        {
            throw new Exception("The method or operation is not implemented.");
        }
    
        public override MembershipUserCollection GetAllUsers(int pageIndex, int pageSize, out int totalRecords)
        {
            throw new Exception("The method or operation is not implemented.");
        }
    
        public override int GetNumberOfUsersOnline()
        {
            throw new Exception("The method or operation is not implemented.");
        }
    
        public override string GetPassword(string username, string answer)
        {
            throw new Exception("The method or operation is not implemented.");
        }
    
        public override MembershipUser GetUser(string username, bool userIsOnline)
        {
            throw new Exception("The method or operation is not implemented.");
        }
    
        public override MembershipUser GetUser(object providerUserKey, bool userIsOnline)
        {
            throw new Exception("The method or operation is not implemented.");
        }
    
        public override string GetUserNameByEmail(string email)
        {
            throw new Exception("The method or operation is not implemented.");
        }
    
        public override int MaxInvalidPasswordAttempts
        {
            get { throw new Exception("The method or operation is not implemented."); }
        }
    
        public override int MinRequiredNonAlphanumericCharacters
        {
            get { throw new Exception("The method or operation is not implemented."); }
        }
    
        public override int MinRequiredPasswordLength
        {
            get { throw new Exception("The method or operation is not implemented."); }
        }
    
        public override int PasswordAttemptWindow
        {
            get { throw new Exception("The method or operation is not implemented."); }
        }
    
        public override MembershipPasswordFormat PasswordFormat
        {
            get { throw new Exception("The method or operation is not implemented."); }
        }
    
        public override string PasswordStrengthRegularExpression
        {
            get { throw new Exception("The method or operation is not implemented."); }
        }
    
        public override bool RequiresQuestionAndAnswer
        {
            get { throw new Exception("The method or operation is not implemented."); }
        }
    
        public override bool RequiresUniqueEmail
        {
            get { throw new Exception("The method or operation is not implemented."); }
        }
    
        public override string ResetPassword(string username, string answer)
        {
            throw new Exception("The method or operation is not implemented.");
        }
    
        public override bool UnlockUser(string userName)
        {
            throw new Exception("The method or operation is not implemented.");
        }
    
        public override void UpdateUser(MembershipUser user)
        {
            throw new Exception("The method or operation is not implemented.");
        }
    
        public override bool ValidateUser(string username, string password)
        {
            throw new Exception("The method or operation is not implemented.");
        }
    }
    

    Holy guacamole! That's a lot of stuff. Sorry for the code puke there, but wanted you to feel a little pain as I did trying to actually implement this thing. Hopefully you didn't get tired of scrolling through that and you're still with me. ;)

    It turns out that you don't have to implement the parts you don't need, but this clearly violates the Interface Segregation Principle. This interface is extremely fat and not cohesive. A better approach would have been to break it up into smaller interfaces that allow the implementers to only worry about the parts that they need. I'm not going to go into the details of splitting this up, but I think you get the idea.

    Since I cannot think of another real world example, let's look at a completely bogus example. Say you have the following code:

    public abstract class Animal
    {
        public abstract void Feed();
    }
    
    public class Dog : Animal
    {
        public override void Feed()
        {
            // do something
        }
    }
    
    public class Rattlesnake : Animal
    {
        public override void Feed()
        {
            // do something
        }
    }
    

    But then you realize that you have a need for some of the animals to be treated as pets and have them groomed. You may be tempted to do

    public abstract class Animal
    {
        public abstract void Feed();
        public abstract void Groom();
    }
    

    which would be fine for the Dog, but it may not be fine for the Rattlesnake (although I'm sure there is some freako out there that grooms their pet rattlesnake)

    public class Rattlesnake : Animal
    {
        public override void Feed()
        {
            // do something
        }
    
        public override void Groom()
        {
            // ignore - I'm not grooming a freaking rattlesnake
        }
    }
    

    Here we have violated the ISP by polluting our Animal interface. This requires us to implement a method that doesn't make sense for the Rattlesnake object. A better choice would to implement an IPet interface which just Dog could implement and without affecting Rattlesnake. You might end up with something like this:

    public interface IPet
    {
        void Groom();
    }
    
    public abstract class Animal
    {
        public abstract void Feed();
    }
    
    public class Dog : Animal, IPet
    {
        public override void Feed()
        {
            // do something
        }
    
        public void Groom()
        {
            // do something
        }
    }
    
    public class Rattlesnake : Animal
    {
        public override void Feed()
        {
            // do something
        }
    }
    

    I think the key is if you find yourself creating interfaces that don't get fully implemented in its clients, then that's a good sign that you're violating the ISP. You can check out the link to this pdf for more complete information on the subject.

    Technorati Tags: ,,
    Posted Mar 14 2008, 11:04 PM by Ray Houston with 8 comment(s)
    Filed under:
Copyright Los Techies 2007. All rights reserved.
Powered by Community Server (Commercial Edition), by Telligent Systems