Mapping a dictionary

Andy's Avatar

Andy

29 Jun, 2010 08:54 PM via web

HI,

Given the following tables

TableA
---------
Id

TableB
-------
Id

TableAB
------------
AId
BId
EnumValue

And the given classes

public class A { 
     public virtual int Id { get; private set; } 
     public virtual IDictionary<EnumValues, B> Dictionary { get; private set; } 
}
public class B { public virtual int Id { get; private set; } }

How would I map this fluently?

Thanks
Andy

  1. Support Staff 2 Posted by Paul Batum on 30 Jun, 2010 11:12 AM

    Paul Batum's Avatar

    Hi Andy,

    I actually have an example of something pretty close to this in my fluent
    nhibernate test project. If you need to download a working example, you can
    get it here:
    http://github.com/paulbatum/Fluent-NH-Test-Bed/tree/fluent-dictionary-entity-bool

    <http://github.com/paulbatum/Fluent-NH-Test-Bed/tree/fluent-dictionary-entity-bool>But
    the gist of it is that this mapping:

                HasManyToMany(x => x.Groups)
                    .AsMap(null)
                    .AsTernaryAssociation()
                    .Element("IsManager", ep => ep.Type<bool>());

    Gives me this joining table:

        create table Groups (
            User_id INTEGER not null,
           IsManager INTEGER,
           Group_id INTEGER not null,
           primary key (User_id, Group_id)
        )

    I think that is pretty close to what you want, right?

    Sorry that the fluent interface for maps is so damn confusing.. we
    definitely plan on doing something about this.

  2. 3 Posted by Andy on 30 Jun, 2010 12:31 PM

    Andy's Avatar

    Paul,

    Thanks so much! I would have never guessed to pass null into AsMap. I am just missing one thing, which confuses me a bit.

    In case, the ep.Type statement has MyType as an enum. I have a convention which should map enums as int values, but I get an exception that the type MyType is not mapped.

    I am going to try just creating an IUserType for the enum and I think that should correct it, but I wanted to see if there was something else I'm missing?

    Thanks again for your help!
    Andy

  3. Support Staff 4 Posted by Paul Batum on 30 Jun, 2010 01:54 PM

    Paul Batum's Avatar

    Passing null doesn't make sense in all cases of using maps, but it works in
    this one. It sucks :/

    There should be an easy way to handle the enum, I can't remember it right
    now though. I'll see if I can dig something up.

  4. 5 Posted by Andy on 30 Jun, 2010 02:01 PM

    Andy's Avatar

    Paul,

    I'm getting an exception for the mapping: NHibernate.MappingException : An association from the table MyManyToManyTable refers to an unmapped class: DomainModel.MyType.

    Any ideas?

  5. 6 Posted by Andy on 30 Jun, 2010 02:31 PM

    Andy's Avatar

    Paul,

    Just wanted to update again; even if I use int for the Dictionary's key, I get the same exception: NHibernate.MappingException : An association from the table MyManyToManyTable refers to an unmapped class: System.Int32.

    I need to add to that we are using a branch we've been updating from April 30th. That branch had another fix we needed, and we were able to also fix the Convention / Subclass map problem we are working on in another thread. We were not able to fix the convention problem in a later branch because how Fluent handles the conventions internally changed pretty dramtically the next day.

    It's possible the dictionary mapping will work for you if its going against the latest code, but not for us because we're only a version around .363.

    Since I'm sure you have limited time, it might be better to focus on the that issue so that we can get current.

    Thanks for all your help so far.
    Andy

  6. 7 Posted by Andy on 01 Jul, 2010 05:26 PM

    Andy's Avatar

    Ok, so I sort of have things figured out. I'm left with two possiblities here. Either the Fluent .AsMap() call I can't get quite right, or there's a bug. I'm leaning toward the former, since the AsMap call is fairly complex.

    The Hbm that was generated via your recommend call was outputting this Xml:

      <map cascade="none" inverse="true" name="DeployedToTargets" mutable="true">
          <key column="AId" />
          <index column="EnumValue" type="MyNamespace.EnumValues, assembly" />
          <element column="BId" type="MyNamespace.B, assembly"/>
      </map>
    

    By trial and error, I found the Xml I wanted is actually this:

      <map cascade="none" inverse="true" name="DeployedToTargets" mutable="true">
          <key column="AId" />
          <index column="EnumValue" type="MyNamespace.EnumValues, assembly" />
          <many-to-many column="BId" class="MyNamespace.B, assembly"/>
      </map>
    

    This has everything I want (except no primary key is set).

    Any ideas?

  7. Support Staff 8 Posted by Paul Batum on 05 Jul, 2010 12:42 PM

    Paul Batum's Avatar

    Sorry Andy, I think I lead you astray with my previous mapping - I got it
    mixed up on which side of the dictionary the entity was.

    Lets try again:

    <http://github.com/paulbatum/Fluent-NH-Test-Bed/tree/fluent-dictionary-enum-entity>
    HasManyToMany<Book>(x => x.FavouriteBooks)
                    .Table("FavouriteBooks")
                    .ParentKeyColumn("CustomerID")
                    .ChildKeyColumn("BookID")
                    .AsMap<string>("BookType");

    It makes this xml:

    <map cascade="all" name="FavouriteBooks" table="FavouriteBooks"
    mutable="true">
          <key>
            <column name="CustomerID" />
          </key>
          <index type="System.String, mscorlib, Version=2.0.0.0,
    Culture=neutral, PublicKeyToken=b77a5c561934e089">
            <column name="BookType" />
          </index>
          <many-to-many class="Domain.Book, Domain, Version=1.0.0.0,
    Culture=neutral, PublicKeyToken=null">
            <column name="BookID" />
          </many-to-many>
        </map>

    and this table:

        create table FavouriteBooks (
            CustomerID INTEGER not null,
           BookID INTEGER not null,
           BookType TEXT not null,
           primary key (CustomerID, BookType)
        )

    You can download the whole working thing here:
    http://github.com/paulbatum/Fluent-NH-Test-Bed/tree/fluent-dictionary-enum-entity

    Let me know how it goes.
    <http://github.com/paulbatum/Fluent-NH-Test-Bed/tree/fluent-dictionary-enum-entity>
    On Fri, Jul 2, 2010 at 3:30 AM, Andy <
    ***@tenderapp.com<tender%***@tenderapp.com>
    > wrote:

  8. 9 Posted by Andy on 06 Jul, 2010 02:02 PM

    Andy's Avatar

    Paul,

    Thanks for the new suggestion, however that leads me back to the An association from the table FavoriteBooks refers to an unmapped class: System.Collections.Generic.KeyValuePair`2[[BookTypeEnum],[Book]]

    Andy

  9. Support Staff 10 Posted by Paul Batum on 07 Jul, 2010 11:39 AM

    Paul Batum's Avatar

    Have you looked at the sample I linked to? How is my sample different to
    your scenario?

  10. Support Staff 11 Posted by Paul Batum on 07 Jul, 2010 11:40 AM

    Paul Batum's Avatar

    Oh I think I misunderstood, that error message looks like it came from my
    sample.

    But it works for me!

    What did you do exactly? Did you grab my sample but run it using non-current
    FNH binaries?

  11. 12 Posted by Andy on 07 Jul, 2010 03:51 PM

    Andy's Avatar

    Hi Paul,

    Sorry for the confusion, I changed the types to match your sample, but the error came from my application using the same mapping. However, that was the message I was getting when the map contained the element tag within the map tag instead of the many-to-many tag.

    We are not using the latest version though; we're using a version committed the day after the .363 release. The .363 release had a bug where Components were not working with Subclass map, but the fix was in the next commit. However that next commit also introduced the bug where Conventions are not being applied in Subclass maps. We grabbed the source and fix that bug as well, because the next commit after that (on 6/1/2010 I believe) totally reworked how conventions are done internally in Fluent, and we were not able to figure out how to fix the Convention + Subclass map issue.

    So we've been stuck on a release from Apr 30, and it's possbile the AsMap works fine in a more recent release... but without the Convention and SubclassMap working as it was in RTM, we can't move off of our forked version...

    Thats why I was hoping you could continue looking into this issue: https://fluentnhibernate.tenderapp.com/discussions/help/164-convent...

    The sooner we can go back to using the most recent Fluent, the better.

    Andy

  12. Support Staff 13 Posted by James Gregory on 03 Aug, 2010 11:30 PM

    James Gregory's Avatar

    As a user of our Dictionary support, you might be interested in our rework of this feature: Dictionary mapping redesign. We're working on simplifying the syntax, while making it more flexible at the same time.

  13. Support Staff 14 Posted by Paul Batum on 05 Aug, 2010 01:48 PM

    Paul Batum's Avatar

    Unfortunately Andy won't be able to try the new syntax without running into
    the aforementioned issues with latest code base, specifically around the
    application of conventions to components (this is the problem that I tried
    to fix but the fix wasn't right and we had to roll it back)

    On Wed, Aug 4, 2010 at 9:32 AM, James Gregory <
    ***@tenderapp.com<tender%***@tenderapp.com>
    > wrote:

  14. 15 Posted by Andy Johnstone on 05 Aug, 2010 01:59 PM

    Andy Johnstone's Avatar

    Sorry I didn't reply to James, but yes, Paul is correct. We've been unable to upgrade without a way to use conventions within components.

    -----Original Message-----
    From: Paul Batum [mailto:tender+d1fb761e6aa64b44dcbdcdc4947d093bd14368a4a=***@sendgrid.me] On Behalf Of Paul Batum
    Sent: Thursday, August 05, 2010 9:51 AM
    To: ***@mywebgrocer.com
    Subject: Re: Mapping a dictionary [Help and guidance]

Reply to this discussion

Preview Comments are parsed with Markdown. Help with syntax

Attached Files

    You can attach files up to 10MB

    What number comes after 20?