views:

825

answers:

1

Hello, I have a problem mapping an Id. The structures of the entities are as follows:

public abstract class Entity<TEntity, TId>
    where TEntity : Entity<TEntity, TId>
{
    public virtual TId Id { get; protected set; }
    public override bool Equals(object obj)...
    ...
}

public class EntityA<EntityA, long> : Entity<EntityA, long>
{
    public virtual EntityB B { get; private set; }
    /* ... */
}

public class EntityB<EntityA, long> : Entity<EntityB, long>
{
    /* ... */
}

In my model, every EntityA must contain exactly one EntityB, and every EntityB that exists is there to be part of an EntityA. It's a common one-to-one relationship.

Now, to the mappings:

public class EntityAMap : ClassMap<EntityA>
{
    public EntityAMap()
    {
        Id(x => x.Id);
        HasOne(x => x.B)
            .Cascade.All();
        /* ... */
    }
}

public class EntityBMap : ClassMap<EntityB>
{
    public EntityBMap()
    {
        Id(x => x.Id)
            .GeneratedBy.Foreign("Id");
        /* ... */
    }
}

Then I create an EntityA, that creates by itself an EntityB. Then when I save it

var entityA = EntityAFactory.CreateNewValidEntityA();
session.SaveOrUpdate(entityA);

NHibernate throws an exception, "Unable to resolve property: Id".

However, my log shows that an EntityA was "inserted" into the DB, and by debugging, I can see that EntityA.Id was attributed a value (ie, nhibernate did a good job saving entityA, retrieving the Id generated by the DB and setting the entityA.Id property accordingly).

However, no entityB was created (empty database, and log shows nothing). So, it looks to me that NHibernate had problems to access this property, when saving EntityB, through the "GeneratedBy.Foreign("Id")" definition. Maybe it's because the property "Id" is not directly a property of EntityA, but from EntityBase, however what I did looks correct to me.

Where is the problem? How can I solve it?

Thanks!

EDIT: Here I show some of the stack trace, if it might help. As you can see, it did Cascade, and it did SaveOrUpdate on the other entity.

at NHibernate.Tuple.Entity.EntityMetamodel.GetPropertyIndex(String propertyName)
at NHibernate.Tuple.Entity.AbstractEntityTuplizer.GetPropertyValue(Object entity, String propertyPath)
at NHibernate.Persister.Entity.AbstractEntityPersister.GetPropertyValue(Object obj, String propertyName, EntityMode entityMode)
at NHibernate.Id.ForeignGenerator.Generate(ISessionImplementor sessionImplementor, Object obj)
...
at NHibernate.Impl.SessionImpl.SaveOrUpdate(String entityName, Object obj)
at NHibernate.Engine.CascadingAction.SaveUpdateCascadingAction.Cascade(IEventSource session, Object child, String entityName, Object anything, Boolean isCascadeDeleteEnabled)
at NHibernate.Engine.Cascade.CascadeToOne(Object child, IType type, CascadeStyle style, Object anything, Boolean isCascadeDeleteEnabled)
at NHibernate.Engine.Cascade.CascadeAssociation(Object child, IType type, CascadeStyle style, Object anything, Boolean isCascadeDeleteEnabled)
at NHibernate.Engine.Cascade.CascadeProperty(Object child, IType type, CascadeStyle style, Object anything, Boolean isCascadeDeleteEnabled)
at NHibernate.Engine.Cascade.CascadeOn(IEntityPersister persister, Object parent, Object anything)
...
at NHibernate.Impl.SessionImpl.SaveOrUpdate(Object obj)
at myproject...
+1  A: 

The Foreign function takes a class rather than a property.

Firstly, you need to reference EntityA from EntityB:

public class EntityB<EntityA, long> : Entity<EntityB, long>
{
    // this is new!
    public virtual EntityA EntityA { get; private set; }

    /* ... */
}

Here's the new mapping file for EntityB:

public EntityBMap()
{
    // first reference EntityA....
    References(x => x.EntityA)
        .SetAttributes(new Attributes
            {
                {"insert", "false"}, 
                {"update", "false"}
            });

    // ... then use it in the Foreign function
    Id(x => x.Id)
        .GeneratedBy.Foreign("EntityA");
    /* ... */
}

The SetAttributes call avoids NHibernate trying to map the Id field twice (and hence blowing up).

RaceFace