Dictionary mapping maps to interfaces instead of implemention #234

Open
fennekit opened this Issue Jul 24, 2013 · 7 comments

Projects

None yet

2 participants

I have a IDictionary<IA, IB> dictionary in my class. I try to map this with FluentNHibernate but it creates a wrong mapping. The application shows that it generates a map using IA and IB instead of classes A and B. This is also clearly visible from the exported *.hbm.xml files. I am using Fluent-nhibernate 1.3.

The following code shows the problem:

using FluentNHibernate.Cfg;
using FluentNHibernate.Mapping;
using NHibernate;
using NHibernate.Cfg;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApplication2
{
    public interface IA
    {
        int Id { get; set; }
        string PropA { get; set; }
    }

    public interface IB
    {
        int Id { get; set; }
        string PropB { get; set; }
    }
    public interface IC
    {
        int Id { get; set; }
        IDictionary<IA, IB> Dictionary { get; set; }
    }
    public class A : IA
    {
        public virtual int Id { get; set;}
        public string PropA { get; set; }
    }
    public class B : IB
    {
        public virtual int Id { get; set; }
        public string PropB { get; set; }
    }

    public class C : IC
    {
        public virtual int Id { get; set; }
        public virtual IDictionary<IA, IB> Dictionary { get; set; }
    }

    public class AMapper : ClassMap<A>
    {
        public AMapper()
        {
            Id(x => x.Id).GeneratedBy.Identity().Column("id");
            Map(x => x.PropA).Column("propa");
        }
    }
    public class BMapper : ClassMap<B>
    {
        public BMapper()
        {     
            Id(x => x.Id).GeneratedBy.Identity().Column("id");
            Map(x => x.PropB).Column("propa");
        }
    }
    public class CMapper : ClassMap<C>
    {  
        public CMapper()
        {     
            Id(x => x.Id).GeneratedBy.Identity().Column("id");
            HasMany<C>(x => x.Dictionary).Table("dict_table").AsEntityMap("C_id").KeyColumn("B_id");
        }
    }


    class Program
    {
        static void Main(string[] args)
        {
            Configuration conf = new Configuration().Configure();
            //configuration.AddAssembly(typeof(Program).Assembly);

            var nhConfig = Fluently.Configure(conf)
            .Mappings(mapping => 
                {
                    mapping.FluentMappings.AddFromAssembly(typeof(A).Assembly);
                    mapping.FluentMappings.ExportTo(@"c:\temp\");
                })
                .ExposeConfiguration(cfg =>{});

            Configuration configuration = nhConfig.BuildConfiguration();
            ISessionFactory sessionFactory = configuration.BuildSessionFactory();
            sessionFactory.OpenSession();


        }
    }
}
Collaborator

But you declared IDictionary of IA, IB. Fluent doesn't know that only implementations of IA and IB are classes A and B, respectively, neither does he know that you want concrete type Dictionary to be used for the interface.
Also, didn't you mean to write HasMany[B] or HasMany[IB] in the mapping?

It is possible to do the same with other 'normal' relations. Using the classic xml files I can get this working. I am currently on holiday. End of next week I can provide some examples.

It may be not a bug, but an incomplete API.

Collaborator

Please provide examples

If I have a reference to an interface in my domain object

public virtual IModel Model { get; set; }

I can specify in my mapping

References<Model>(x => x.Model).Column("model_id"); 

and this works (since Model implements IModel). I need something similar for Dictionaries.

The implementation of the dictionary is as follows in Angle.cs

public virtual IDictionary<IUser, IUserAngle> AngleUsers
        {
            get
            {
                return _angleUsers;
            }
            set
            {
                _angleUsers = value;
            }
        }

The mapping in AngleMap.cs is

HasMany<UserAngle>(x => x.AngleUsers).Table("UserAngle").AsEntityMap("UserId").KeyColumn("AngleId").Cascade.All();

the mapping generated for this relation by FluentNhibernate is the following (intermediate) Angle.hbm.xml

<map cascade="all" name="AngleUsers" table="UserAngle">
      <key>
        <column name="AngleId" />
      </key>
      <index-many-to-many class="EveryAngle.Edgar.Domain.User.IUser, EveryAngle.Edgar.Domain, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null">
        <column name="UserId" />
      </index-many-to-many>
      <one-to-many class="EveryAngle.Edgar.Domain.User.IUserAngle, EveryAngle.Edgar.Domain, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
    </map>

This should be (this works, tried it by doing the mapping with the Angles.hbm.xml) (Note the use of the class instead of the Interface)

<map cascade="all" name="AngleUsers" table="UserAngle">
      <key>
        <column name="AngleId" />
      </key>
      <index-many-to-many class="EveryAngle.Edgar.Domain.User.User, EveryAngle.Edgar.Domain, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null">
        <column name="UserId" />
      </index-many-to-many>
      <one-to-many class="EveryAngle.Edgar.Domain.User.UserAngle, EveryAngle.Edgar.Domain, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
    </map>

In short the API does not have a way to specify the classes to use for the dictionary (in my opinion).

Collaborator

Ok, that's more like it, let's see what I can do to help.

2013/8/22 Fennek IT notifications@github.com

If I have a reference to an interface in my domain object

public virtual IModel Model { get; set; }

I can specify in my mapping

References(x => x.Model).Column("model_id");

and this works (since Model implements IModel). I need something similar
for Dictionaries.

The implementation of the dictionary is as follows in Angle.cs

public virtual IDictionary<IUser, IUserAngle> AngleUsers
{
get
{
return _angleUsers;
}
set
{
_angleUsers = value;
}
}

The mapping in AngleMap.cs is

HasMany(x => x.AngleUsers).Table("UserAngle").AsEntityMap("UserId").KeyColumn("AngleId").Cascade.All();

the mapping generated for this relation by FluentNhibernate is the
following (intermediate) Angle.hbm.xml

This should be (this works, tried it by doing the mapping with the
Angles.hbm.xml) (Note the use of the class instead of the Interface)

In short the API does not have a way to specify the classes to use for the
dictionary (in my optionion).

Reply to this email directly or view it on GitHubhttps://github.com/jagregory/fluent-nhibernate/issues/234#issuecomment-23075367
.

ó Õ×ÁÖÅÎÉÅÍ,
þÅÒÍ£ÎÎÏ× çÌÅÂ,
ÔÅÌ. (916) 314-9324

Hi, would adding an AsEntityMap function to ManyToManyPart.cs help?

        public ManyToManyPart<TChild> AsEntityMap(Type indexType, string indexColumn, Type typeOfValue, string valueColumn)
        {
            return AsMap(null).AsTernaryAssociation(indexType, indexColumn,typeOfValue, valueColumn);
        }

This would allow me to specify the necessary types.

Create a pull request for this issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment