Map foreign key in Fluent NHibernate without object property
Asked Answered
W

1

6

My question is, is there a possible Fluent NHibernate mapping for Parent and Child objects that does not require the Child object to have a Parent object property? I haven't figured out how to map the reference back to the Parent. When I call Create with the mappings as-is, I get an exception because the Child object does not have the required foreign key (required in the data store) back to the Parent.

I have two POCO classes:

public class Parent
{
    public virtual int Id { get; set; }
    public virtual string Name { get; set; }
    public virtual IList<Child> Childs { get; set; }
}

public class Child
{
    public virtual int Id { get; set; }
    public virtual string Name { get; set; }
    public virtual int ParentId { get; set; }
}

And some mappings:

public class ParentMap : ClassMap<Parent>
{
    public ParentMap()
    {
        this.Table("Parents");
        this.Id(x => x.Id);
        this.Map(x => x.Name);
        this.HasMany(x => x.Childs).KeyColumn("ChildId").Cascade.AllDeleteOrphan();
    }
}

public class ChildMap : ClassMap<Child>
{
    public ChildMap()
    {
        this.Table("Childs");
        this.Id(x => x.Id);
        this.Map(x => x.Name);
        // Needs some sort of mapping back to the Parent for "Child.ParentId"
    }
}

And Create method:

public Parent Create(Parent t)
{
    using (this.session.BeginTransaction())
    {
        this.session.Save(t);
        this.session.Transaction.Commit();
    }
    return t;
}

I want to be able to create a Parent object that has a list of Child objects, but not have the Child objects have references back to their Parent (other than the Parent ID). I want to do this to avoid the circular reference from Parent to a list of Childs back to the Parent object, since that is causing issues with JSON serialization.

Wellread answered 13/7, 2013 at 2:21 Comment(3)
Is mapping the parent property (in Child) to a private field an option?Sedge
That's what I'm doing, though I'm trying to avoid it since I can't then run queries on the ParentId property. It seems like I should be able to map a property with a foreign key constraint to a column without requiring an object...Peal
In the case of the original question, Felipe's answer is correct. Don't serialize the entities directly, transform them into a DTO or ViewModel first, getting the exact structure you want first.Sedge
R
3

You can map these entities without problem, try this:

public class ParentMap : ClassMap<Parent>
{
    public ParentMap()
    {
        this.Table("Parents");
        this.Id(x => x.Id);
        this.Map(x => x.Name);
        this.HasMany(x => x.Childs).KeyColumn("ChildId").Cascade.AllDeleteOrphan();
    }
}

public class ChildMap : ClassMap<Child>
{
    public ChildMap()
    {
        this.Table("Childs");
        this.Id(x => x.Id);
        this.Map(x => x.Name);
        this.Map(x => x.ParentId);
        // if you have a reference of Parent object, you could map as a reference, for sample:
        this.References(x => x.Parent).Column("ParentId");
    }
}

When you get entities from ISession, do not serialize it to some format because these can be proxies of nhibernate instead entities objects. Try to create DTO (Data Transfer Object) classes and convert these entities to a DTO object, and serialize it. You will avoid circular references. For sample:

public class ParentDTO
{   
    public int Id { get; set; }
    public string Name { get; set; }
    public int ParentId { get; set; }

    /* here you just have the primitive types or other DTOs, 
       do not reference any Entity type*/
}

And when you need to read the values to share the serialized value:

var dto = ISession.Query<Parent>()
                  .Select(x => 
                      new ParentDTO() { 
                           Id = x.Id, 
                           Name = x.Name, 
                           ParentId = x.ParentId)
                  .ToList();

Get this result from the Data Access Layer and try to serialize, for sample:

var result = Serialize(dto);
Rhys answered 12/9, 2014 at 19:25 Comment(2)
A critical piece for me that your answer does not address is the ParentId property must have a foreign key constraint on the Id property of the Parent object. You just Mapped it, so it has no constraints on it at all.Peal
I aggree with you @PatrickQuirk, but in the model that OP provided does not has a reference, just an Id.Rhys

© 2022 - 2024 — McMap. All rights reserved.