F.P
F.P

Reputation: 17831

NHibernate tries to cast Entity to IDictionary when saving

I have a very simple setup:

public class Role
{
    public virtual int Id { get; set; }
    public virtual string Name { get; set; }
    public virtual ISet<Permission> Permissions { get; set; }

    public Role()
    {
        Permissions = new HashSet<Permission>();
    }
}

public enum Permission
{
    ACCESS_USER_MANAGER, [... and many more]
}

And according mapping files:

<class entity-name="UserManager.Model.Role" table="roles">
    <id name="Id" type="int">
        <generator class="identity" />
    </id>

    <property name="Name" column="name" type="string"/>

    <set name="Permissions" table="permissions">
        <key column="role"/>
        <element column="name" type="string"/>
    </set>

</class>

But, when I try to create a role and save it:

[TestMethod]
public void TestRoles()
{
    ITransaction transaction = _session.BeginTransaction();
    Role role = new Role();
    role.Name = "Test-Role";
    role.Permissions.Add(Permission.ACCESS_USER_MANAGER);
    _session.Save(role);
    transaction.Commit();
}

I get an exception:

System.InvalidCastException: Unable to cast object of type 'UserManager.Model.Role' to type 'System.Collections.IDictionary'.

Why does NHibernate try to cast my Entity to an IDictionary? And, more importantly: How can I make this work? What am I missing?

Upvotes: 1

Views: 209

Answers (1)

Radim K&#246;hler
Radim K&#246;hler

Reputation: 123891

I can see three problems here. Firstly you've used the mapping related to:

4.4. Dynamic models

A cite:

The following examples demonstrates the representation using Maps (Dictionary). First, in the mapping file, an entity-name has to be declared instead of (or in addition to) a class name:

<hibernate-mapping>
<class entity-name="Customer">
...

And this is what we can see in the mapping above entity-name instead of name. As the example shows if we use dynamic then we would continue:

// Create a customer
var frank = new Dictionary<string, object>();
frank["name"] = "Frank";
...
// Save customer
s.Save("Customer", frank);

See:

5.1.3. class

A cite:

<class
    name="ClassName"                              (1)
    ...

(1) name: The fully qualified .NET class name of the persistent class (or interface), including its assembly name.

So, we cannot use dynamic mapping but the standard class mapping:

// Instead of this
// <class entity-name="UserManager.Model.Role" table="roles">

// we should use this
<class name="UserManager.Model.Role" table="roles">

Secondly, the Enum should be mapped to int column with this element mapping (also I used permissionId as column name to show that it should be int):

<element column="permissionId" type="UserManager.Model.Permission"/>

Summary this should be working mapping:

<class name="UserManager.Model.Role" table="roles">
    <id name="Id" type="int">
        <generator class="identity" />
    </id>

    <property name="Name" column="name" type="string"/>

    <set name="Permissions" table="permissions">
        <key column="role"/>
        //<!--<element column="name" type="string"/>-->
        <element column="permissionId" type="UserManager.Model.Permission"/>
    </set>

</class>

The third issue, could be swapping of iesi and System ISet. It should be like this:

using Iesi.Collections.Generic;
public class Role
{
    public virtual int Id { get; set; }
    public virtual string Name { get; set; }
    public virtual ISet<Permission> Permissions { get; set; }

    public Role()
    {
      //Permissions = new HashSet<Permission>();
        Permissions = new HashedSet<Permission>();
    }
}

Upvotes: 1

Related Questions