2

I'm currently in the process of rewriting an older library for managing documents.

I'm having difficulties to specify an interface for the management of links between those documents. Previously they have been specified simply as the primary keys of the other documents. But in order to have a more object-oriented interface the primary keys should be replace with objects of the actual documents.

Now the implementation of the AddLink/RemoveLink methods are a problem because if you add a link from document A to document B another link from B to A is automatically added.

A more general description of the problem would be: How can you change properties that depend on each other but are in different objects (possibly behind interfaces).

Things I've tried so far:

  • Using internal methods to change the links "behind the back" of the object

    This only works for normal classes but when interfaces are involved (my case) it won't work.

  • Using some kind of recursion detection mechanism, to simply stop at that point

    This works but it's a hack in my opinion

  • Using event handlers (doesn't work for adding, only for removing)

  • Giving up on the idea of having direct access to the objects and just continue to use the primary keys

Example Code:

class Document
{
    private List<Document> links;

    public IReadOnlyList<Document> Links { get { return links.AsReadOnly(); } }

    public void AddLink(Document other)
    {
        links.Add(other);
        // How to add this to other.Links?
        // Just calling other.AddLink(this) would cause infinite recursion
    }
}

1 Answer 1

0

OK, I don't see the advantage of having the object rather than the id, but..

one way to do this is to inject a Document/Link repository. so when you add the link, it gets added (as a two way link) to the repository. Both documents generate their links when queried by checking the (shared) repository.

ie.

    /// <summary>
    /// inject into documents, this access a shared database or its a shared instance etc
    /// </summary>
    public interface IRepository
    { 
        Document GetDocument(string id);

        /// <summary>
        /// get all the links which are linked to or from the Id
        /// </summary>
        /// <param name="documentId"></param>
        List<Document> GetLinks(string documentId);

        /// <summary>
        /// add link refernce both docs
        /// </summary>
        /// <param name="linkFromId"></param>
        /// <param name="linkToId"></param>
        void AddLink(string linkFromId, string linkToId);
    }

    public class Document
    {
        public string Id {get;set;}

        private IRepository repo;

        public Document(IRepository repo)
        {
            this.repo = repo;
        }

        public IReadOnlyList<Document> Links 
        { 
            get 
            { 
                return repo.GetLinks(this.Id).AsReadOnly();
            } 
        }

        public void AddLink(Document other)
        {
            repo.AddLink(this.Id,other.Id);
        }
    }
9
  • So you mean don't have the links field in the object but rather have the property query the shared repository every time? Oct 2, 2015 at 12:04
  • yup, obvs you can improve with caching if required.
    – Ewan
    Oct 2, 2015 at 12:05
  • tbh though i would stick with the Ids, just think about the memory footprint
    – Ewan
    Oct 2, 2015 at 12:06
  • Also with access directly to the objects one can create relatively simple queries like "get all linked documents that have been create before X". The objects lazy load the data if it's requested. Oct 2, 2015 at 12:10
  • Regarding memory footprint: Most of the time the id alone is useless because I have to show the name or something in the UI. So I need to load some data anyway. Oct 2, 2015 at 12:11

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.