0

I have a program where the domain is focused around programs.

As part of the domain, I have a 'ProgramType', which is an enum formed mostly via a string from the database but also via a bit of logic. I currently have it split between three files (ProgramType, DatabaseTypeAttribute, and ProgramTypeExtensions).

However, is this really the best way to do this? It seems strange to be splitting this code up like this. I'd like to aggregate all of this into a single class, but I'm not sure how to approach doing so (as it should still behave as an enum) - or even if it's a good idea.

What is the best approach?

public enum ProgramType
{
    [DatabaseType("DESKTOP")]
    Desktop = 1,
    [DatabaseType("WEB")]
    Web = 2,
    Unknown = 3,
    BrokenDesktop = 4
}

internal class DatabaseTypeAttribute : Attribute
{
    internal string DatabaseType { get; }

    internal DatabaseTypeAttribute(string type)
    {
        DatabaseType = type;
    }
}

public static class ProgramTypeExtensions
{
    public static string GetDatabaseType(this ProgramType programType)
    {
        var type = programType.GetType();
        var name = Enum.GetName(type, programType);
        return type.GetField(name).GetCustomAttributes(false)
            .OfType<DatabaseTypeAttribute>().SingleOrDefault()?.DatabaseType;
    }

    internal static ProgramType GetProgramType(DatabaseTypeAttribute databaseType,
        string url)
    {
        var dbType = GetValueFromDatabaseType(databaseType.DatabaseType);

        if (dbType == ProgramType.Desktop && !File.Exists(url))
        {
            return ProgramType.BrokenDesktop;
        }

        return dbType;
    }

    private static ProgramType GetValueFromDatabaseType(string databaseType)
    {
        var pgmType = Enum.GetValues(typeof(ProgramType)).Cast<ProgramType?>()
            .FirstOrDefault(pgmtype =>
                pgmtype != null && pgmtype.Value.GetDatabaseType() == databaseType);

        return pgmType ?? ProgramType.Unknown;
    }
}

As requested, here is how the extension methods are used:

//In Repo:
var parameters = new List<DBParametre>
{
    new DBParametre(ProgramType.Web.GetDatabaseType()),
};

//In domain object:
public Program(string name, string type, string url)
{
    Name = name;
    Url = url;
    Type = ProgramTypeExtensions.GetProgramType(type, url);
}
  • How are the extension methods being used? – Greg Burghardt Jun 28 '18 at 21:24
1

A few things:

You can nest enums (it's more of a strongly typed hack), though the code looks a bit terrible: https://stackoverflow.com/questions/980766/how-do-i-declare-a-nested-enum

You can simplify your enum logic by using [enum].ToString() to get the name instead of reflection.

The whole point of an enum is to have a lookup value in the db, so 0-5 in the db don't mean anything (to the c# code), but in the enum they are strongly typed making the code readable. Storing the enum name as a string in the db defeats the purpose imho.

Lastly, if you didn't want to do the above (I wouldn't) you can make a class called "Program" to encapsulate the program type and database type and then control their relationship through code.

  • It is not true that you can nest enums... unless you mean in another class but here it sounds as if you wanted to nest it insinde other enums and this is not possible. I'd give +1 if you clarify this because I like another thing that you pointed out Storing the enum name as a string in the db defeats the purpose imho. – t3chb0t Jun 29 '18 at 7:38
  • Thank you for your Answer. I had a couple questions/clarifications. Clarification 1 ("The whole point of an enum is to have a lookup value in the db"): Wouldn't its purpose also be to have a compile-time set of enumerable values? I considered another data structure, such as Dictionary, but then I wouldn't be able to get access to the strings "WEB" or "DESKTOP" without saving them in separate constants, which is kind of nasty imo. – Sarov Jun 29 '18 at 13:23
  • Clarification 2 ("make a class called "Program" to encapsulate the program type and database type"): I already have one - you suggest to move the enum logic into it, even though I access ProgramType outside of the domain? – Sarov Jun 29 '18 at 13:23
  • Oh, also. The reason I use an attribute instead of just ToString() is because I want to make explicit that some values (Web, Desktop) are stored in the database, while others (Unknown, BrokenDesktop) are not. Even if the database changes to store integer instead (which it might), I'd probably still store a boolean in an attribute. – Sarov Jun 29 '18 at 13:29
  • @t3chb0t you are correct in that it's more of a facade (not the pattern), but it does accomplish the objective of making types dependent on other types more than 1 level deep, now is it a good idea? who knows, but it once fit a use case for me perfectly, so I didn't have to prefix my enums. – RandomUs1r Jun 29 '18 at 17:09

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.