patterncsharpMinor
Resolve entity behaviour based on enumeration type
Viewed 0 times
enumerationtyperesolvebehaviourbasedentity
Problem
The core of this question is removing a Static Class smell from my Web-Api/MVC solution. It presently works but feels kind of dirty.
Present Solution
Standard solution with 6 projects:
In my domain layer I have a simple Asset.
A Guid, a name and an enum
The Taxonomy, in its cut down version, looks like this:
```
namespace Constants.Taxonomy1
{
using System.Collections.Generic;
public enum AssetType : int
{
Undefined,
WTG,
WindFarm,
MeteorologicalStation
}
public static class Taxonomy
{
private static Dictionary> childRelationships = new Dictionary>()
{
{ AssetType.Undefined, new List { } },
{ AssetType.WTG, new List { } },
{ AssetType.WindFarm, new List { AssetType.WTG, AssetType.MeteorologicalStation } },
{ AssetType.M
Present Solution
Standard solution with 6 projects:
- MVC
- Api
- Api.Client - wrapped client to make in-code calls
- Api.Model - Dto's, Request models, etc.
- Domain - Entity layer, services, client mocks
- Logging
In my domain layer I have a simple Asset.
using Constants.Taxonomy1;
public class Asset : TrackedEntity, IEntity
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Id { get; set; }
[StringLength(128, MinimumLength = 1)]
[Required]
[Index("IX_NameTypeAndParent", 2, IsUnique = true)]
public string Name { get; set; }
[Required]
[Index("IX_NameTypeAndParent", 3, IsUnique = true)]
public AssetType Type { get; set; }
[Index("IX_NameTypeAndParent", 1, IsUnique = true)]
public Guid? AssetId { get; set; }
public virtual Asset Parent { get; set; }
public virtual ICollection Children { get; set; }
}A Guid, a name and an enum
AssetType. The AssetType defines what other types of Asset can be its parent or children. This behaviour is currently defined in an external library; the Constants.Taxonomy1 import. This is separate from the solution so that it can be reused elsewhere and contains mappings between the various Taxonomies that we used.The Taxonomy, in its cut down version, looks like this:
```
namespace Constants.Taxonomy1
{
using System.Collections.Generic;
public enum AssetType : int
{
Undefined,
WTG,
WindFarm,
MeteorologicalStation
}
public static class Taxonomy
{
private static Dictionary> childRelationships = new Dictionary>()
{
{ AssetType.Undefined, new List { } },
{ AssetType.WTG, new List { } },
{ AssetType.WindFarm, new List { AssetType.WTG, AssetType.MeteorologicalStation } },
{ AssetType.M
Solution
This feels like you want more than an enum. You can create an abstract class with a protected constructor. A few private sub classes later you have both an enum and objects with behavior directly modeling the parent-child relationships you are looking for:
Now you can refer to each
By implementing custom explicit casts to int and string, you can even cast them:
By making the
Because the
public abstract class AssetType
{
public static readonly AssetType Undefined = new GenericAssetType(0, "Undefined");
public static readonly AssetType WTG = new GenericAssetType(1, "WTG", AssetType.WindFarm);
public static readonly AssetType WindFarm = new WindFarmAssetType(2, "WindFarm");
public static readonly AssetType MeteorlogicalStation = new GenericAssetType(3, "MeteorlogicalStation", AssetType.WindFarm);
public int Value { get; private set; }
public string Text { get; private set; }
public IEnumerable ChildRelationships { get; protected set; }
public AssetType ParentRelationship { get; protected set; }
protected AssetType(int value, string text) : this(value, text, AssetType.Undefined)
{
}
protected AssetType(int value, string text, AssetType parentRelationship)
{
Value = value;
Text = text;
ParentRelationship = parentRelationship;
}
public override string ToString()
{
return Text;
}
static public explicit operator int(AssetType assetType)
{
return assetType.Value;
}
static public implicit operator string(AssetType assetType)
{
return assetType.Text;
}
private class GenericAssetType : AssetType
{
protected GenericAssetType(int value, string text) : base(value, text, AssetType.Undefined)
{
ChildRelationships = new AssetType[0];
}
}
private class WindFarmAssetType : AssetType
{
public override List ChildRelationships { get; private set; }
protected WindFarmAssetType(int value, string text) : base(value, text, AssetType.Undefined)
{
ChildRelationships = new AssetType[2]
{
AssetType.WTG,
AssetType.MeteorlogicalStation
};
}
}
}Now you can refer to each
AssetType as if it were an enum:AssetType.Undefined
AssetType.WindFarm
AssetType.WindFarm.ChildRelationships.ElementAt(0) // = AssetType.WTG
AssetType.WTG.ParentRelationship // = AssetType.WindFarmBy implementing custom explicit casts to int and string, you can even cast them:
int value = (int)AssetType.WindFarm; // = 2
string text = (string)AssetType.WindFarm; // = "WindFarm"
string text2 = AssetType.WindFarm.ToString(); // = "WindFarm"By making the
ChildRelationships an IEnumerable with a protected setter, you ensure outside code is unable to add or remove child relationships.Because the
AssetType class has a protected constructor, you also limit the instances that are available to the system to just those you want.Code Snippets
public abstract class AssetType
{
public static readonly AssetType Undefined = new GenericAssetType(0, "Undefined");
public static readonly AssetType WTG = new GenericAssetType(1, "WTG", AssetType.WindFarm);
public static readonly AssetType WindFarm = new WindFarmAssetType(2, "WindFarm");
public static readonly AssetType MeteorlogicalStation = new GenericAssetType(3, "MeteorlogicalStation", AssetType.WindFarm);
public int Value { get; private set; }
public string Text { get; private set; }
public IEnumerable<AssetType> ChildRelationships { get; protected set; }
public AssetType ParentRelationship { get; protected set; }
protected AssetType(int value, string text) : this(value, text, AssetType.Undefined)
{
}
protected AssetType(int value, string text, AssetType parentRelationship)
{
Value = value;
Text = text;
ParentRelationship = parentRelationship;
}
public override string ToString()
{
return Text;
}
static public explicit operator int(AssetType assetType)
{
return assetType.Value;
}
static public implicit operator string(AssetType assetType)
{
return assetType.Text;
}
private class GenericAssetType : AssetType
{
protected GenericAssetType(int value, string text) : base(value, text, AssetType.Undefined)
{
ChildRelationships = new AssetType[0];
}
}
private class WindFarmAssetType : AssetType
{
public override List<AssetType> ChildRelationships { get; private set; }
protected WindFarmAssetType(int value, string text) : base(value, text, AssetType.Undefined)
{
ChildRelationships = new AssetType[2]
{
AssetType.WTG,
AssetType.MeteorlogicalStation
};
}
}
}AssetType.Undefined
AssetType.WindFarm
AssetType.WindFarm.ChildRelationships.ElementAt(0) // = AssetType.WTG
AssetType.WTG.ParentRelationship // = AssetType.WindFarmint value = (int)AssetType.WindFarm; // = 2
string text = (string)AssetType.WindFarm; // = "WindFarm"
string text2 = AssetType.WindFarm.ToString(); // = "WindFarm"Context
StackExchange Code Review Q#107491, answer score: 3
Revisions (0)
No revisions yet.