Allow client code to use a "tunneling" style, such as
string columnName = db.Tables["Customers"].Columns["customer_number"].Name;
[...] I want to save collection classes for each, exposing them as properties, and not using an index. Does this sound right?
Yes, it sounds better than indexers, because it helps improve type safety. If you open a regular collection, each of them can have its own element type. Using the indexer, you must select the same collection type for all open collections. To illustrate this fact:
public DataTable this[string tableName] { ... }
vs.
public IList<Customer> Customers { get { ... } } public IList<Supplier> Suppliers { get { ... } }
So, yes, expose single "typed" collections instead of the "untyped" collection collections.
Side note . You say you want to enable tunneling; some people may tell you that this violates some generally accepted principles of OO, such as the principle of single responsibility (SRP) or separation of concerns (SoC).
- prevent client applications from directly creating instances of contained objects
public class Db { public IList<Customer> Customers { get { ... } } ... }
“Contained objects” can mean two things here:
Someone may replace your Customers collection. You simply prevent this by creating a read-only property, i.e. only provide getter.
Someone can insert a new Customer into your db.Customers collection. You prevent this by choosing a collection type (read-only) for Customers that does not allow insertion.
For instance:
private List<Customer> customers = new List<Customer>(); // ^ for class-internal use only public IList<Customer> Customers { get { return this.customers.AsReadOnly(); } } // ^^^^^^^^^^^^^ // expose collection as read-only
- work with an unlimited number of contained objects and detect
This again can mean two things:
Your db provides an unlimited number of collections. Obviously this will not work if you want each collection to be specially typed. This only works when all your collections are displayed as having the same type (for example, DataTable s or as non-generic ICollection s), and you will need to provide an index. That is, you make your numerous collections accessible through one collection that you open with the indexer.
You publish a known and final number of collections (e.g. Customers , Suppliers , etc.). However, each of them can contain as many sub-objects as you want. I assumed this scenario when I wrote the above parts of my answer. The benefits are the best type of security and compilation time checking when tunneling.
Regarding the last point:
db.Customers["John Smith"].BillingAddress
vs.
db.Tables["Customers"].Rows["John Smith"].Columns["BillingAddress"] as Address
In the first case, the compiler can check the types of properties, such as Customers , BillingAddress , while in the second case, these properties are encoded as strings, and the compiler will not notice if you have typos there. In addition, you must specify the correct data type yourself using cast-types.