Question

I have the following classes

public class Foo
{
    public virtual int Id { get; protected set; }
    public virtual IEnumerable<Bar> Bars { get; protected set; }
}

public class Bar
{
    public virtual int Id { get; protected set; }
    public virtual Foo Foo1 { get; set; }
    public virtual Foo Foo2 { get; set; }
}

with the following (presumably incorrect) mappings

public class FooMap : ClassMap<Foo>
{
    public FooMap()
    {
        this.Id(x => x.Id);
        this.HasMany(x => x.Bars);
    }
}

public class BarMap : ClassMap<Bar>
{
    public BarMap()
    {
        this.Id(x => x.Id);
        this.References(x => x.Foo1);
        this.References(x => x.Foo2);
    }
}

When creating the tables, FNH is doing the following:

create table "Bar" (
    Id  integer primary key autoincrement,
   Foo1_id INT,
   Foo2_id INT,
   Foo_id INT,
   constraint FK52E419EE7F0CDD3 foreign key (Foo1_id) references "Foo",
   constraint FK52E419EE3C0DCDD3 foreign key (Foo2_id) references "Foo",
   constraint FK52E419EE34BDF7FF foreign key (Foo_id) references "Foo"
)

i.e. has an extra FK reference.

This means that calling someBar.Foo1 is fine, but calling soFoo.Bars is always empty.

I've looked into using

this.HasMany(x => x.Bars).KeyColumns.Add("Foo1_id", "Foo2_id");

but this returns the error:

Foreign key (FK52E419EE660E4A59:Bar [Foo1_id, Foo2_id])) must have same number of columns as the referenced primary key (Foo [Id]).

What is the correct way to map this form of relationship?


To clarify the expected result if we have

Table:Foos   |  Table:Bars
Id           |  Id    Foo1_id   Foo2_id
--           |  -----------------------
11           |  1     11        12
12           |  2     12        11
13           |  3     12        13

then this.Session.Get<Foo>(11).Bars should contain Bars with Id of 1 and 2.

Was it helpful?

Solution

I'm pretty sure you cannot do what you are asking, with the auto-"merge voodoo" of the Foo.Bars (thus my question in the comments of the question).

I think this is what you can do. Note, I've come up with something a little less generic than Foo and Bar.

I have a "Project". And an Employee. Each Employee can "own" or "work-on" a project (or both). Obviously, in the real world this would be dumb to force an employee to only own or work on a single project. But it works here.

The Project has (many) Owners and Workers. And I have a get(readonly get) property for Anyone-associated-with-the-project. That is where you merge the two distinct relationships.

I have no idea on the Lazy-Initialization with this. I'm just pointing that the auto-merge thing you're looking for.. probably isn't in the cards.

public class Project
{
    public virtual int ProjectKey { get; set; }
    public virtual string ProjectName { get; set; }
    public virtual IList<Employee> ProjectOwners { get; set; }
    public virtual IList<Employee> ProjectWorkers { get; set; }

    public virtual IList<Employee> AnybodyAssociatedWithThisProject 
    {
        get
        {
            List<Employee> returnItems = new List<Employee>();
            if (null != this.ProjectOwners)
            {
                returnItems.AddRange(this.ProjectOwners);
            }
            if (null != this.ProjectOwners)
            {
                returnItems.AddRange(this.ProjectWorkers);
            }
            return returnItems;
        }
    }
}


public class Employee
{
    public virtual int EmployeeKey { get; set; }
    public virtual string SSN { get; set; }
    public virtual Project OwnedProject { get; set; }
    public virtual Project WorkedOnProject { get; set; }
}


public class ProjectMap : ClassMap<Project>
{
    public ProjectMap()
    {
        Id(x => x.ProjectKey);
        Map(x => x.ProjectName);
        HasMany(x => x.ProjectOwners)
            .KeyColumn("OwnedProjectKey");
        HasMany(x => x.ProjectWorkers)
            .KeyColumn("WorkedOnProjectKey");
        Table("Project");
    }
}

public class EmployeeMap : ClassMap<Employee>
{
    public EmployeeMap()
    {
        Id(x => x.EmployeeKey);
        Map(x => x.SSN);
        References(x => x.OwnedProject).Column("OwnedProjectKey");
        References(x => x.WorkedOnProject).Column("WorkedOnProjectKey");
        Table("Employee");
    }
}

I tested some data. Everything seems to work. The one caveat is that if an Employee "owns" and "works-on" a Project, my simple ".AddRange" merge code will duplicate that Employee. Other than that, it looks solid.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top