views:

1028

answers:

8

Hi all, my first time on the site so apologies if it's tagged incorrectly or been answered elsewhere...

I keep running into particular situation on my current project and I was wondering how you guys would deal with it. The pattern is: a parent with a collection of children, and the parent has one or more references to particular items in the child collection, normally the 'default' child.

A more concrete example:

public class SystemMenu 
{
    public IList<MenuItem> Items { get; private set; }
    public MenuItem DefaultItem { get; set; }
}

public class MenuItem
{
    public SystemMenu Parent { get; set; }
    public string Name { get; set; }
}

To me this seems like a good clean way of modelling the relationship, but causes problems immediately thanks to the circular association, I can't enforce the relationship in the DB because of the circular foreign keys, and LINQ to SQL blows up due to the cyclic association. Even if I could bodge my way round this, it's clearly not a great idea.

My only idea currently is to have an 'IsDefault' flag on MenuItem:

public class SystemMenu 
{
    public IList<MenuItem> Items { get; private set; }
    public MenuItem DefaultItem 
    {
        get 
        {
            return Items.Single(x => x.IsDefault);
        }
        set
        {
            DefaultItem.IsDefault = false;
            value.DefaultItem = true;
        }
    }
}

public class MenuItem
{
    public SystemMenu Parent { get; set; }
    public string Name { get; set; }
    public bool IsDefault { get; set; }
}

Has anyone dealt with something similar and could offer some advice?

Cheers!

Edit: Thanks for the responses so far, perhaps the 'Menu' example wasn't brilliant though, I was trying to think of something representative so I didn't have to go into the specifics of our not-so-self-explanatory domain model! Perhaps a better example would be a Company/Employee relationship:

public class Company
{
    public string Name { get; set; }
    public IList<Employee> Employees { get; private set; }
    public Employee ContactPerson { get; set; }
}

public class Employee
{
    public Company EmployedBy { get; set; }
    public string FullName { get; set; }
}

The Employee would definitely need a reference to their Company, and each Company could only have one ContactPerson. Hope this makes my original point a bit clearer!

+6  A: 

Your solution seems quite reasonable.

Another thing to think about is that your objects in memory don't have to exactly match the database schema. In the database you can have the simpler schema with the child properties, but in memory you can optimize things and have the parent with references to the child objects.

David Norman
+2  A: 

Maybe a self-referential GoF Composite pattern is an order here. A Menu has a collection of leaf MenuItems, and both have a common interface. That way you can compose a Menu out of Menus and/or MenuItems. The schema has a table with a foreign key that points back to its own primary key. Works with walking menus that way, too.

duffymo
+25  A: 

The trick to solving this is to realize that the parent does not need to know about all of the methods of the child, and that the child does not need to know all the methods of the parent. Therefore you can use the Interface Segregation Principle to decouple them.

In short, you create an interface for the parent that has only those methods that the child needs. You also create an interface for the child that has only those methods that the parent needs. Then you have the parent contain a list of the child interfaces, and you have the child point back to the parent interface. I call this the Flip Flob Pattern because the UML diagram has the geometry of an Eckles-Jordan flip-flop (Sue me, I'm an old hardware engineer!)

  |ISystemMenu|<-+    +->|IMenuItem|
          A    1  \  / *     A
          |        \/        |
          |        /\        |
          |       /  \       |
          |      /    \      |
          |     /      \     |
    |SystemMenu|        |MenuItem|

Notice that there is not cycle in this diagram. You cannot start at one class and follow the arrows back to your starting point.

Sometimes, in order to get the separation just right, you have to move some methods around. There might be code that you thought should have been in the SystemMenu that you move to the MenuItem, etc. But in general the technique works well.

Uncle Bob
AS usual, Uncle Bob talks sense - a very simple application of lateral thinking. (P.S. My input is in no way biased by the fact that I, also, am an 'Uncle Bob')
belugabob
To understand Uncle Bob, you must first understand (what it means to be an) Uncle Bob
Kyle Walsh
This seems like an complex solution to a simple problem. Interface driven programming works well, but what's the point of polluting the namespace when it's not necessary? There is no issue with having circular reerences in C# so long as you don't follow them infinitely.
Mystere Man
I wouldn't use interfaces to represent entities. Your menu should point to specific menuitems and not to something representing a menu item.
Paco
Very nice, wish I'd read this in time to implement it! Although, like Paco, I'm not entirely comfortable using interfaces to represent entities (it's been heavily abused on this project and is a constant pain point...)
Jon M
Nice. I like this answer
RichardOD
+3  A: 

I don't really see your problem. Clearly you're using C#, which holds objects as references not instances. This means it's perfectly fine to have cross-referencing, or even self-referencing.

in C++ and other languages where objects are more compositied then you can have problems, which are typically solved using references or pointers, but C# should be fine.

More than likely your problem is that you're trying to follow all references somehow, leading to a circular reference. LINQ uses lazy loading to address this issue. For instance, LINQ won't load the Company or the Employee until you reference it. You just need to avoid following such references further than one level.

However, you can't really add two tables as each others foreign key, otherwise you would never be able to delete any record, since deleting an employee would require deleting the company first, but you can't delete the company without deleting the employee. Typically, in this case, you would only use one as a real foreign key, the other would simply be a psuedo-FK (that is, one that is used as an FK but doesn't have constraints enabled). You have to decide which is the more important relationship.

In the company example, you would likely want to delete the employee but not the company, so make the company->employee FK the constraint relationship. This prevents you from deleting the company if there are employees, but you can delete employees without deleting the company.

Also, avoid creating new objects in the constructor in these situations. For instance, if your Employee object creates a new Company object, which includes a new employee ojbect created for the employee, it will eventually exhaust memory. Instead, pass the objects already created to the constructor, or set them after construction, possibly by using an initalization method.

For instance:

Company c = GetCompany("ACME Widgets");
c.AddEmployee(new Employee("Bill"));

then, in AddEmployee, you set the company

public void AddEmployee(Employee e)
{
    Employees.Add(e);
    e.Company = this;
}
Mystere Man
+1  A: 

In code, you need to have references both ways to reference things both ways. But in the database, you only need the reference one way to make things work. Because of the way joins work, you only need to have the foreign key in one of your tables. When you think about it, every foreign key in your database could be flipped around, and create and create a circular reference. Best to just pick one record, in this case probably the child with a foreign key to the parent, and just be done.

Kibbee
+1  A: 

In a domain driven design sense way, you can choose to avoid bidirectional relations between entities where it's possible. Choose one "aggregate root" to hold the relations, and use the other entity only when navigation from the aggregate root. I try to avoid bidirectional relations where it's possible. Because of YAGNI, and it will make you ask the question "what was first, the chicken or the egg?" Sometimes you will still need bidirectional associations, then choose one of the solutions mentioned earlier.

/// This is the aggregate root
public class Company
{
    public string Name { get; set; }
    public IList<Employee> Employees { get; private set; }
    public Employee ContactPerson { get; set; }
}

/// This isn't    
public class Employee
{
    public string FullName { get; set; }
}
Paco
A: 

You can enforce foreign keys in the database where two tables refer to each other. Two ways come to mind:

  1. The default child column in the parent is initially null and is only updated once all the child rows have been inserted.
  2. You defer constraint checking until commit time. This means you can insert first the parent with an initially broken reference to the child, then insert the child. One problem with deferred constraint checking is that you can end up with database exceptions being thrown at commit time which is often inconvenient in many db frameworks. Also, it means you need to know the primary key of the child before you insert it which may be awkward in your setup.

I've assumed here that the parent menu item lives in one table and the child in a different table but the same solution would work if they are both in the same table.

Many DBMS's support deferred constraint checking. Possibly yours does too although you don't mention which DBMS you are using

Adrian Pronk
A: 

Thanks to all who answered, some really interesting approaches! In the end I had to get something done in a big hurry so this is what I came up with:

Introduced a third entity called WellKnownContact and corresponding WellKnownContactType enum:

public class Company
{
    public string Name { get; set; }
    public IList<Employee> Employees { get; private set; }
    private IList<WellKnownEmployee> WellKnownEmployees { get; private set; }
    public Employee ContactPerson
    {
        get
        {
            return WellKnownEmployees.SingleOrDefault(x => x.Type == WellKnownEmployeeType.ContactPerson);
        }
        set
        {                
            if (ContactPerson != null) 
            {
                // Remove existing WellKnownContact of type ContactPerson
            }

            // Add new WellKnownContact of type ContactPerson
        }
    }
}

public class Employee
{
    public Company EmployedBy { get; set; }
    public string FullName { get; set; }
}

public class WellKnownEmployee
{
    public Company Company { get; set; }
    public Employee Employee { get; set; }
    public WellKnownEmployeeType Type { get; set; }
}

public enum WellKnownEmployeeType
{
    Uninitialised,
    ContactPerson
}

It feels a little cumbersome but gets around the circular reference issue, and maps cleanly onto the DB which saves trying to get LINQ to SQL to do anything too clever! Also allows for multiple types of 'well known contacts' which is definitely coming in the next sprint (so not really YAGNI!).

Interestingly, once I came up with the contrived Company/Employee example it made it MUCH easier to think about, in contrast to the fairly abstract entities that we're really dealing with.

Jon M