0
votes

If I don't map Color but map an object that has a Color attribute, FluentNHibernate successfully maps it to a varbinary(max). However, that's extremely inefficient given that realistically Color is just composed of 4 bytes and I am keen on improving it without using a new type to Proxy it.

Internally within Color it is made up of four properties,

  1. long value (cast down to int to represent ARGB)
  2. short state, indicates if this is a known & valid colour
  3. string name, the name of the colour if known.
  4. short knownColor a value to indicate which known colour it is

So I have attempted to map this as follows.

public ColorMapping()
{
    CompositeId()
        .KeyProperty(c => Reveal.Member<Color, long>("value"))
        .KeyProperty(c => Reveal.Member<Color, short>("state"))
        .KeyProperty(c => Reveal.Member<Color, string>("name"))
        .KeyProperty(c => Reveal.Member<Color, short>("knownColor"));
}

However, on usage I get the following exception,

Class Initialization method DataContextTest.ClassInitialise threw exception. FluentNHibernate.Cfg.FluentConfigurationException: FluentNHibernate.Cfg.FluentConfigurationException: An invalid or incomplete configuration was used while creating a SessionFactory. Check PotentialReasons collection, and InnerException for more detail.

---> FluentNHibernate.Cfg.FluentConfigurationException: An invalid or incomplete configuration was used while creating a SessionFactory. Check PotentialReasons collection, and InnerException for more detail.

---> NHibernate.MappingException: Could not compile the mapping document: (XmlDocument) ---> NHibernate.MappingException: Could not determine type for: System.Linq.Expressions.Expression1[[System.Func2[[System.Drawing.Color, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a],[System.Int64, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, for columns: NHibernate.Mapping.Column(Member).

Am I misusing Reveal? If so, how am I meant to use it?

2
What is the contents of PotentialReasons? You may need to load the assembly first.Sebazzz
@Sebazzz PotentialReasons Count = 0M Afifi

2 Answers

1
votes

i would map it as UserType which converts it back and forth

// in mapping
Map(x => x.Color).Column("ColorARGB").CustomType<ColorUserType>();

[Serializable]
class ColorUserType : IUserType
{
    public object Assemble(object cached, object owner)
    {
        return DeepCopy(cached);
    }

    public object DeepCopy(object value)
    {
        return value;
    }

    public object Disassemble(object value)
    {
        return DeepCopy(value);
    }

    bool IUserType.Equals(object x, object y)
    {
        var colorX = x as Color;
        var colorY = y as Color;
        return colorX == null ? colorY = null : colorX.ToArgb() == colorY.ToArgb();
    }

    public virtual int GetHashCode(object x)
    {
        var colorX = (Color)x;
        return (colorX != null) ? colorX.ToArgb() : 0;
    }

    public bool IsMutable { get { return false; } }

    public object NullSafeGet(IDataReader rs, string[] names, object owner)
    {
        return Color.FromArgb((int)NHibernateUtil.Int32.Get(rs, names[0]));
    }

    public void NullSafeSet(IDbCommand cmd, object value, int index)
    {
        NHibernateUtil.Int32.Set(cmd, ((Color)value).ToArgb(), index);
    }

    public object Replace(object original, object target, object owner)
    {
        return original;
    }

    public Type ReturnedType { get { return typeof(Color); } }

    public SqlType[] SqlTypes { get { return new []{ SqlTypeFactory.Int32 }; } }
}
0
votes

I went with an ICompositeUserType in the end. Code as follows,

public class ColorType : ICompositeUserType
{
    /// <summary>
    /// Get the value of a property
    /// </summary>
    /// <param name="component">an instance of class mapped by this "type"</param>
    /// <param name="property"/>
    /// <returns>
    /// the property value
    /// </returns>
    public object GetPropertyValue(object component, int property)
    {
        var color = (Color) component;
        if (property == 0)
        {
            return color.ToArgb();
        }

        return (int) color.ToKnownColor();
    }

    /// <summary>
    /// Set the value of a property
    /// </summary>
    /// <param name="component">an instance of class mapped by this "type"</param>
    /// <param name="property"/>
    /// <param name="value">the value to set</param>
    public void SetPropertyValue(object component, int property, object value)
    {
        throw new InvalidOperationException("Color is immutable");
    }

    /// <summary>
    /// Compare two instances of the class mapped by this type for persistence
    ///             "equality", ie. equality of persistent state.
    /// </summary>
    /// <param name="x"/><param name="y"/>
    /// <returns/>
    public new bool Equals(object x, object y)
    {
        return ReferenceEquals(x, y) ||
               x != null && y != null &&
               object.Equals(x, y);
    }

    /// <summary>
    /// Get a hashcode for the instance, consistent with persistence "equality"
    /// </summary>
    public int GetHashCode(object x)
    {
        return x == null
                   ? 0
                   : x.GetHashCode();
    }

    /// <summary>
    /// Retrieve an instance of the mapped class from a IDataReader. Implementors
    ///             should handle possibility of null values.
    /// </summary>
    /// <param name="dr">IDataReader</param>
    /// <param name="names">the column names</param>
    /// <param name="session"/>
    /// <param name="owner">the containing entity</param>
    /// <returns/>
    public object NullSafeGet(IDataReader dr, string[] names,
        ISessionImplementor session, object owner)
    {
        var argb = (int?) NHibernateUtil.Int32.NullSafeGet(dr, names[0]);
        var knownColor = (int?) NHibernateUtil.Int32.NullSafeGet(dr, names[1]);
        return knownColor != null
            ? Color.FromKnownColor((KnownColor) knownColor.Value)
            : Color.FromArgb(argb.Value);
    }

    /// <summary>
    /// Write an instance of the mapped class to a prepared statement.
    ///             Implementors should handle possibility of null values.
    ///             A multi-column type should be written to parameters starting from index.
    ///             If a property is not settable, skip it and don't increment the index.
    /// </summary>
    /// <param name="cmd"/>
    /// <param name="value"/>
    /// <param name="index"/>
    /// <param name="settable"/>
    /// <param name="session"/>
    public void NullSafeSet(IDbCommand cmd, object value, int index,
        bool[] settable, ISessionImplementor session)
    {
        var color = (Color) value;
        if (color.IsKnownColor)
        {
            ((IDataParameter) cmd.Parameters[index]).Value = DBNull.Value;
            ((IDataParameter) cmd.Parameters[index + 1]).Value = (int) color.ToKnownColor();
        }
        else
        {
            ((IDataParameter) cmd.Parameters[index]).Value = color.ToArgb();
            ((IDataParameter) cmd.Parameters[index + 1]).Value = DBNull.Value;
        }
    }

    /// <summary>
    /// Return a deep copy of the persistent state, stopping at entities and at collections.
    /// </summary>
    /// <param name="value">generally a collection element or entity field</param>
    /// <returns/>
    public object DeepCopy(object value)
    {
        return value;
    }

    /// <summary>
    /// Transform the object into its cacheable representation.
    ///             At the very least this method should perform a deep copy.
    ///             That may not be enough for some implementations, 
    ///             method should perform a deep copy. That may not be enough for 
    ///             some implementations, however; for example, associations must 
    ///             be cached as identifier values. (optional operation)
    /// </summary>
    /// <param name="value">the object to be cached</param>
    /// <param name="session"/>
    /// <returns/>
    public object Disassemble(object value, ISessionImplementor session)
    {
        return value;
    }

    /// <summary>
    /// Reconstruct an object from the cacheable representation.
    ///             At the very least this method should perform a deep copy. (optional operation)
    /// </summary>
    /// <param name="cached">the object to be cached</param>
    /// <param name="session"/>
    /// <param name="owner"/>
    /// <returns/>
    public object Assemble(object cached, ISessionImplementor session, object owner)
    {
        return cached;
    }

    /// <summary>
    /// During merge, replace the existing (target) value in the entity we are merging to
    ///             with a new (original) value from the detached entity we are merging. For immutable
    ///             objects, or null values, it is safe to simply return the first parameter. For
    ///             mutable objects, it is safe to return a copy of the first parameter. However, since
    ///             composite user types often define component values, it might make sense to recursively
    ///             replace component values in the target object.
    /// </summary>
    public object Replace(object original, object target, ISessionImplementor session, object owner)
    {
        return original;
    }

    /// <summary>
    /// Get the "property names" that may be used in a query.
    /// </summary>
    public string[] PropertyNames { get { return new[] {"Argb", "KnownColor"}; } }

    /// <summary>
    /// Get the corresponding "property types"
    /// </summary>
    public IType[] PropertyTypes
    {
        get
        {
            return new IType[]
                   {
                       NHibernateUtil.Int32, NHibernateUtil.Int32
                   };
        }
    }

    /// <summary>
    /// The class returned by NullSafeGet().
    /// </summary>
    public Type ReturnedClass { get { return typeof (Color); } }

    /// <summary>
    /// Are objects of this type mutable?
    /// </summary>
    public bool IsMutable { get { return false; } }
}