views:

137

answers:

2

Hi,

What would the basic C# code look like to model a many-to-many relationship, where the relationship itself has attributes? And also in this case the many-to-many was referential. So a possible database model for this might look like the following (just to give an example of what I'm talking about)

  • Nodes
    • ID
    • Name
    • Description
  • Relationships
    • Parent_ID
    • Child_ID
    • Relationships_Type
+1  A: 
public class Node
{
     public int Id {get;set;}
     public string Name {get;set;}
     public string Description{get;set;}
     public Dictionary<RelationShipType,IEnumerable<Node>> ChildNodes {get;set;}
}

public enum RelationShipType
{
   ....
}
Gregoire
+1  A: 
public class Node
{
    // General properties

    public List<Relationship> Relationships { get; set; }
}

public class Relationship
{
    public Node Parent { get; set; }
    public Node Child { get; set; }
    public RelationshipType Type { get; set; }
}

public enum RelationshipType
{
    //...
}

The most important (and easily botched) component of this is the Relationships property on the Node class. The way I defined is the easiest way, but a more reliable way would be to model it in more of a database fashion, where you have a central relationship repository and the Node is connected.

public class RelationshipRepository
{
    private List<Relationship> relationships = new List<Relationship>();

    public IEnumerable<Relationship> GetRelationships(Node node)
    {
        return relationships.Where(r => r.Child == node || r.Parent == node);
    }

    public IEnumerable<Relationship> GetChildRelationships(Node node)
    {
        return relationships.Where(r => r.Parent == node);
    }

    public IEnumerable<Relationship> GetParentRelationships(Node node)
    {
        return relationships.Where(r => r.Child == node);
    }

    public void Add(Node parent, Node child, RelationshipType type)
    {
        relationships.Add(new Relationship()
        {
            Parent = parent,
            Child = child,
            Type = type
        });

        parent.RelationshipSource = this;
        child.RelationshipSource = this;
    }
}

public class Node
{
    // General properties

    public RelationshipRepository RelationshipSource { get; set; }

    public IEnumerable<Relationship> Relationships 
    { 
        get { return relationships.GetRelationships(this); }
    }

    public IEnumerable<Relationship> Children
    { 
        get { return relationships.GetChildRelationships(this); }
    }

    public IEnumerable<Relationship> Parents
    { 
        get { return relationships.GetParentRelationships(this); }
    }
}

This would allow for you to create a single RelationshipRepository instance, add your relationships between Nodes using the Add function, and it will take care of the rest. Subsequent calls to Relationships, Children, or Parents on one of the affected Nodes will automatically examine the relationshipSource to determine the children, parents, or all relationships.

Adam Robinson
thanks Adam - can I ask in the class/object world how do the foreign key relationships work - i.e. if I have two nodes and a relationships that ties them, what happens if I delete a node from the node list? I'm guessing the node would disappear from the list but the relationship would still be present due to the relationship reference? Like is it up to the programmer to write wrapper methods (like your AddNode) to take care of this? i.e. there's no built in equivalent to a database foreign key constraint?
Greg
@Greg: It can be done, but you'd need to make the setter for the `Child` and `Parent` properties `internal`, add functions to accomplish what you want to the `RelationshipRepository`, then keep the business logic in another assembly. This will force developers to go through the repository to make changes. You could also add something like `RemoveChild` and `RemoveParent` to the `Node` class that would just call these functions that are on the repository. The same could go for `AddChild` and `AddParent` as well.
Adam Robinson