views:

143

answers:

2

Hey, I've been searching around for a solution to a tricky problem we're having with our code base.

To start, our code resembles the following:

class User
{ 
   int id;
   int accountId;

   Account account
   {
     get { return Account.Get(accountId); }
   }     
}

class Account
{
   int accountId;
   OnlinePresence Presence
   {
      get { return OnlinePresence.Get(accountId); }  
   } 
   public static Account Get(int accountId)
   {
      // hits a database and gets back our object.
   }
}

class OnlinePresence
{
   int accountId;
   bool isOnline;
   public static OnlinePresence Get(int accountId)
   {
      // hits a database and gets back our object.
   }
}

What we're often doing in our code is trying to access the account Presence of a user by doing

var presence = user.Account.Presence;

The problem with this is that this is actually making two requests to the database. One to get the Account object, and then one to get the Presence object. We could easily knock this down to one request if we did the following :

var presence = UserPresence.Get(user.id);

This works, but sort of requires developers to have an understanding of the UserPresence class/methods that would be nice to eliminate.

I've thought of a couple of cool ways to be able to handle this problem, and was wondering if anyone knows if these are possible, if there are other ways of handling this, or if we just need to think more as we're coding and do the UserPresence.Get instead of using properties.

  1. Overload nested accessors. It would be cool if inside the User class I could write some sort of "extension" that would say "any time a User object's Account property's Presence object is being accessed, do this instead".

  2. Overload the . operator with knowledge of what comes after. If I could somehow overload the . operator only in situations where the object on the right is also being "dotted" it would be great.

Both of these seem like things that could be handled at compile time, but perhaps I'm missing something (would reflection make this difficult?). Am I looking at things completely incorrectly? Is there a way of enforcing this that removes the burden from the user of the business logic?

Thanks! Tim

A: 

Why not go with the Repository pattern and have the following:

public class UserPresenceRepository
{
    public UserPresenceRepository(string connString)
    {
        // configure db properties
    }

    public UserPresence GetPresence(User user)
    {
        // get precense from user.accountId if possible 
        // and skip the trip for Account
    }
}

So your calling code would look like:

UserPresenceRepository repo = new UserPresenceRepository(connString);
repo.GetPresence(user);

The end result is a clear definition of a Repository to call if you need User Presence information. The GetPresence method also clearly operates on the User object instead of requiring developers to know which Id to pass in.

The other option here would to stop lazy loading your objects. That way you can load everything you need in one trip to the database and have it ready.

Justin Niessner
A: 

Think it's better the Proxy pattern.

class User
{
    int id;
    int accountId;

    Account Account
    {
        get { return new ProxyAccount(accountId); }
    }
}

abstract class Account
{
    protected int accountId;

    protected Account(int accountId)
    {
        this.accountId = accountId;
    }

    public OnlinePresence Presence
    {
       get { return new ProxyOnlinePresence(accountId); }
    }

    /*
        other properties of the Account go here as abstract properties

        public abstract string SomeProperty { get; set; }

    */

    public static Account Get(int accountId)
    {
        // hits a database and returns an instance of DBAccount.
    }
}

class ProxyAccount : Account
{
    private Account account;
    public ProxyAccount(int accountId) : base(accountId)
    {
    }

    private Account GetAccount()
    {
        if (account == null)
            account = Account.Get(accountId);
        return account;
    }

    /*
        Accounts abstract properties are implemented here

        public override string SomeProperty
        {
            get { return GetAccount().SomeProperty; }
            set { GetAccount().SomeProperty = value; }
        }
    */
}

class DBAccount : Account
{
    public DBAccount(int accountId) : base(accountId)
    {
    }

    /*
        Accounts abstract properties are implemented here

        public override string SomeProperty { get; set; }
    */
}

I just showed you the way only with Account class, but you also have to do it with OnlinePresence as well (as you can see, I've assumed you'll have a ProxyOnlinePresence class).

Fede