3

I'm creating a library in C# which will be used to query databases.

In order to use this library, a user first needs to tell the library what the database schema contains, by creating Schema, Table, and Column objects.

A Schema object consists of:

  • A name, which is a string.
  • A collection of Table objects that it owns.

A Table object consists of:

  • A reference to the Schema object that owns it.
  • A name, which is a string.
  • A collection of Column objects that it owns.

Finally, a Column object consists of:

  • A reference to the Table object that owns it.
  • A name, which is a string.
  • A type, which is one of the values String, DateTime, and Reference.
  • If the type is Reference, then a target, which is a reference to a Table object within the same Schema.

I can think of a ton of ways to design a public interface for creating these objects, but all of them have flaws. What approach should I use?

Here are the ideas I've thought of:

Provide mutable classes only

The user can create Schema, Table, and Column objects, and then change all properties of these objects after the fact.

Downsides:

  • Ensuring that the states of these objects remain consistent with each other could be difficult.
  • Since all of these objects are mutable, the user will have to keep in mind that they could change at any time. (And so will I, the author of the library!)

Provide immutable classes only: naive edition

The classes Schema, Table, and Column are all immutable. All properties of these objects must be specified when they are created. You can't create a Schema without specifying all of the Tables it contains, and you can't create a Table without specifying which Schema it belongs to.

Fatal downside:

  • Creating objects wouldn't actually be possible: you can't create a schema without creating its tables first, and you can't create tables without creating the schema first.

Provide immutable classes only: bottom-up construction

First, you create Column objects. Next, you create Table objects by passing in Column objects. Finally, you create a Schema object by passing in Table objects. All of these classes are immutable.

Downsides:

  • The Column object can't have a property indicating which Table the column belongs to, because the Column object is created before the Table object is, and the Column object is immutable. Likewise, the Table object can't have a property indicating which Schema it belongs to.
  • The "target table" property of a Column object will have to just be a string, not a reference to a Table object.
  • The API user is forced to create objects in a specific order. What if you want to create tables first and add columns later?

Provide two kinds of immutable classes

Provide two families of classes. First, there are the SchemaDefinition, TableDefinition, and ColumnDefinition classes, which work exactly as in the "bottom-up construction" approach.

There are also Schema, Table, and Column classes, which are also immutable. These classes have all the properties that you could hope for: a Schema has a property telling you about all of its Tables, and a Table has a property telling you which Schema it belongs to.

The Schema, Table, and Column classes have no public constructors. Instead, there's a SchemaFactory class which takes a SchemaDefinition and uses it to create a bunch of Schema, Table, and Column objects, and then gives you the Schema.

Downsides:

  • There are two classes for each type of business object, instead of just one.
  • The API user is still forced to create objects in a specific order.

Provide mutable and immutable classes

Provide two families of classes. First, there are the SchemaMutable, TableMutable, and ColumnMutable classes, which work exactly as in the "provide mutable classes only" approach.

There are also Schema, Table, and Column classes, which work just like in the "two kinds of immutable classes" approach. There's a factory class which takes a SchemaMutable and gives you the Schema.

Downsides:

  • Again, there are two classes for each type of business object.
  • And again, ensuring that the states of these objects remain consistent with each other could be difficult.
6
  • 2
    Why aren't you using an out of the box ORM? This is not a wheel that you want to reinvent.
    – RubberDuck
    Sep 20, 2016 at 23:05
  • @RubberDuck This is an experimental project. I want to see what happens if I implement some ideas that haven't been implemented before. Sep 21, 2016 at 3:52
  • What do you mean "you can't create a Table without specifying which Schema it belongs to"? Table does not need to be a aware of the schema it is in.
    – paparazzo
    Sep 22, 2016 at 12:36
  • @Paparazzi That's correct. But if a table does know what schema it belongs to, then it's impossible to create an immutable Table object without specifying the schema. Sep 22, 2016 at 14:15
  • Then I suggest you fix the question because the current definition of a table is name and columns.
    – paparazzo
    Sep 22, 2016 at 14:18

2 Answers 2

3

There is another approach to solving this problem. You could use classes that are immutable for the API consumer. You could then include an extra class, which takes care of creating objects and linking them together. This class could have internal access to mutate the objects. Because you now have full control of what scenarios you want to support for creating and mutating the objects, you can then reason about the things you need to check and update for each of these scenarios.

Constructors for these objects would be private.

The API consumer would only need to know about the Context class, but from that point on the API is self discover-able.

1

Not perfect but could go with a write once

private string prop = string.Empty;
public string Prop 
{
    get { return prop; }
    set 
    {
        if (string.IsNullOrEmpty(prop))
           prop = value;
    }
}

op edit the question

so for Schema constructor

public Schema ( List<columun> columns, string name ) 
{
     foreach (column col in columns)
     {
        if (col != null)
           throw proper exception
        col.Schema = this;
     }
     _columns = columns; 
     _name = name;
}

Your Answer

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

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