1

I'm modelling a system that is a combination of two distinct yet similar data repositories**: one containing patent data and one containing trademark data. There is a very large overlap in the schemae, i.e. each database contains much of the same type of information.

To get more specific: there are "cases" -- patent cases and trademark cases -- the data for each being stored in the respective database. A case has a lot of the same basic data regardless of whether it's a patent case or a trademark case, but, of course, there is also data that is unique to each type of case. There are also "actions" associated with every case. Unlike cases, though, actions share the exact same schema in both the patent and the trademark database.

In my application, I have a Case class with PatentCase and TrademarkCase that derive from it. A Case has a collection of Action objects. The Action also has a reference to its associated case.

I am employing lazy loading in both directions: Case with its associated Actions, and Action with its associated Case. Now I know I could simply use dependency injection to give each Action a reference to its associated Case. However, what I'm not showing here is that an Action could be used elsewhere and might be loaded before its associated case is loaded, thus the bi-directional lazy-loading.

Where I'm struggling is how to figure out what type of Case to lazy-load from the Action (see comment in CaseLoader.GetCase). I don't want to have to store the type of action (or do I?) when the action is loaded from the respective repository, because then the action has a concern that it should not have, namely what repository it came from. But I can't think of a better way to do it.

What is the best way to determine what type of Case is associated with an Action?

Here is the code:

public abstract class Case
{
    private ReadOnlyCollection<Action> _associatedActions;

    public ReadOnlyCollection<Action> AssociatedActions
    {
        get
        {
            if (_associatedActions == null) _associatedActions = ActionLoader.GetActions(this);
            return _associatedActions;
        }
    }
}

public class PatentCase : Case
{
    // patent stuff
}

public class TrademarkCase : Case
{
    // trademark stuff
}

public class Action
{
    private int _associatedCaseID;
    private Case _associatedCase;

    public Case AssociatedCase
    {
        get
        {
            if (_associatedCase == null) _associatedCase = CaseLoader.GetCase(this);
            return _associatedCase;
        }
    }
}

internal static class ActionLoader
{
    internal static ReadOnlyCollection<Action> GetActions(Case forCase)
    {
        if (forCase is PatentCase)
        {
            // retrieve actions from data layer, and return.
        }
        else if (forCase is TrademarkCase)
        {
            // retrieve actions from data layer, and return.
        }
        else
        {
            throw new ArgumentException("The argument is not a derivation of case that this method knows about.");
        }
    }
}

internal static class CaseLoader
{
    internal static Case GetCase(Action forAssociatedAction)
    {
        // what is the best way to determine here what type of case is associated with the action?
    }
}


** The databases belong to a third-party vendor. While, in another world, I could envision all of this existing in one database where all common data is better organized (thus making my task easier), the reality is they sell their products as separate packages -- we have purchased their "patent" and "trademark" packages -- hence the separate databases. I'm simply trying to make a wrapper that effectively combines them.

  • Do the Actions have their own id? – Erik Eidt Feb 25 '16 at 17:51
  • Yes, they do..... – rory.ap Feb 25 '16 at 17:51
  • Ok, is the Action id unique across both the patent and trademark databases or just unique within on of them? And, next, are you capturing the action id within the Action object? – Erik Eidt Feb 25 '16 at 17:52
  • Action ID is not guaranteed to be unique across both databases, only within their respective db, and yes I'm capturing the ID within the object. – rory.ap Feb 25 '16 at 17:53
  • Then Action is already tied to a particular database, and I would capture that as well, because you can't use the id without knowing which database. Put another way, once you consider both databases, then an Action id key becomes the db primary key together with which db. In other words, there is no value in the present design having Action pretending it doesn't know which db it came from. (You could create a design that separates the db-specific part of Action from the rest of Action, but you should have a reason to do that, and as yet I don't see one.) – Erik Eidt Feb 25 '16 at 17:55
1

I would suggest having an CaseReference class which contains all the information required to find the case (target DB and case ID). You have two options with this approach:

class CaseReference {
    public CaseDatabase Database { get;set; } // enum which identifies the target database
    public int CaseId { get;set; }
}

Or you can use polymorphism:

abstract class CaseReference {
    public int CaseId { get;set; }
    // other methods which provide interface for logic to connect to database
}
class PatentCaseReference : CaseReference { ... }
class TrademarkCaseReference : CaseReference { ... }

Then your Action class needs to store a CaseReference instead of just the case ID. Your CaseLoader can either inspect the value of CaseReference.Database to determine which database to open in the first case, or defer to the logic in the CaseReference class in the second case.

  • Thanks for your answer. I think what your answer and the comments under my question seem to have in common is that there is no way I can get around having the Action (I know that's what you probably meant instead of Case when you said "Then your Case class needs to store a CaseReference") have knowledge of its source repository. – rory.ap Feb 26 '16 at 12:45
  • Yep, fixed the typo. – Erik Feb 26 '16 at 17:45

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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