14

I have a domain class with a property IList<string> that I want to map to a table with a single data value (i.e. it has an ID, a foreign key ID to the domain entity table, and a varchar data column).

I keep getting the error:

Association references unmapped class: System.String

How can I map a table to a collection of strings?

BartoszKP
  • 34,786
  • 15
  • 102
  • 130
roryf
  • 29,592
  • 16
  • 81
  • 103
  • There is nothing wrong with this. You just mapped it with one-to-many, which only works for lists of entities. Take a look at derek's and Frederiks anserwers. – Stefan Steinegger May 11 '09 at 15:37

3 Answers3

22

I just ran into a similar situation; and I found that it is indeed possible to map a collection of strings. Note that you'll have to map those strings as value objects.

This is what I have:

public class Chapter
{
    private ISet<string> _synonyms = new HashedSet<string>();

    public ReadOnlyCollection<string> Synonyms
    {
       get { return new List<string>(_synonyms).AsReadOnly(); }
    }
}

Mapping:

<class name="Chapter" table="Chapter">
   <set name="Synonyms" table="ChapterSynonyms">
       <key column="ChapterId" />
       <element column="ChapterCode" type="string" />
   </set>
</class>
Stefan Steinegger
  • 63,782
  • 15
  • 129
  • 193
Frederik Gheysels
  • 56,135
  • 11
  • 101
  • 154
  • 10
    Came across this again recently, here is the FluentNHibernate mapping I used based on your XML mapping: mapping.HasMany(x => x.Synonyms).AsBag().Element("ChapterCode", m => m.Type()); – roryf Dec 11 '09 at 12:53
  • Shouldn't HashedSet be HashShet? – jvdveuten May 12 '15 at 10:14
  • 1
    @roryf you are using a Bag, where it should be a Set. The correct code should be: HasMany(x => x.Synonyms).Table("Synonyms").AsSet().Element("ChapterCode", m => m.Type().Not.Nullable()); – jvdveuten May 12 '15 at 10:24
  • No HashedSet should not be HashSet. HashedSet is an Iesi.Collections type, and this was used when using NHibernate before MS created the HashSet type. Although the sample code will work with an MS HashSet as well, using HashedSet is perfectly valid. – Frederik Gheysels May 12 '15 at 11:33
8

Unless I am mistaken you can do this:

<bag name="Identities" access="property">
  <key column="accountId"/>
  <element column="identity" type="string"/>
</bag>

Identities being an IList<string>

Derek Ekins
  • 11,215
  • 6
  • 61
  • 71
1

You can do this with IUserType like so:

public class DelimitedList : IUserType
{
    private const string delimiter = "|";

    public new bool Equals(object x, object y)
    {
        return object.Equals(x, y);
    }

    public int GetHashCode(object x)
    {
        return x.GetHashCode();
    }

    public object NullSafeGet(IDataReader rs, string[] names, object owner)
    {
        var r = rs[names[0]];
        return r == DBNull.Value 
            ? new List<string>()
            : ((string)r).SplitAndTrim(new [] { delimiter });
    }

    public void NullSafeSet(IDbCommand cmd, object value, int index)
    {
        object paramVal = DBNull.Value;
        if (value != null)
        {
            paramVal = ((IEnumerable<string>)value).Join(delimiter);
        }
        var parameter = (IDataParameter)cmd.Parameters[index];
        parameter.Value = paramVal;
    }

    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 SqlType[] SqlTypes
    {
        get { return new SqlType[] { new StringSqlType() }; }
    }

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

    public bool IsMutable
    {
        get { return false; }
    }
}

Then define the IList<string> property as type="MyApp.DelimitedList, MyApp".

NOTE: SplitAndTrim is a string extension with various overrides that I created. Here is the core method:

public static IList<string> SplitAndTrim(this string s, StringSplitOptions options, params string[] delimiters)
    {
        if (s == null)
        {
            return null;
        }
        var query = s.Split(delimiters, StringSplitOptions.None).Select(x => x.Trim());
        if (options == StringSplitOptions.RemoveEmptyEntries)
        {
            query = query.Where(x => x.Trim() != string.Empty);
        }
        return query.ToList();
    }
Tim Scott
  • 15,106
  • 9
  • 65
  • 79