0

I'm trying to build a fluent API for method chaining. These are the methods

public interface IBaseRelationships
{
    IEnumerable<Person> Parents(IEnumerable<Person> people);
    IEnumerable<Person> Children(IEnumerable<Person> people);
    IEnumerable<Person> Siblings(IEnumerable<Person> people);
}

And the implementation

   public class BaseRelationships : IBaseRelationships
    {
        public BaseRelationships(IFamilyGraph familyGraph)
        {
            FamilyGraph = familyGraph;
        }

        public IFamilyGraph FamilyGraph { get; }

        public IEnumerable<Person> Parents(IEnumerable<Person> people)
        {
            List<Person> result = new List<Person>();
            foreach (var person in people)
            {
                IPersonRelationships personRelationships = FamilyGraph.Get(person);
                result.AddRange(personRelationships.Parents);
            }
            return result;
        }
        public IEnumerable<Person> Children(IEnumerable<Person> people)
        {
            List<Person> result = new List<Person>();
            foreach (var person in people)
            {
                IPersonRelationships personRelationships = FamilyGraph.Get(person);
                List<Person> children = personRelationships.Edges.Where(m => m.RelationshipType == RelationshipType.Parent)
                    .Select(m => m.Target)
                    .ToList();
                result.AddRange(children);
            }
            return result;

        }
        public IEnumerable<Person> Siblings(IEnumerable<Person> people)
        {
            List<Person> result = new List<Person>();
            return result;
        }
    }
}

I'd like to do something like this.

var person = new List<Person> {new Person()};
var cousins = person.Parents().Siblings().Children();

I know with this current implementation that is not possible and I might have to write extension methods. For that, the classes which contain the extension methods have to be static hence I can't inject the FamilyGraph dependency.

With the current implementation if I return IBaseRelationships instead of IEnumerable<Person> I'll be able to get this to work. But I'm not sure how I'll be able to get the actual result (IEnumerable<Person>) out of it.

Any idea on how to build this for the methods in the interface would be great.

Edit. Addding FamilyGraph fore reference

public class FamilyGraph : IFamilyGraph
{
    private Dictionary<Person, PersonRelationships> Families;
    public FamilyGraph(IPersonStore personStore)
    {
        Families = new Dictionary<Person, PersonRelationships>();
        PersonStore = personStore;
    }
    public IPersonStore PersonStore { get; }

    public void Add(EdgeInput inputEdge)
    {
        Edge edge;
        try
        {
            edge = GetEdge(inputEdge);
        }
        catch (ArgumentException)
        {
            throw;
        }
        switch (edge.RelationshipType)
        {
            case Enums.RelationshipType.Parent:
                AddParentRelationship(edge);
                return;
            case Enums.RelationshipType.Spouse:
                AddSpouseRelationship(edge);
                return;
        }
    }
    public Edge GetEdge(EdgeInput inputEdge)
    {
        Person source, target;
        try
        {
            source = PersonStore.GetPerson(inputEdge.Source);
            target = PersonStore.GetPerson(inputEdge.Target);
        }
        catch (Exception)
        {

            throw;
        }
        return new Edge(source, target, inputEdge.RelationshipType);
    }
    public IPersonRelationships Get(Person person)
    {
        PersonRelationships personRelationships;
        Families.TryGetValue(person, out personRelationships);
        return personRelationships;
    }
}

public interface IPersonRelationships
    {
        List<Edge> Edges { get; }
        List<Person> Parents { get; }
        Person Spouse { get; }

        void AddEdge(Edge edge);
        void AddParent(Person parent);
        void AddSpouse(Person spouse);
        bool CanAddParent(Person parent);
    }

public interface IPersonStore
{
    void Add(Person person);
    bool Contains(string personName);
    Person GetPerson(string personName);
}
6
  • What is FamlyGraph? Is it a store with all the relationships? Commented Jun 15, 2019 at 13:35
  • 1
    Extension methods would probably be ideal, perhaps there's a different way to inject the dependency? I hate to suggest a Service Locator, but it might do the trick. Or, failing that, perhaps go with your idea to return IBaseRelationships and then just add one more method to the interface which returns the final IEnumerable<Person>. It limits the fluent syntax because once the final one is called you'd need to start a new chain any time after it, but that may not end up being so bad in the usage. Commented Jun 15, 2019 at 13:44
  • 1
    assuming you can't control the person and familygraph classes it might be easiest to introduce extension methods to transform to/from the releastionship class so it would look like: person.GetRelationships(familygraph).Parents().Siblings().Children().ToPersons() @David - didn't see your comment to start, but I believe this is essentially what you mean as well. Commented Jun 15, 2019 at 13:55
  • @thebenman IPersonRelationships ? just the public interface is enough Commented Jun 16, 2019 at 6:54
  • @AvinKavish Added Commented Jun 16, 2019 at 6:56

1 Answer 1

2

Let's start at the type of public API you want to expose, I've made a small adjustment to allow individuals as well as collectives.

var person = new Person();
var people = new List<Person> { person };
var cousins = person.Parents().Siblings().Children();
// Or
var cousins = people.Parents().Siblings().Children();

Since you want to start chaining on a person/people, this person needs to be aware of the FamilyGraph they belong to.

public class Person {

    public FamilyGraph FamilyGraph { get; set; }

    IEnumerable<Person> Parents() => FamilyGraph.Get(person).Parents;

    IEnumerable<Person> Children() => 
        FamilyGraph.Get(person).Edges
          .Where(m => m.RelationshipType == RelationshipType.Parent)
          .Select(m => m.Target)
          .ToList();

    IEnumerable<Person> Siblings() => FamilyGraph.Get(person)./* your logic here */;
}


public static class PeopleExtensions 
{
    public static IEnumerable<Person> Parents(this IEnumerable<Person> people) =>
        people.SelectMany(person => person.Parents()).ToList();

    public static IEnumerable<Person> Siblings(this IEnumerable<Person> people) =>
        people.SelectMany(person => person.Siblings()).ToList();

    public static IEnumerable<Person> Children(this IEnumerable<Person> people) =>
        people.SelectMany(person => person.Children()).ToList();

}
Sign up to request clarification or add additional context in comments.

3 Comments

This is great. But making the Person object FamilyGraph aware is hard since it is the other way around. FamilyGraph depends on the Person objects. And I do not feel great about passing the FamilyGraph with every method call.
Then you can't chain it starting at a person. You will have to start at the FamliyGraph and have an intermediary type that carries the FamilyGraph forward. I.e FamilyGraph.query(person).Parents().Siblings().Cousins() query would return something like IFamilyGraphQueryable
Can you show me the FamilyGraph class and the return type of FamilyGraph.get

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.