0

I'm developing an ASP.NET Web Api 2 service with .NET Framework 4.5.1 and C#.

I'm following this book to do it: ASP.NET MVC 4 and the Web API Building a REST Service from Start to Finish

I have these two classes.

User:

public class User
{
    public int UserId { get; set; }
    public string UserName { get; set; }

    public virtual ICollection<Group> Groups { get; set; }
}

Group:

public class Group
{
    public int GroupId { get; set; }
    public string GroupName { get; set; }

    public ICollection<User> Members { get; set; }
}

An User could have Groups, and a Group has Users as Members.

These two classes are my Data.Models and I use a Mapper class to 'translate' them as Api.Models with these two methods:

public Models.User CreateUser(Data.Models.User modelUser)
{
    if (modelUser == null)
        return null;

    Models.User user = new Models.User()
    {
        UserId = modelUser.UserId,
        UserName = modelUser.UserName
    };

    if (modelUser.Groups != null)
        user.Groups = modelUser.Groups
            .Select(CreateGroup)
            .ToList();
}

public Models.Group CreateGroup(Data.Models.Group modelGroup)
{
    if (modelGroup == null)
        return null;

    Models.Group group = new Models.Group
    {
        GroupId = modelGroup.GroupId,
        GroupName = modelGroup.GroupName
    };

    if (modelGroup.Members != null)
        group.Members = modelGroup.Members
            .Select(CreateUser)
            .ToList();

    return group;
}

As you can see in Mapper class, CreateUser method calls CreateGroup method, and CreateGroup method calls CreateUser.

In my case user1 is member of group1 and group1 has user1 as a member. So, I get this:

  • On CreateUser foruser1 it will call CreateGroup for group1.
  • On CreateGroup for group1 it will call CreateUser for user1.
  • And so on...

Any idea about how to avoid this infinite recursive calls?

A solution could be to remove navigation properties like Groups on User, or Members in Group class from CreateUser and CreateGroup methods.

4
  • just create empty user and group and then mutate both to include the other object in their collections - that's the OO/imperative way and it's fine in this case - for an immutable/functional approach you would need to "tie the knot" Commented Sep 6, 2014 at 10:03
  • Thanks for your comment, but if I had read it in Klingon I would understand the same (is a joke, I don't speak Klingon :-)). I'm searching more information about tie the knot. Thanks. Commented Sep 6, 2014 at 10:06
  • no don't - it's indeed a bit of flapsy comment on my side - the problem is just that if you want to do something like this (mutual recursive definitions of something without mutating) it get's really tricky - just follow my first advise and create user and group and then add them to the mutual collections ;) Commented Sep 6, 2014 at 10:15
  • You have a cycle, so dont follow user/group that has already been dealt with. Commented Sep 6, 2014 at 12:03

1 Answer 1

2

There are at least two possible solutions (I can think of two but there can be more).

  1. (simple graph) split both your mapper methods into two - first one to initialize simple properties (name, id) and the second one to initialize navigation properties. Then, in a place in your code where you call the mapper, write a little bit longer script that makes use of these new methods that initialize navigation properties from user to groups but do not call the other method (from groups to users) so that you will have a graph of users poiting to groups.

  2. (full graph) add a tail parameter to each of your methods that represents a list of objects that have already been visited. Then, upon recursive call, check if the element is already in the list and only if it is not - add it and call recursively. This way, recursive calls will explore whole dependency graph between users and groups and will make sure no user and no group is processed more than once.

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

Comments

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.