This is part of a document management application that is in a prior question. Manage users, roles, and groups.   A user can have only one role but they can be in many groups.  There is join struct UserIDGroupID to manage the relationship for user to groups.  This the same as the DB model. A group cannot contain a group.  That makes for some long SQL and document authority is already relatively expensive.public class UserGroup{    public enum Role { Basic, Edit, DocAdmin, UserAdmin };    public static List<User> LibraryUsers { get; } = new List<User>();    public static List<Group> LibraryGroups { get; } = new List<Group>();    private static List<UserIDGroupID> UserIDsGroupIDs = new List<UserIDGroupID>();    private static SDocsServer SDocsServer;    public class User    {        public UInt16 ID { get; }        public string Name { get; }        public string Initials { get; }        public DateTime LastLogOn { get; }        public Role Role { get; }        public bool Locked { get; }        private List<Group> groups = null;        public List<Group> Groups        {            get            {                //most of the time groups will not be called also want a fast startup                if (groups == null)                {                    groups = new List<Group>();                    foreach (UserIDGroupID userIDGroupID in UserIDsGroupIDs.Where(x => x.UserID == ID))                    {                        groups.AddRange(LibraryGroups.Where(x => x.ID == userIDGroupID.GroupID));                    }                }                 return groups;            }        }        internal User(UInt16 id, string name, string initials, DateTime lastLogOn, Role role, bool locked)        {            ID = id;            Name = name;            Initials = initials;            LastLogOn = lastLogOn;            Locked = locked;            LibraryUsers.Add(this);  //not sure this is the best way         }    }    public class Group    {        public UInt16 ID { get; }        public string Name { get; }        private List<User> users = null;        public List<User> Users        {            get            {                //Users.AddRange(LibraryUsers.Where(x => x.UserIDsGroupIDs.Any(g => g.GroupID == ID)));                //most of the time Users will not be called also want a fast startup                if (users == null)                {                    users = new List<User>();                    foreach (UserIDGroupID userIDGroupID in UserIDsGroupIDs.Where(x => x.GroupID == ID))                    {                        Users.AddRange(LibraryUsers.Where(x => x.ID == userIDGroupID.UserID));                    }                }                return users;            }        }        internal Group(UInt16 id, string name)        {            ID = id;            Name = name;            LibraryGroups.Add(this);  //not sure this is the best way         }    }    //User can have only one role but many groups          internal struct UserIDGroupID    {        public UInt16 UserID { get; }        public UInt16 GroupID { get; }        public UserIDGroupID(UInt16 userID, UInt16 groupID)        {            UserID = userID;            GroupID = groupID;        }    }    internal UserGroup(SDocsServer sDocsServer)    {        SDocsServer = sDocsServer;        foreach (Group group in sDocsServer.GetGroups())        {        }        foreach (User user in sDocsServer.GetUsers())        {        }                   foreach (UserIDGroupID userIDGroupID in sDocsServer.GetUserIDGroupID())        {            //should not have duplicate - trusting server        }    }}public class UserGroupTest{    public UserGroupTest()    {        UserGroup userGroup = new UserGroup(new SDocsServer());        foreach (UserGroup.User user in UserGroup.LibraryUsers)        {            Debug.WriteLine(${user.Name} {user.Role});            foreach (UserGroup.Group group in user.Groups)            {                Debug.WriteLine($  {group.Name});            }        }        foreach (UserGroup.Group group in UserGroup.LibraryGroups)        {            Debug.WriteLine(${group.Name});            foreach (UserGroup.User user in group.Users)            {                Debug.WriteLine($  {user.Name});            }        }    }}public class SDocsServer{    //this is a whole diffent server side program     //this is thousands of line of code     //NOT asking for review of this code     //this ONLY is here so you do not get syntax error if you load the code     internal bool UpdateDocPropValue(SDocsClient.DocPropValue DocPropValue)    {        if (!Test())        {            throw new ArgumentException(server null or not available);        }        return true;    }    internal bool AddDoc(SDocsClient.Document document)    {        if (!Test())        {            throw new ArgumentException(server null or not available);        }        return true;    }    internal IEnumerable<UserGroup.User> GetUsers()    {        yield return new UserGroup.User(1, Jane Doe, JD, new DateTime(2000, 1, 1), UserGroup.Role.Basic, false);        yield return new UserGroup.User(2, John Doe, JD, new DateTime(2000, 1, 1), UserGroup.Role.Basic, false);    }    internal IEnumerable<UserGroup.Group> GetGroups()    {        yield return new UserGroup.Group(1, One);        yield return new UserGroup.Group(2, Two);    }    internal IEnumerable<UserGroup.UserIDGroupID> GetUserIDGroupID()    {        yield return new UserGroup.UserIDGroupID(1, 1);        yield return new UserGroup.UserIDGroupID(1, 2);        yield return new UserGroup.UserIDGroupID(2, 2);    }    public List<SDocsClient.Document> Search(List<SDocsClient.SearchItem> SearchItems)    {        if (!Test())        {            throw new ArgumentException(server null or not available);        }        return new List<SDocsClient.Document>();    }    public bool Test()    {        return true;    }    public SDocsServer()    {        //connect to db    }}
Manage Users and Groups
c#;.net
I tried to simplify your solution, while keeping the lazy-loading...User Classpublic class User{    public UInt16 ID { get; }    public string Name { get; }    public string Initials { get; }    public DateTime LastLogOn { get; }    public Role Role { get; }    public bool Locked { get; }    internal User(UInt16 id, string name, string initials, DateTime lastLogOn, Role role, bool locked)    {        ID = id;        Name = name;        Initials = initials;        LastLogOn = lastLogOn;        Locked = locked;    }}Group Classpublic class Group{    public UInt16 ID { get; }    public string Name { get; }    internal Group(UInt16 id, string name)    {        ID = id;        Name = name;    }}Join Classpublic struct UserIDGroupID{    public UInt16 UserID { get; }    public UInt16 GroupID { get; }    public UserIDGroupID(UInt16 userID, UInt16 groupID)    {        UserID = userID;        GroupID = groupID;    }}UserGroup Classpublic class UserGroup{    private readonly ReadOnlyDictionary<UInt16, User> _users;    private readonly ReadOnlyDictionary<UInt16, Group> _groups;    private readonly IReadOnlyList<UserIDGroupID> _joins;    private readonly Dictionary<UInt16, List<Group>> _userIdToGroups;    private readonly Dictionary<UInt16, List<User>> _groupIdToUsers;    public UserGroup(SDocsServer sDocsServer)    {        // populate the users        Dictionary<UInt16, User> users = new Dictionary<UInt16, User>();        foreach (var user in sDocsServer.GetUsers())        {            users.Add(user.ID, user);        }        _users = new ReadOnlyDictionary<UInt16, User>(users);        // populate the groups        Dictionary<UInt16, Group> groups = new Dictionary<UInt16, Group>();        foreach (var group in sDocsServer.GetGroups())        {            groups.Add(group.ID, group);        }        _groups = new ReadOnlyDictionary<UInt16, Group>(groups);        // populate the joins        _joins = sDocsServer.GetUserIDGroupID().ToList();        _userIdToGroups = new Dictionary<UInt16, List<Group>>();        _groupIdToUsers = new Dictionary<UInt16, List<User>>();    }    public IReadOnlyList<User> LibraryUsers    {        get { return _users.Values.ToList(); }    }    // lazy-loading the your joins    public IReadOnlyList<Group> GroupsByUserId(UInt16 userId)    {        if (!_groupIdToUsers.ContainsKey(userId))        {            var groups = new List<Group>();            var joins = _joins.Where(x => x.UserID == userId);            // you noted you trusted the mapping to be 100% free of errors,            // otherwise check if (joins == null)            foreach (var join in joins)            {                groups.Add(_groups[join.GroupID]);            }            _userIdToGroups.Add(userId, groups);        }        return _userIdToGroups[userId];    }    // lazy-loading the your joins    public IReadOnlyList<User> UsersByGroupId(UInt16 groupId)    {        if (!_groupIdToUsers.ContainsKey(groupId))        {            var users = new List<User>();            var joins = _joins.Where(x => x.GroupID == groupId);            // you noted you trusted the mapping to be 100% free of errors,            // otherwise check if (joins == null)            foreach (var join in joins)            {                users.Add(_users[join.UserID]);            }            _groupIdToUsers.Add(groupId, users);        }        return _groupIdToUsers[groupId];    }}Based on my understanding of your code, I think this gets you the kind of optimization you were looking for in the first place, as the methods that populate the search indexers are separately queryable.The main priority was to try and get rid of the unusual pattern of constructors and static members.ExtraIf you wanted to keep the lists within the models, such as the List<Group> Groups property in the User class, you could do something like this.public class User{    public UInt16 ID { get; }    public List<Group> Groups { get (UserGroup.GroupsByUserId(this.ID)); }}the drawback, if you consider it one, is that the UserGroup class/methods would need to become static.