Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

Multi-column IUserType fails in SubclassMap<> #210

Closed
matthewpeckterremark opened this Issue · 7 comments

3 participants

@matthewpeckterremark

When mapping a subclass entity property with a custom IUserType that takes multiple columns, the resulting Configuration contains a property mapping specification including only the first provided column. Converting the subclass entity to a root class and using the exact same IUserType results in no issue. Something that occurs during the application of the IIndeterminateSubclassMappingProvider is dropping all columns after the first.

@chester89
Collaborator

I would appreciate if you post your mappings and entities so I could investigate

@matthewpeckterremark

Fluent code built from jagregory-fluent-nhibernate-d3232ef as of 2012/07/20. By the time the configuration process gets to the end of the try block in FluentConfiguration.BuildConfiguration() (line 260 of FluentNHibernate.Cfg.FluentConfiguration.cs in the version I built from), the only column listed in the Contexts property mapping is "IsIcon", even though ColumnMappingCollection.Add(string) executed for all four columns specified in the ImageMap class, and the mapping definition prior to the end of BuildConfiguration() listed all four columns specified. I haven't had time to chase down exactly where the problem occurs.

Entities, mappings, and IUserType below, trimmed-down to eliminate excess clutter. "Entity" is an abstract base class with no implementation. Inheriting Image directly from Entity, adding the Id property and mapping to it, and converting the mapping to a ClassMap, the symptoms disappear, and the Contexts collection is filled as expected.

public class Media : Entity
{

    public virtual Guid Id { get; protected set; }
    public virtual bool IsDeprecated { get; set; }
    public virtual DateTime Created { get; set; }
    public virtual DateTime Modified { get; set; }

}

public class MediaMap : ClassMap<Media>
{
    public MediaMap()
    {
        Id(x => x.Id).Column("[Guid]").GeneratedBy.GuidComb();
        Map(x => x.IsDeprecated).Not.Nullable();
        Map(x => x.Created).Not.Nullable();
        Map(x => x.Modified).Not.Nullable();
    }
}

public class Image : Media
{
    private IList<string> contexts = new List<string>();

    public virtual string Title { get; set; }
    public virtual string Description { get; set; }
    public virtual int Width { get; set; }
    public virtual int Height { get; set; }

    public virtual IList<string> Contexts { get { return contexts; } }
}

public class ImageMap : SubclassMap<Image>
{
    public ImageMap()
    {
        Table("[ImageInfo]");
        KeyColumn("[Guid]");
        Map(x => x.Title).Length(100).Not.Nullable();
        Map(x => x.Description).Length(500).Nullable();
        Map(x => x.Width).Not.Nullable();
        Map(x => x.Height).Not.Nullable();
        Map(x => x.Contexts)
            .Access.CamelCaseField()
            .CustomType<ImageContextsUserType>()
            .Columns.Add(new [] {"IsIcon", "IsPromo", "IsWallpaper", "IsPlaceholder"})
            .Not.Nullable();
    }
}

public class ImageContextsUserType : IUserType
{
    public object NullSafeGet(IDataReader rs, string[] names, object owner)
    {
        IList<string> contexts = new List<string>();

        if ((bool)NHibernateUtil.Boolean.NullSafeGet(rs, names[0])) contexts.Add("Icon");
        if ((bool)NHibernateUtil.Boolean.NullSafeGet(rs, names[1])) contexts.Add("Promo");
        if ((bool)NHibernateUtil.Boolean.NullSafeGet(rs, names[2])) contexts.Add("Wallpaper");
        if ((bool)NHibernateUtil.Boolean.NullSafeGet(rs, names[3])) contexts.Add("Placeholder");

        return contexts;
    }

    public void NullSafeSet(IDbCommand cmd, object value, int index)
    {
        IList<string> contexts = value as IList<string>;

        if (contexts != null)
        {
            NHibernateUtil.Boolean.NullSafeSet(cmd, contexts.Contains("Icon"), index);
            NHibernateUtil.Boolean.NullSafeSet(cmd, contexts.Contains("Promo"), index + 1);
            NHibernateUtil.Boolean.NullSafeSet(cmd, contexts.Contains("Wallpaper"), index + 2);
            NHibernateUtil.Boolean.NullSafeSet(cmd, contexts.Contains("Placeholder"), index + 3);
        }
    }

    public Type ReturnedType
    {
        get { return typeof(IList<string>); }
    }

    public SqlType[] SqlTypes
    {
        get
        {
            return new [] {
                NHibernateUtil.Boolean.SqlType, NHibernateUtil.Boolean.SqlType,
                NHibernateUtil.Boolean.SqlType, NHibernateUtil.Boolean.SqlType };
        }
    }
}
@chester89
Collaborator

Well, that's really weird - I just repeated your setup, and resulted hbm xml contains all the columns of a user type.
Here's the relevant part: https://gist.github.com/chester89/6066142
I assume it's correct, or am I missing something?

@gliljas

I can confirm that it fails in the Nuget version, but works in "the trunk". The error did not occur with NH 3.1 and FNH 1.2.0.712.

@chester89
Collaborator
@gliljas

Yes

@chester89
Collaborator
@chester89 chester89 closed this
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.