views:

343

answers:

2

I have an enum called Permissions. A user can be assigned permissions, or permissions can be asigned to a role and the user can be given a role.

User and Role both have a property like this:

public virtual IList<Permission> Permissions { get; set; }

I want to use an enum for Permissions so in my code I can do things like

public static bool UserHasPermission(Permission.DeleteUser)

Right now I have about 50 different permissions in my enum. It would be nice if I didn't have to populate a mirror set of data in the database. My enum looks like this:

public enum Permission
    {
        //site permissions 1-99
        [StringValue("View Users")]
        ViewUser = 1,

        [StringValue("Add User")]
        AddUser = 2,

        [StringValue("Edit User")]
        EditUser = 3,

        [StringValue("Delete User")]
        DeleteUser = 4

        ...

}

Currently, I have a table for Permissions that is the PermissionId (int) and PermissionName (varchar (50)).

Here are my tables on the roles side. The user side is exactly the same as far as permissions go:

CREATE TABLE dbo.Roles
(
    RoleId       int                 IDENTITY(2,1) NOT NULL,
    RoleName        varchar (50)  NOT NULL,

    CONSTRAINT PK_Roles PRIMARY KEY CLUSTERED (RoleId)
)

CREATE TABLE dbo.RolePermissions
(
    RolePermissionId      int                 IDENTITY(1,1) NOT NULL,
    RoleId       int                 NOT NULL,
    PermissionId       int     NOT NULL,

    CONSTRAINT PK_RolePermissions PRIMARY KEY CLUSTERED (RolePermissionId),
    CONSTRAINT FK_RolePermissions_Roles FOREIGN KEY (RoleId) REFERENCES Roles(RoleId),
    CONSTRAINT U_RolePermissions UNIQUE(RoleId, PermissionId)
)

Then, I have a permissions tble in case I need it, but I just don't get how to map either the id field in RolePermissions or the Permissions table back to the enum.

CREATE TABLE dbo.Permissions
(
    PermissionId        int                 NOT NULL,
    PermissionName      varchar (50)  NOT NULL,

    CONSTRAINT PK_Permissions PRIMARY KEY CLUSTERED (PermissionId)
)
  1. Can I map the enum to the table?
  2. Should I even map it, or should I just take out the Permissions table and in UserPermissions and RolePermissions leave PermissionId just an int and map the into to the enum?
  3. If I keep the Permissions table, is there a way for nhibernate to autopopulate the data in the Permissions table from the data in the enum?

Right now, I have no mapping to the permission enum, other than something like this:

HasManyToMany(x => x.Permissions)
                 .WithParentKeyColumn("RoleId")
                 .WithChildKeyColumn("PermissionId")
                 .WithTableName("RolePermissions")
                 .LazyLoad()
                 .Cascade.All();

Unfortunately, this causes an error:

An association from the table RolePermissions refers to an unmapped class: GotRoleplay.Core.Domain.Model.Permission

What am I doing wrong with enums? Is there a standard way or best practice for using them with fluentnhibernate when the enum is a list of values on the object and not just a single value?

+1  A: 

Hello,

Just to make sure I understand, are you trying to just map an enum property in your class to a field in a database? If that's it, then here's an example of how to do that:

public class Toy {
    public virtual int Id { get; private set; }
    public virtual string Name { get; set; }
    public virtual ToyCondition Condition { get; set; }
}

public enum ToyCondition {
    New,
    Used
}

public class ToyMap : ClassMap<Toy> {
    public ToyMap() {
        Id(x => x.Id);
        Map(x => x.Name);
        Map(x => x.Condition).CustomTypeIs(typeof(ToyCondition));
    }
}

After that you can get, set and do logic with the Condition property just like normal using the ToyCondition enum.

Toy newToy = new Toy();
newToy.Condition = ToyCondition.New;
toyRepository.Save(newToy);

In the database for the Condition field, it should be an int. I hope this made sense and answers your question. Sorry if it doesn't and I'm way off.

Edit: Sorry I just noticed you asked this question and some one gave you the same answer as me. I don't think I can help you this. :(

CalebHC
My problem is it is not a single instance of an enum. It is a list of values. I can't map it the same way. CustomTypeIs is not availavle when you have collection.
Josh
A: 

So, after a few days of talking to other developers, digging around online, and posting to the FluentNHibernate group on google, I have discovered that collections of enums are currently not exactly supported. If there is a way to do what I want to do, well, it's undocumented or a workaround.

So, I went back to the drawing board with what I was doing and really thought about it. Basically, my only choice was to use a class for my permissions. But, I want to use my enum in code, and that's when it hit me. The PermissionId field can be converted from an int to the enum and vice versa. Also, using some logic I have done before when using an enum of roles with the built in ASP.NET provider, I can write an administrative tool that will build permissions based on what is in my enum.

First, I renamed my enum.

public enum PermissionCode
    {
        //site permissions 1-99
        [StringValue("View Users")]
        ViewUser = 1,

        [StringValue("Add User")]
        AddUser = 2,

        [StringValue("Edit User")]
        EditUser = 3,

        [StringValue("Delete User")]
        DeleteUser = 4,
    }

Then, I created a new Permission class. This class mirrors the database and is simply an int Id and a string name. However, I added one property to convert that id into my PermissionCode enum.

public class Permission
    {
        public virtual int PermissionId { get; set; }
        public virtual string PermissionName { get; set; }

        public virtual PermissionCode PermissionCode 
        {
            get
            {
                return (PermissionCode)PermissionId;
            }
        }
    }

Then, in my User and Role objects, I reference Permission, and I can still do matchups like bool UserHasPermission(PermissionCode.DeleteUser);

Then in global.asax, on Application_Start I will check the webconfig for a flag that will indicate if the permissions need to be rebuilt. This way I can enable the rebuild only when I need to without having to have a permission to get in and possibly needing to deal with an error. This means I only maintain a single list of permissions in my enum, which is ideal.

Granted, the overall approach isn't as slick as I wanted, but it works. Does anyone have any other suggestions?

Josh
I can't help you with a better solution but I'm glad you found an answer.
CalebHC

related questions