Creating an Enum of GUIDs
Asked Answered
H

4

5

I used this accepted answer to create an Enum of Guids.

public enum AccessRoles
{
    [EnumGuid("2ED3164-BB48-499B-86C4-A2B1114BF1")]
    SysAdmin =1,
    [EnumGuid("A5690E7-1111-4AFB-B44D-1DF3AD66D435")]
    Admin = 2,
    [EnumGuid("30558C7-66D9-4189-9BD9-2B87D11190")]
    OrgAdmin = 3,
}

class EnumGuid : Attribute
{
    public Guid Guid;

    public EnumGuid(string guid)
    {
        Guid = new Guid(guid);
    }
}

I try check if a Guid is part of an enum, it throws an exception System.InvalidOperationException even though userId = 2ED3164-BB48-499B-86C4-A2B1114BF1 is a valid guid.

if(Enum.IsDefined(typeof(AccessRoles), userId))
{

}    

I tried converting it to string and checking, but that time it does not throw an error but does not go inside the if loop.

if(Enum.IsDefined(typeof(AccessRoles), userId.ToString().ToUpper()))
{

} 

So how do I fix it? Or is there a better way? I want to avoid the multiple if statements or a case statement and so what to use it as enums so they are reusable.

Hermann answered 31/10, 2017 at 20:45 Comment(14)
Adding an attribute to an enum doesn't change the values that are valid for that enum. A Guid value is not valid for the IsDefined() method. Why did you think that would work? What are you actually trying to do? Please fix your question so it includes a good minimal reproducible example that shows clearly the context and a good explanation of what specifically you can't figure out.Courage
2ED3164-BB48-499B-86C4-A2B1114BF1 not a valid GUID, you are missing 3 chars, 1 at the first 8 and 2 at the last portion.Bataan
I want to check if a particular userId is part of the 3 Guids present in AccessRoles. Hence I used the example in the link to design an enum with Guids and find if it is present.Hermann
@12seconds That is a random Guid I gave as an example.Hermann
Enums can only be created over integral types - a Guid isn't one of those. What you did was tagging integral enum members with guids but that doesn't make the guids part of the enum. learn.microsoft.com/en-us/dotnet/csharp/language-reference/…Luddite
@Luddite Is there a better solution?Hermann
@Hermann You can create a map of Guid -> enum members using reflection and then use the map to look up enum values.Luddite
Why use an enum for this in the first place? I would create a simple mapping class instead. If you add a role, having an enun means you must change and recompile the code. A mapping class can take the mapping from the database or configuration file. Also, it gives you far more flexibility than an enum does.Neuron
@ZoharPeled Can you provide me with a link of an example of the same?Hermann
It's getting late here so not right now, I'll try to post an answer tomorrow.Neuron
Personally I would prefer using bitmask via Enum with Flag attribute. You then do bit operations to determine if user has permission to do functions. See blog.typps.com/2007/10/one-bit-masks-for-access-control.html for more info, same concept for roles.Bataan
@ZoharPeled - Any chance of providing the mapping example - I can't find anything on Bing - thx...Sepoy
@Sepoy I've already answered this question way back when it was posted. If my answer doesn't help you get the idea, feel free to post your own question. You can ping me to look at it if you want to.Neuron
sorry didn't see your answer below...Sepoy
N
5

I would replace your enum with an immutable struct, and add a static class to hold all possible roles in the application:

public struct AccessRole
{
    public AccessRole(Guid guid, int number, string name) : this()
    {
        Uuid = guid;
        Number = number;
        Name = name;
    }

    public Guid Uuid {get;}
    public int Number {get;}
    public string Name {get;}
}

Then you can add a static class for AccessRoles:

public static class AccessRoles
{
    private static List<AccessRole> _roles;
    static AccessRoles()
    {
        _roles = new List<AccessRole>();
        // Here I populate it hard coded for the sample,
        // but it should be populated from your database or config file
        _roles.Add(new AccessRole(new Guid("2ED3164-BB48-499B-86C4-A2B1114BF1"), 1, "SysAdmin"));
        _roles.Add(new AccessRole(new Guid("A5690E7-1111-4AFB-B44D-1DF3AD66D435"), 2, "Admin"));
        _roles.Add(new AccessRole(new Guid("30558C7-66D9-4189-9BD9-2B87D11190"), 3, "OrgAdmin"));
    }

    public static AccessRole GetRole(Guid uuid)
    {
        return _roles.Find(r => r.Uuid == uuid);
    }

    public static AccessRole GetRole(int number)
    {
        return _roles.Find(r => r.Number == number);
    }

    public static AccessRole GetRole(string name)
    {
        return _roles.Find(r => r.Name == name);
    }
}

Now all you have to do is change the way the _roles list is populated in the static constructor to either a database of a configuration file, and you're good to go. Note that the AccessRoles provides static methods to get a search for a role by either property. It can be replaced with a single method that will get a predicate, but I think that this way it's more readable.

Neuron answered 1/11, 2017 at 5:47 Comment(1)
Thank you - This is really nice and covers all the way you would like to get from such a structure. This is very useful for static data. +1Luanneluanni
U
2

I would suggest a complete different approach, when working with fixed user roles.

Using an Enumeration you can achieve same and much more:

public abstract class UserRoleType : Enumeration<UserRoleType>
{
    protected UserRoleType(int value, string displayName) 
        : base(value, displayName)
    {}

    public static readonly UserRoleType Unknown = new UnknownRoleType();
    public static readonly UserRoleType Administrator = new AdministratorRoleType();
    public static readonly UserRoleType System = new SystemRoleType();
    public static readonly UserRoleType Moderator = new ModeratorRoleType();

    public virtual bool CanCreateUser => false;
    public virtual bool CanBlockUser => false;
    public virtual bool CanResetUserPassword => false;
}

public sealed class UnknownRoleType : UserRoleType
{
    public UnknownRoleType()
        : base(0, "Unknown")
    { }
}

public sealed class AdministratorRoleType : UserRoleType
{
    public AdministratorRoleType()
        : base(10, "Administrator")
    {}

    public override bool CanCreateUser => true;
    public override bool CanBlockUser => true;
    public override bool CanResetUserPassword => true;
}

public sealed class SystemRoleType : UserRoleType
{
    public SystemRoleType()
        : base(20, "System")
    { }
    public override bool CanBlockUser => true;
    public override bool CanResetUserPassword => true;
}

public sealed class ModeratorRoleType : UserRoleType
{
    public ModeratorRoleType()
        : base(40, "Moderator")
    { }
    public override bool CanBlockUser => true;
}

By setting abstract/virtual properties on the abstract UserRoleType, you system only have operate on the abstract class.

When your user context is being initialized (on login), you simply find the user role by

        var roleTypeValueFromDatabase = 10;
        var roleType = UserRoleType.FromValueOrDefault(roleTypeValueFromDatabase, UserRoleType.Unknown);

        if (roleType.CanCreateUser)
        {
            // create user..
        }

        // Find roles with specific rights
        var rolesThatCanResetPassword = UserRoleType.GetAll().Where(urt => urt.CanResetUserPassword);

About the Enumeration class, there are several implementation of them on github/nuget.

Mine is for .Net core v2 - https://github.com/microknights/Collections with Nuget: Install-Package MicroKnights.Collections

Headspring - https://github.com/HeadspringLabs/Enumeration source files only.

Utile answered 31/10, 2017 at 21:24 Comment(2)
Rather than having a million different types, you can just have a few fields and a single type that returns the appropriate value based on those fields.Canaan
Correct you can do that, but i personally prefer the specific implementation.Utile
P
2
public enum AccessRoles
{
    SysAdmin = 1,
    Admin = 2,
    OrgAdmin = 3
}


public class Attributes
{
    public static Dictionary<int, Guid> Attribute = new Dictionary<int, Guid>()
    {
        {(int)AccessRoles.SysAdmin, Guid.Parse("6D18698C-04EC-4E50-84DB-BE513D5875AC")},
        {(int)AccessRoles.Admin, Guid.Parse("32E86718-7034-4640-9076-A60B9B6CA51A")},
        {(int)AccessRoles.OrgAdmin, Guid.Parse("2CA39E37-8AEA-463F-AE14-E9D92AC5FB5E")}
    };
}

Console.WriteLine(Attributes.Attribute[(int)AccessRoles.SysAdmin]);
Console.WriteLine(Attributes.Attribute[(int)AccessRoles.Admin]);
Console.WriteLine(Attributes.Attribute[(int)AccessRoles.OrgAdmin]);
Prelect answered 10/5, 2018 at 13:16 Comment(1)
Please add some explanations to what you did.Teneshatenesmus
M
1

Maybe passing the Guid values with System.ComponentModel.AmbientValueAttribute like so :

using System.ComponentModel;

public enum AccessRoles
{
    [AmbientValue(typeof(Guid), "749e73c0-ba25-4f69-9f81-ec21d9942e52")]
    SysAdmin = 1,
    [AmbientValue(typeof(Guid), "39cc7e3d-db5f-4619-a577-e24cb89de5a7")]
    Admin = 2,
    [AmbientValue(typeof(Guid), "93902f8d-46d3-4b43-b684-b0ee66bbf7de")]
    OrgAdmin = 3,
}

With an extension to obtain the AmbientValue:

using System.Reflection;

public static class EnumExtensions
{
   public static object GetAmbientValue(this Enum enumVal)
   {
        Type type = enumVal.GetType();
        MemberInfo[] memInfo = type.GetMember(enumVal.ToString());
        object[] attributes = memInfo[0].GetCustomAttributes(typeof(AmbientValueAttribute), false);

        if (attributes == null || attributes.Length == 0)
           return default;

        return ((AmbientValueAttribute)attributes[0]).Value;
    }
}

And finally obtaining the Guid value like so :

var valGuid = (Guid)AccessRoles.SysAdmin.GetAmbientValue();
Murphey answered 10/11, 2022 at 14:20 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.