A little background - these are classes I've written on top of an Entity Framework model that represents tables on Sql Server with the following names:
Dictionary.Databases
,Dictionary.Schemas
,Dictionary.Tables
,Dictionary.Columns
SysData.Databases
,SysData.Schemas
,SysData.Tables
,SysData.Columns
Compare.Databases
,Compare.Schemas
,Compare.Tables
,Compare.Columns
Each 'set' of tables (eg
*.Databases
) are roughly identical - all of them contain relevant primary key fields (eg all theSchemas
tables containDatabaseID
andSchemaID
).Tables in the
Dictionary
andSysData
schemas contain metadata fields extracted from thesys
tables (eg theColumns
tables containColumnName
,ColumnType
,IsNullable
etc).The tables in the
Dictionary
schema also contain additional common fields for custom user-entered data (Description
, etc).
I've noticed that the code I've just written for 4 classes looks very similar, and I feel like I should be able to write them better and apply some structure. They don't quite fit anything I know how to do with interfaces, however:
// for brevity's sake, only listing the very simple functions
// in the controllers here, if anyone's wondering why I'm
// bothering writing these classes instead of just going straight
// to my Entity Framework model when I need to, since these are all
// one-line methods
public class DictionaryDatabaseController {
private DictionaryContext context;
public DictionaryDatabaseController() {
this.context = new DictionaryContext();
// other initialization
}
public DictionaryDatabase FindDatabase(params object[] primaryKey) {
return this.context.Databases.Find(primaryKey);
}
public DbSet<DictionaryDatabase> GetDatabases() {
return this.context.Databases;
}
// ... etc ...
}
// This class is really similar to the one above...
public class SysDataDatabaseController {
// also has a private context, just a different type.
// SysDataContext and DictionaryContext both extend DbContext
private SysDataContext context;
public SysDataDatabaseController() {
this.context = new SysDataContext();
// other initialization
}
// Identical method, different but related return type.
// DictionaryDatabase and SysDataDatabase both implement IDatabase
public SysDataDatabase FindDatabase(params object[] primaryKey) {
return this.context.Databases.Find(primaryKey);
}
// again, basically the same, but returning a `DbSet` of
// `SysDataDatabases` rather than `DictionaryDatabase`s
public DbSet<SysDataDatabase> GetDatabases() {
return this.context.Databases;
}
// ... etc ...
}
// another class called CompareDatabaseController. Same stuff inside,
// but with CompareContext and CompareDatabase
See the commonality? I feel like I should be able to define an interface called IDatabaseController
, but the return type of each function & the type of the context
private member is different.
The two context
s are structurally related - they both extend an Entity Framework class called DbContext
.
Similarly DictionaryDatabase
, SysDataDatabase
and CompareDatabase
all implement an interface called IDatabase
You can't, however, define an interface like this and make those classes implement it:
interface IDatabaseController {
IDatabase FindDatabase(params object[] primaryKey);
DbSet<IDatabase> GetDatabases();
// ... other common functions ...
}
As the return type must be identical to that defined in the interface (eg, DictionaryDatabaseController.FindDatabase
would have to return IDatabase
rather than DictionaryDatabase
). But I feel like I should be able to do something like this, and can't figure out what. It feels like something that's probably been commonly encountered before. Maybe there's a design pattern for it.
Making me further convinced that I can and should restructure this, there are 3 other sets of classes for Schemas
, Tables
, and Columns
(DictionarySchemaController
, SysDataSchemaController
, etc etc), which again all look suspiciously similar to the above.
Any suggestions on how to improve this?
One thing I forgot to note - all the Context
classes provided by Entity Framework are again all very similar and also feel like can be put into some kind of structure:
public class DictionaryContext {
public DbSet<DictionaryDatabase> Databases { get; set; }
public DbSet<DictionarySchema> Schemas { get; set; }
public DbSet<DictionaryTable> Tables { get; set; }
public DbSet<DictionaryColumn> Columns { get; set; }
// ... other stuff ...
}
public class SysDataContext {
public DbSet<SysDataDatabase> Databases { get; set; }
public DbSet<SysDataSchema> Schemas { get; set; }
public DbSet<SysDataTable> Tables { get; set; }
public DbSet<SysDataColumn> Columns { get; set; }
// ... other stuff ...
}
public class CompareContext {
public DbSet<CompareDatabase> Databases { get; set; }
public DbSet<CompareSchema> Schemas { get; set; }
public DbSet<CompareTable> Tables { get; set; }
public DbSet<CompareColumn> Columns { get; set; }
// ... other stuff ...
}