I have an object that has 4 properties of IList<String> type. You can natively map these properties in a separate table with this simple mapping

<bag name="keys" access="field" cascade="all-delete-orphan" table="Keys" fetch="join"> <key column="deps_id" /> <element column="deps_key" type="String" /> </bag>

Ok, this is a simple mapping that store all the keys into another table with two columns, one is the foreign key to the main object, the other is a string column to store the data. But a similar mapping does not satisfy me, because I need to create 4 more table to store string coming from my 4 IList<String> properties…too bad.

The solution is…again….IUserType. The solution is store the strings in a single column, combining the strings in one with a separator that does not happens to be part of any string, as an example, if the collection contains (“Key1″, “Key2″) I want to be stored as “Key1#Key2″, here is the get part of the usertype

public object NullSafeGet(System.Data.IDataReader rs, string[] names, object owner) { List<String> result = new List<String>(); Int32 index = rs.GetOrdinal(names[0]); if (rs.IsDBNull(index) || String.IsNullOrEmpty((String) rs[index])) return result; foreach (String s in ((String)rs[index]).Split(cStringSeparator)) result.Add(s); return result; }

If the data in database is null, or is an empty string I return an empty List<String>, but if the data is not empty I split the string with the separator, and then add each single string in the list. The set part is similar

public void NullSafeSet(System.Data.IDbCommand cmd, object value, int index) { if (value == null || value == DBNull.Value) { NHibernateUtil.String.NullSafeSet(cmd, null, index); } IEnumerable<String> stringList = (IEnumerable<String>) value; StringBuilder sb = new StringBuilder(); foreach(String s in stringList) { sb.Append(s); sb.Append(cStringSeparator); } if (sb.Length > 0) sb.Length--; NHibernateUtil.String.Set(cmd, sb.ToString(), index); }

If the value is null I set null value, if not I simply cast the original value as an IEnumerable<String>, that is sufficient for me to enumerate all the string, store them in a StringBuilder, and finally remove the trailing separator and store the string in database. Here is a Test

... Domain d = new Domain(); d.BaseUri = new Uri("http://www.nablasoft.com"); d.ContentBlackList.Add("Key1"); d.ContentBlackList.Add("Key2"); ...

This piece of code produces this SQL

INSERT INTO Domain (doma_baseUrl, doma_contentBlackList, doma_Id) VALUES (@p0, @p1, @p2); @p0 = 'http://www.nablasoft.com/', @p1 = 'Key1#Key2', @p2 = '28857095-f642-4d11-875a-714eb35f0537'

As you can see the Uri is translated into a string and the two keywords are combined into one, in this way I can store all the 4 properties in the same table, having a clearer structure of the database.

alk.

Tags:




kick it on DotNetKicks.com

3 Responses to “Second IUserType of the day – Store a list of string in database with NHIbernate.”

  1. Instead of doing:

    IEnumerable stringList = (IEnumerable) value;
    StringBuilder sb = new StringBuilder();
    foreach(String s in stringList) {
    sb.Append(s);
    sb.Append(cStringSeparator);
    }
    if (sb.Length > 0) sb.Length–;
    NHibernateUtil.String.Set(cmd, sb.ToString(), index);

    you can do:

    NHibernateUtil.String.Set(cmd, String.Join(cStringSeparator,((List)value).ToArray()), index);

  2. You are right, the String.Join makes the code more readable. Thanks for the comment :D

    alk.

Trackbacks/Pingbacks

  1. Some details on older post about usertype

Leave a Reply