0
votes

I'm trying to create a test application that shows event dependencies: i.e. a resulting dependency graph (as opposed to just a tree graph). Something like:

public class Event() {
    public virtual int Id {get;set;}
    public virtual IList<Event> Dependencies {get;set;}
}

The other requirement is that I be able to traverse the graph in both directions: given any one event, I can access its dependencies as well as its prerequisites using Nhibernate.

Many events can be dependent on one event occuring... but also any given event may depend on many other events. What should the model look like (or does this require more than one model)? How can it be mapped using Fluent NHibernate? And is there a configuration/mapping that would protect against circular references?

2

2 Answers

0
votes

We do something similar to this and the way its mapped is that the dependent Events should have a column that map up to a parent event. This creates the necessary parent/child relationship that you need for the mapping to be valid and prevent some kind of circular reference. We switched to NH 3.2 by code mappings, so my fluent might be a little shoddy but heres my best guess:

public class EventMap : ClassMap<Event>
{
    public EventMap()
    {
       //the normal ID and property stuff

       References(x => x.ParentEvent).Column("ParentEventId");

       HasMany(x => x.Dependencies).KeyColumn("ParentEventId");
    }
}

Edit:

Sorry - didn't see you wanted a HasManyToMany. That might look something like:

public class EventMap : ClassMap<Event>
    {
        public EventMap()
        {
           //the normal ID and property stuff

           HasManyToMany(x => x.Dependencies).Table("EventDependentEvent").AsBag();
        }
    }

This should map away the linking table you will need. You will need to guard against some amount of the "circularness" yourself - that is, make sure in your business logic that you can't create a loop or some kind of massive object graph dependency issue.

0
votes

The solution I ended up with (so far)...

In the Model:

    public virtual IList<Event> Dependencies{ get; set; }
    public virtual IList<Event> Prerequisites{ get; set; }

In the Mapping:

        HasManyToMany(x => x.Dependencies)
            .Table("Dependencies")
            .ChildKeyColumn("Dependent");
        HasManyToMany(x => x.Prerequisites)
            .Table("Prerequisites")
            .ChildKeyColumn("Prerequisite");

And I prevent circular ref's by looking for a false from:

    private bool IsPrerequisiteEvent(Event dependent, Event prereq)
    {
        bool isPrereq = false;
        if (prereq == null)
            isPrereq = false;
        else if (dependent.Id == prereq.Id)
            isPrereq = true;
        else 
        {
            int i = 0;
            while (!isPrereq && i < dependent.PrerequisiteEvents.Count)
            {
                isPrereq |= IsPrerequisiteEvent(dependent.PrerequisiteEvents[i], prereq);
                i++;
            }
        }
        return isPrereq;
    }

    private bool IsDependentEvent(Event prereq, Event dependent)
    {
        bool isDependent = false;
        if (prereq == null)
            isDependent = false;
        else if (dependent.Id == prereq.Id)
            isDependent = true;
        else
        {
            int i = 0;
            while (!isDependent && i < dependent.DependentEvents.Count)
            {
                isDependent |= IsDependentEvent(prereq, dependent.DependentEvents[i]);
                i++;
            }
        }
        return isDependent;
    }

There are tradeoffs to this approach: the db IS denormalized, but I don't have to create a new dependency object and populate a list of dependent/prereq tickets for each ticket and then do my checks. Seems easier to code this way for now. Open to suggestions though!