Using well-known instance types

Most applications contain a set of static relational data, such as a list of countries, states, credit card types, and others. The application does not need to waste time retrieving this static data from the database; it never changes. In this recipe, we will show you how you can use the well-known instance type from the unofficial NHibernate AddIns project to avoid this unnecessary work.

How to do it…

  1. Create a new class library project named WKITExample.
  2. Install the NHibernate package using the NuGet Package Manager Console by executing the following command:
    Install-Package NHibernate
    
  3. Add the following GenericWellKnownInstanceType class:
    [Serializable]
    public abstract class GenericWellKnownInstanceType<T, TId> 
      IUserType where T : class
    {
    
      private Func<T, TId, bool> findPredicate;
      private Func<T, TId> idGetter;
      private IEnumerable<T> repository;
    
      protected GenericWellKnownInstanceType(
        IEnumerable<T> repository,
        Func<T, TId, bool> findPredicate,
        Func<T, TId> idGetter)
      {
        this.repository = repository;
        this.findPredicate = findPredicate;
        this.idGetter = idGetter;
      }
    
      public Type ReturnedType
      {
        get { return typeof(T); }
      }
    
      public bool IsMutable
      {
        get { return false; }
      }
    
      public new bool Equals(object x, object y)
      {
        if (ReferenceEquals(x, y))
        {
          return true;
        }
        if (ReferenceEquals(null, x) || 
          ReferenceEquals(null, y))
        {
          return false;
        }
    
        return x.Equals(y);
      }
    
      public int GetHashCode(object x)
      {
        return (x == null) ? 0 : x.GetHashCode();
      }
    
      public object NullSafeGet(IDataReader rs, 
        string[] names, object owner)
      {
        int index0 = rs.GetOrdinal(names[0]);
        if (rs.IsDBNull(index0))
        {
          return null;
        }
    
        var value = (TId)rs.GetValue(index0);
        return repository.FirstOrDefault(x => 
          findPredicate(x, value));
      }
    
      public void NullSafeSet(IDbCommand cmd, 
        object value, int index)
      {
        if (value == null)
        {
          ((IDbDataParameter)cmd.Parameters[index])
            .Value = DBNull.Value;
        }
        else
        {
          ((IDbDataParameter)cmd.Parameters[index])
            .Value = idGetter((T)value);
        }
      }
    
      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;
      }
    
      /// <summary>
      /// The SQL types for the columns 
      /// mapped by this type. 
      /// </summary>
      public abstract SqlType[] SqlTypes { get; }
    
    }
  4. Add the following StateType class:
    public class StateType 
      : GenericWellKnownInstanceType<State, string>
    {
    
      private static readonly SqlType[] sqlTypes = 
        new[] { SqlTypeFactory.GetString(2)};
    
      public StateType()
        : base(new States(), 
        (entity, id) => entity.PostalCode == id,
        entity => entity.PostalCode)
      { }
    
      public override SqlType[] SqlTypes
      {
        get { return sqlTypes; }
      }
    
    }
  5. Add the following State class:
    [Serializable]
    public class State
    {
    
      public virtual string PostalCode { get; private set; }
      public virtual string Name { get; private set; }
    
      internal State(string postalCode, string name)
      {
        PostalCode = postalCode;
        Name = name;
      }
    
    }
  6. Add the following States collection class:
    public class States : ReadOnlyCollection<State>
    {
    
      public static State Arizona = new State("AZ", "Arizona");
      public static State California = 
        new State("CA", "California");
      public static State Colorado = new State("CO", "Colorado");
      public static State Oklahoma = new State("OK", "Oklahoma");
      public static State NewMexico = 
        new State("NM", "New Mexico");
      public static State Nevada = new State("NV", "Nevada");
      public static State Texas = new State("TX", "Texas");
      public static State Utah = new State("UT", "Utah");
    
      public States()
        : base(new State[] { Arizona, California, Colorado, 
          Oklahoma, NewMexico, Nevada, Texas, Utah })
      { }
    
    }
  7. Add an Address class using the following properties:
    public virtual Guid Id { get; set; }
    public virtual string Line1 { get; set; }
    public virtual string Line2 { get; set; }
    public virtual string City { get; set; }
    public virtual State State { get; set; }
    public virtual string Zip { get; set; }
  8. Add the following mapping document:
    <?xml version="1.0" encoding="utf-8" ?>
    <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
        assembly="WKITExample"
        namespace="WKITExample">
      <typedef 
        class="WKITExample.StateType, WKITExample" 
        name="State"/>
      <class name="Address">
        <id name="Id">
          <generator class="guid.comb" />
        </id>
        <property name="Line1" not-null="true" />
        <property name="Line2" />
        <property name="City" not-null="true" />
        <property name="State" type="State" not-null="true" />
        <property name ="Zip" not-null="true" />
      </class>
    </hibernate-mapping>

How it works…

In this recipe, we have an Address entity with a State property. Suppose we have a requirement to print the state's postal abbreviation on shipping labels, but we need to display the full state name when the user completes an order. It would be a waste of resources to fetch these State entities from the database each time.

GenericWellKnownInstanceType allows us to create a static list of States in our application, and use them with our Address entity. We use the PostalCode property to uniquely identify it in the list. In the database, this postal code value is stored in the State field of Address. When NHibernate loads an Address from the database, it attaches the appropriate State instance to the State property. In this way, State works just like an entity. This is handled by the StateType class, which implements IUserType. When loading an Address, the StateType class is responsible for reading the abbreviation from the raw data and returning the correct State instance. Similarly, when we save an address, it translates the State instance to the abbreviation stored in the Address table.

When inheriting from GenericWellKnownInstanceType, we must provide the following four items:

  • A collection of all the well-known instances; this is our states collection
  • A predicate to locate the correct well-known instance given a database value
  • A delegate that returns the database value from a well-known instance
  • The type of database field used to store this database value: in this case, a two-character string field

The unofficial NHibernate AddIns project also includes a WellKnownInstanceType, which specifies a 32-bit integer database value.

See also

  • Creating an encrypted string type
  • Mapping enumerations
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.117.232.239