2

I'm trying to map a list of components that contains a unique key constraint on its values.

A 'trimmed' sample of the classes are below:

public class MyObject
{
    public virtual int Id { get; set; }
    public virtual IList<Alias> Aliases { get; set; }
}

public class Alias
{
    public Alias(string type, string value)
    {
        Type = type;
        Value = value;
    }

    public virtual string Type { get; protected set; }
    public virtual string Value { get; protected set; }
}

And the relevant NHibernate mapping from MyObject (types have been trimmed for brevity):

  <list cascade="all-delete-orphan" name="Aliases" table="MyObject_Aliases" mutable="true">
      <key>
        <column name="MyObject_id" />
      </key>
      <index>
        <column name="`Index`" />
      </index>
      <composite-element class="Alias">
        <property name="Type" type="System.String">
          <column name="Type" unique-key="UK_Alias" />
        </property>
        <property name="Value" type="System.String">
          <column name="Value" unique-key="UK_Alias" />
        </property>
      </composite-element>
    </list>

The intention is to prevent two MyObjects from containing the same Alias.

This works perfectly well when adding a new Alias to MyObject. However, if you were to remove an alias from the list where there are aliases after the removed alias then NHibernate tries to reorder the list which causes a key violation.

A sample of the SQL that NHibernate is generating is:

-- statement #1, saving MyObject.Aliases
INSERT INTO MyObject_Aliases
           (MyObject_id,
            "Index",
            Type,
            Value)
VALUES     (101 /* @p0 */,
            0 /* @p1 */,
            'A' /* @p2 */,
            'ALIAS1' /* @p3 */)

INSERT INTO MyObject_Aliases
           (MyObject_id,
            "Index",
            Type,
            Value)
VALUES     (101 /* @p0 */,
            1 /* @p1 */,
            'A' /* @p2 */,
            'ALIAS2' /* @p3 */)

-- statement #2, updating MyObject.Aliases after removing the first list item
UPDATE MyObject_Aliases
SET    Type = 'A' /* @p0 */,
       Value = 'ALIAS2' /* @p1 */
WHERE  MyObject_id = 101 /* @p2 */
       AND "Index" = 0 /* @p3 */

Statement #2 in this example throws:

NHibernate.Exceptions.GenericADOException : could not update collection rows: [MyObject.Aliases#101][SQL: UPDATE MyObject_Aliases SET Type = ?, Value = ? WHERE MyObject_id = ? AND "Index" = ?]
  ----> System.Data.SQLite.SQLiteException : Abort due to constraint violation
columns Type, Value are not unique

The exception itself makes perfect sense based on what is actually happening, but how do I get this to actually work as expected?

3
  • 1
    Do you need to use a list mapping, or can you use a set instead? Commented Mar 11, 2011 at 16:31
  • Aside from my own ignorance, there does not appear to be any reason that I can not use a set instead which does in fact appear to behave as I would expect aside from having to implement my own comparator which isn't a big deal. If you'd care to turn your comment into an actual answer for me I'd love to be able mark this question as answered! Commented Mar 11, 2011 at 17:19
  • What is the solution if a list mapping is needed? Commented Sep 8, 2016 at 15:28

1 Answer 1

1

If your collection doesn't need to be indexed you should probably use a set mapping instead. A set is a collection type that guarantees that there are no duplicates. A list is a collection type which can be indexed.

Sign up to request clarification or add additional context in comments.

3 Comments

I do lose the ability to access my collection with an index but sifting through the set is made easier in that regard with LINQ anyway.
@Chris, you can still index your collection in code, by exposing a List as a property, a good example can be found here stackoverflow.com/questions/824587/…
I have the exact same problem as this question, but with a genuine need to use a list mapping. How can I solve this still?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.