I'm writing a data access library in C#, the structure of my data model classes are as follows (within the DataAccess assembly):
public abstract class DataModel {...} (protected constructor)
public abstract class DataClass {...} (protected constructor)
public abstract class DataField {...} (protected constructor)
public class IntegerField : DataField
{
public IntegerField(DataClass cls, string name, bool isNullable,
int? defaultValue, int? minValue, int? maxValue) {...}
}
public class StringField : DataField
{
public StringField(DataClass cls, string name, bool isNullable,
string defaultValue, int maxLength) {...}
}
public class BooleanField : DataField
{
public BooleanField(DataClass cls, string name, bool isNullable,
bool? defaultValue) {...}
}
...
As you can see, each field has a different constructor.
To use this you automatically generate classes from an XML file, for example, a "Store" data model, which contains a "Customer" data class and an "Employee" data class will generate this code:
public class StoreDataModel : DataModel
{
public CustomerDataClass Customer { get; private set;}
public EmployeeDataClass Employee { get; private set;}
public StoreDataModel()
{
Customer = new CustomerDataClass(this);
AddClass(customer)
Employee = new EmployeeDataClass(this);
AddClass(employee)
}
}
public class CustomerDataClass : DataClass
{
public StringField FirstNameField { get; private set; }
public StringField LastNameField { get; private set; }
public DateField BirthDateField { get; private set; }
public CustomerDataClass(StoreDataModel model) : base(model)
{
FirstNameField = new StringField(this, "FirstName", true, null, 50);
LastNameField = new StringField(this, "LastName", true, null, 50);
...
}
}
public class EmployeeDataClass : DataClass {...}
My problem with this code is that since the generated code is in a different assembly than the base data model classes, the StringField/IntegerField/... classes must have public constructors and that means anyone outside the data access assembly can create instances of them and I would like to prevent that.
Since the field classes have different constructors I can't think of a way to use reflection or generics to create the fields within the base data access assembly.
The only way I could think of was add a "Initialize" method to each field class that would include the properties of that specific fields and in the custom data class constructor do this:
public CustomerDataClass(StoreDataModel model) : base(model)
{
FirstNameField = AddField<StringField>(this, "FirstName", true, null)
.Initialize(50);
LastNameField = AddField<StringField>(this, "LastName", true, null)
.Initialize(50);
...
}
This will allow me to change the field constructors to internal and won't allow anyone to create an instance of a field. The problem with this solution is that it would allow anyone to run the "Initialize" method and alter the field (this can be solved by adding a private boolean member (m_IsInitialized) that will be set to true when "Initialize" is first run, and each time "Initialize" is called, if m_IsInitialized is true, it will throw an exception, but this solution is a little ugly).
Does anyone have a better solution?