I'm coding an API to import data into our software.
As you can see below my models hierarchy is broken down into different types of models (A & B).
Each model :
embed another model : "ImportModel" embed "Production" which embed "Parts"
Ex : ImportModelA => ProductionModelA => List of PartModelA
has specific properties related to itself and some common properties
public class ImportModelA : ImportModelBase
{
public string ImportModelAProp { get; set; }
}
public class ImportModelB : ImportModelBase
{
public string ImportModelBProp { get; set; }
}
public abstract class ImportModelBase
{
public ProductionModelBase Production { get; set; }
}
public class ProductionModelA : ProductionModelBase
{
public int ProductionModelAProp { get; set; }
}
public class ProductionModelB : ProductionModelBase
{
public int ProductionModelBProp { get; set; }
}
public abstract class ProductionModelBase
{
public List<PartModelBase> Parts { get; set; }
}
public class PartModelA : PartModelBase
{
public int PartModelAProp { get; set; }
}
public class PartModelB : PartModelBase
{
public int PartModelBProp { get; set; }
}
public abstract class PartModelBase
{
public short CommonProp { get; set; }
}
Here's my problem :
In my modelA specific service method, I have no choice but to downcast the "Production" and "parts" models to get access to their specific properties :
public class ImportModelAManagement
{
public void DoModelASpecificStuff(ImportModelA importModel)
{
// First downcast
var production = (ProductionModelA)importModel.Production;
// Uses productionModelA specific property
Console.WriteLine(production.ProductionModelAProp);
// Second downcast
var parts = production.Parts.ConvertAll(p => (PartModelA)p)
.ToList();
// Uses partModelA specific property
var firstPartModelAProp = parts[0].PartModelAProp;
Console.WriteLine(firstPartModelAProp);
}
}
This method is an "external" operation, so it can't be added to the model classes directly.
As an alternative, I've already tried to use generics instead as shown below :
public class ImportModelA : ImportModelBase<ProductionModelA, PartModelA>
{
public string ImportModelAProp { get; set; }
}
public class ImportModelB : ImportModelBase<ProductionModelB, PartModelB>
{
public string ImportModelBProp { get; set; }
}
public abstract class ImportModelBase<TProductionModel, TPartModel>
where TProductionModel : ProductionModelBase<TPartModel>
where TPartModel : PartModelBase
{
public TProductionModel Production { get; set; }
}
public class ProductionModelA : ProductionModelBase<PartModelA>
{
public int ProductionModelAProp { get; set; }
}
public class ProductionModelB : ProductionModelBase<PartModelB>
{
public int ProductionModelBProp { get; set; }
}
public abstract class ProductionModelBase<TPartModel>
where TPartModel : PartModelBase
{
public List<TPartModel> Parts { get; set; }
}
public class PartModelA : PartModelBase
{
public int PartModelAProp { get; set; }
}
public class PartModelB : PartModelBase
{
public int PartModelBProp { get; set; }
}
public abstract class PartModelBase
{
public short CommonProp { get; set; }
}
Downcasting problem is now solved.
Unfortunately, this design will quickly get unmaintainable as the code base grows.
I know that my design isn't correct so :
- What can I do to avoid downcasting or using "convertAll" on these nested models ?
- Is the "visitor" pattern an appropriate way to solve this problem ?
Edit :
Not sure but here could be a way to solve this problem by simplifying the initial hierarchy :
public class ImportModelA : ImportModelBase
{
public string ImportModelAProp { get; set; }
public ProductionModelA Production { get; set; }
}
public class ImportModelB : ImportModelBase
{
public string ImportModelBProp { get; set; }
public ProductionModelB Production { get; set; }
}
public abstract class ImportModelBase
{
public string ImportModelBaseProp { get; set; }
}
public class ProductionModelA : ProductionModelBase
{
public int ProductionModelAProp { get; set; }
public PartModelA Parts { get; set; }
}
public class ProductionModelB : ProductionModelBase
{
public int ProductionModelBProp { get; set; }
public PartModelB Parts { get; set; }
}
public abstract class ProductionModelBase
{
public string ProductionModelBaseProp { get; set; }
}
public class PartModelA : PartModelBase
{
public int PartModelAProp { get; set; }
}
public class PartModelB : PartModelBase
{
public int PartModelBProp { get; set; }
}
public abstract class PartModelBase
{
public short CommonProp { get; set; }
}
Thanks