using System; using System.Collections.Generic; using System.Text; using LoaderColl = System.Collections.Generic.IList; namespace SubSonic { /// /// A delegate to handle loading of single objects, generally for use when loading a foreign key relationship /// from a parent object. /// /// Expected type of the object to load /// The object's primary key value /// An object, or null if none available public delegate IActiveRecord SingleObjectLoader(Loaders.SingleObjectData data); /// /// A delegate to use for callback purposes when an item is loaded later in the "chain" /// /// The found value /// The ID used to find the value public delegate void SingleObjectLoadedCallback(IActiveRecord value, object id); /// /// A generic delegate that roughly matches up to the various FetchById functions /// /// The Item type to fetch /// The key type to fetch with /// The primary key value to fetch /// An object, or null if none available public delegate ItemType FetchByDelegate(KeyType id) where ItemType : IActiveRecord; /// /// A utility class to use when loading object relationships /// public static class Loaders { public class SingleObjectData { private readonly Type type; public Type Type { get { return type; } } private readonly object id; public object ID { get { return id; } } private readonly IActiveRecord parent; public IActiveRecord Parent { get { return parent; } } public SingleObjectData(Type type, object id, IActiveRecord parent) { this.type = type; this.id = id; this.parent = parent; } } /// /// Converts a FetchByDelegate to a SingleObjectLoader delegate /// /// The Item type to fetch /// The key type to fetch with /// The FetchByDelegate to convert /// A SingleObjectLoader public static SingleObjectLoader ConvertToSingleObjectLoader(FetchByDelegate fetchBy) where ItemType : IActiveRecord { return delegate(SingleObjectData data) { return fetchBy((KeyType)data.ID); }; } /// /// Loads a single object using the specified ID, a ranked collection of object loaders, and a fallback loader /// /// The type of object to load /// Primary key to load with /// A set of loaders to check /// A fallback loader to use if no other loaders yielded results /// The specified object, or null if not found public static T LoadSingleObject(object id, LoaderColl loaders, SingleObjectLoader fallbackLoader, IActiveRecord parent) where T : class, IActiveRecord { T v = null; Loaders.SingleObjectData data = new SingleObjectData(typeof(T), id, parent); if (loaders != null) { int foundIndex = -1; // Checks the list of loaders for a matching object for(int i = 0; i < loaders.Count; i++) { SingleObjectLoader loader = loaders[i].Loader; if(loader != null && (v = loader(data) as T) != null) { foundIndex = i; break; } } // If not found, uses the fallback loader. Sets the index to the collection count // so all callbacks will be referenced if(v == null) { v = fallbackLoader(data) as T; foundIndex = loaders.Count; } // Runs through relevant callbacks for(int i = 0; i < foundIndex; i++) { SingleObjectLoadedCallback callback = loaders[i].Callback; if (callback != null) { callback(v, id); } } } return v ?? fallbackLoader(data) as T; } } public class LoaderCallbackPair { private readonly SingleObjectLoader loader; public SingleObjectLoader Loader { get { return loader; } } private readonly SingleObjectLoadedCallback callback; public SingleObjectLoadedCallback Callback { get { return callback; } } public LoaderCallbackPair(SingleObjectLoader loader, SingleObjectLoadedCallback callback) { this.loader = loader; this.callback = callback; } } }