I have an entity framework model Setup as the following:
-TblProject
--TblLine
---TblGroup
----TblStation
-----TblDevice
------TblSubDevice
-------TblSubSubDevice
Each of these classes implement a BaseTable which contains ID, Name and Type properties. I can cast any of my entities as a BaseTable and access these properties.
I have a situation where I keep having to write repetitive code just because each class is a different type, see this sorting function for example:
public static void OrderTreeSetup(ObservableCollection<tblProject> Table, System.ComponentModel.ListSortDirection direction)
{
ICollectionView ParentTable = CollectionViewSource.GetDefaultView(Table);
ParentTable.SortDescriptions.Clear();
ParentTable.SortDescriptions.Add(new System.ComponentModel.SortDescription("Name", direction));
foreach (tblProject Project in Table)
{
ICollectionView Projects = CollectionViewSource.GetDefaultView(Project.tblLines);
Projects.SortDescriptions.Clear();
Projects.SortDescriptions.Add(new System.ComponentModel.SortDescription("Name", direction));
foreach (tblLine Line in Project.tblLines)
{
ICollectionView Lines = CollectionViewSource.GetDefaultView(Line.tblGroups);
Lines.SortDescriptions.Clear();
Lines.SortDescriptions.Add(new System.ComponentModel.SortDescription("Name", direction));
foreach (tblGroup Group in Line.tblGroups)
{
ICollectionView Groups = CollectionViewSource.GetDefaultView(Group.tblStations);
Groups.SortDescriptions.Clear();
Groups.SortDescriptions.Add(new System.ComponentModel.SortDescription("Name", direction));
foreach (tblStation Station in Group.tblStations)
{
ICollectionView Stations = CollectionViewSource.GetDefaultView(Station.tblDevices);
Stations.SortDescriptions.Clear();
Stations.SortDescriptions.Add(new System.ComponentModel.SortDescription("Name", direction));
foreach (tblDevice Device in Station.tblDevices)
{
ICollectionView Devices = CollectionViewSource.GetDefaultView(Device.tblSubDevices);
Devices.SortDescriptions.Clear();
Devices.SortDescriptions.Add(new System.ComponentModel.SortDescription("Name", direction));
foreach (tblSubDevice SubDevice in Device.tblSubDevices)
{
ICollectionView SubDevices = CollectionViewSource.GetDefaultView(SubDevice.tblSubSubDevices);
SubDevices.SortDescriptions.Clear();
SubDevices.SortDescriptions.Add(new System.ComponentModel.SortDescription("Name", direction));
}
}
}
}
}
}
}
How can I change my entity framework model to provide means to more generic programming?
There is indeed room for optimization in your code (as you would expect when you see this amount of repetitive code). This can be even done with relatively little effort.
Let's start with the low hanging fruit: Since CollectionViewSource.GetDefaultView
accepts a parameter of type object
you can replace your initalization code...
ICollectionView ParentTable = CollectionViewSource.GetDefaultView(Table);
ParentTable.SortDescriptions.Clear();
ParentTable.SortDescriptions
.Add(new System.ComponentModel.SortDescription("Name", direction));
...with a call to this method:
private static void ConfigureSorting(object entity, ListSortDirection direction)
{
ICollectionView view = CollectionViewSource.GetDefaultView(entity);
view.SortDescriptions.Clear();
view.SortDescriptions.Add(new SortDescription("Name", direction));
}
Which would make your initialization code look cleaner and avoid the maintenance issues of the repetitive parts of your code:
public static void OrderTreeSetup(ObservableCollection<tblProject> table,
ListSortDirection direction)
{
ConfigureSorting(table, direction);
foreach (tblProject project in table)
{
ConfigureSorting(project.tblLines, direction);
foreach (tblLine line in project.tblLines)
{
//...etc...
}
}
}
This is however not the end of the line. Since you can apply interfaces to your generated (in case of database or model first) or self-written (in case of code first) POCOs, you can create an interface that would allow a recursive configuration of your objects:
interface ICascadingSetup
{
IEnumerable<object> Children { get; }
}
You can apply this interface to all POCOs which have children that allow a setup (that would be all except TblSubSubDevice
):
partial class TblProject : ICascadingSetup
{
IEnumerable<object> ICascadingSetup.Children => tblLines;
}
//...etc...
Now you can introduce a recursive method that will do the initializing:
public static void OrderTreeSetup(ObservableCollection<tblProject> table,
ListSortDirection direction)
{
ConfigureSortingRecursive(table, direction);
//...all further initializations you need to do...
}
private static void ConfigureSortingRecursive(IEnumerable<object> entities,
ListSortDirection direction)
{
ICollectionView view = CollectionViewSource.GetDefaultView(entities);
view.SortDescriptions.Clear();
view.SortDescriptions.Add(new SortDescription("Name", direction));
foreach (object entity in entities) {
if (entity is ICascadingSetup cascadingSetup) {
ConfigureSortingRecursive(cascadingSetup.Children, direction);
}
}
}
Whenever you have this tree-like representation of similar objects it is always worth to check if recursion is an option to simplify things.