NHibernate in code. Create ManyToOne and ManyToMany using PropertyInfo

213 views Asked by At

I use NHibernate 4.0.2.4000. I'm creating a mapping using PropertyInfo:

public class MyClassMapping<T> : ClassMapping<T> where T : BaseEntity
{
    public MyClassMapping()
    {
        ....
        PropertyInfo[] props = typeof(T).GetProperties();
        foreach (PropertyInfo prop in props)
        {
            /* some code */

            Bag(prop.Name, collectionMapping =>
            {
                collectionMapping.Table(prop.Name + "_" + classType.Name +"_Rel");
                collectionMapping.Key(k => k.Column(prop.Name + "_Id"));
            },
            mapping => mapping.ManyToMany(p => p.Column(classType.Name + "_Id")));

            /* some code */
        }
    }
}

But the "Bag" returns an error: The type arguments for method [...] cannot be inferred from the usage. Try specifying the type arguments explicitly. I know that I have to use Bag<TElement>() but I don't have TElement.

Methods (Id(), Property()) work well.

I tried to use it:

this.GetType()
    .GetMethod("Bag")
    .MakeGenericMethod(prop.PropertyType)
    .Invoke(this, new object[]{ prop.Name, [...], [...] });

but delegates are too complicated.

The method ManyToOne() works:

public class MyClassMapping<T> : ClassMapping<T> where T : BaseEntity
{
    public MyClassMapping()
    {
        ....
        PropertyInfo[] props = classType.GetProperties();
        foreach (PropertyInfo prop in props)
        {
            /* some code */

            RegisterManyToOneMappingByMember(prop, map =>
            {
                map.Column(aManyToOne.Name);
                ....
            });

            /* some code */
        }
    }

    protected virtual void RegisterManyToOneMappingByMember(MemberInfo property, Action<IManyToOneMapper> mapping)
    {
        RegisterManyToOneMapping<BaseEntity>(mapping, property);
    }

Maybe someone knows another solution? Please don't propose to do map in the xml in memory dynamically. Thanks.

1

There are 1 answers

1
Radim Köhler On BEST ANSWER

The way here could be, to split the Bag defintion and refelection.

So, firstly method creating Bag, consuming generic argument TElement:

public virtual void CreateBag<TElement>(PropertyInfo prop, Type classType)
{
    Bag<TElement>(prop.Name,
    collectionMapping =>
    {
        collectionMapping.Table(prop.Name + "_" + classType.Name + "_Rel");
        collectionMapping.Key(k => k.Column(prop.Name + "_Id"));
    },
    mapping => mapping.ManyToMany(p => p.Column(classType.Name + "_Id")))
    ;
}

And now adjusted iterator:

var classType = ... // current class type 
PropertyInfo[] props = classType.GetProperties();
foreach (PropertyInfo prop in props)
{
    // here ... just find out if this property
    // is candidate for Bag mapping
    var isBagCandidate = prop.PropertyType.GenericTypeArguments.Length > 0
        && !prop.PropertyType.IsValueType
        && ... // these checks should be adjusted
        ;

    if (!isBagCandidate)
    {
        continue; // do not continue to Bag mapping
    }

    // now we just get reference to our method defined above
    MethodInfo method = typeof(MyClassMapping).GetMethod("CreateBag");
    MethodInfo generic = method.MakeGenericMethod(
                                           prop.PropertyType.GenericTypeArguments[0]);

    // and call it, with needed params
    generic.Invoke(this, new object[] { prop, classType });
}