Search code examples
asp.netroleproviderhierarchical

Hierarchical SQL roles based on default ASP.NET RoleProvider


I'm trying to implement the following adjustments to the default ASP.NET RoleProvider so that it supports hierarchical role definitions. However i cannot create the following function, it keeps Executing the function...

Ref: http://mark.tremaine.net/howto/hierarchical-sql-role-provider/

What is wrong with this function?

    -- ================================================
    -- Template generated from Template Explorer using:
    -- Create Multi-Statement Function (New Menu).SQL
    --
    -- Use the Specify Values for Template Parameters 
    -- command (Ctrl-Shift-M) to fill in the parameter 
    -- values below.
    --
    -- This block of comments will not be included in
    -- the definition of the function.
    -- ================================================
    SET ANSI_NULLS ON
    GO
    SET QUOTED_IDENTIFIER ON
    GO
    -- =============================================
    -- Author:      <Author,,Name>
    -- Create date: <Create Date,,>
    -- Description: <Description,,>
    -- =============================================


     CREATE FUNCTION [dbo].[aspnet_Roles_Ancestor_TVF] (
     @RoleId uniqueidentifier
     )
     RETURNS
     @aspnet_Roles TABLE (
     ApplicationId uniqueidentifier
     , RoleId uniqueidentifier
     , RoleName nvarchar(256)
     , LoweredRoleName nvarchar(256)
     , Description nvarchar(256)
     , ParentRoleId uniqueidentifier
     )
     AS
     BEGIN
     ; WITH aspnet_Roles_CTE (
     ApplicationId
     , RoleId
     , RoleName
     , LoweredRoleName
     , Description
     , ParentRoleId
     , HierarchyLevel
     ) AS (
     SELECT
     ApplicationId
     , RoleId
     , RoleName
     , LoweredRoleName
     , Description
     , ParentRoleId
     , 1 AS HierarchyLevel
     FROM aspnet_Roles
     WHERE RoleId = @RoleId

     UNION ALL

     SELECT
     aspnet_Roles.ApplicationId
     , aspnet_Roles.RoleId
     , aspnet_Roles.RoleName
     , aspnet_Roles.LoweredRoleName
     , aspnet_Roles.Description
     , aspnet_Roles.ParentRoleId
     , aspnet_Roles_CTE.HierarchyLevel + 1 AS HierarchyLevel
     FROM aspnet_Roles
     INNER JOIN aspnet_Roles_CTE
     ON aspnet_Roles.RoleId = aspnet_Roles_CTE.ParentRoleId
     )

     INSERT INTO @aspnet_Roles (
     ApplicationId
     , RoleId
     , RoleName
     , LoweredRoleName
     , Description
     , ParentRoleId
     )
     SELECT
     ApplicationId
     , RoleId
     , RoleName
     , LoweredRoleName
     , Description
     , ParentRoleId
     FROM aspnet_Roles_CTE
     ORDER BY HierarchyLevel

     RETURN
     END
    GO

Solution

  • I believe the issue is in the structure of your CTE. The first half of the Union query should represent the parent and the second should return the children. I.e, you are walking down the hierarchy not up it. Thus, I would change the On clause in the second half of the Union query to:

    aspnet_Roles_CTE.RoleId = aspnet_Roles.ParentRoleId.

    Edit

    Some sample data would help. Here's a small test I whipped up:

    Declare @RoleId int;
    Set @RoleId = 1;
    
    With aspnet_Roles As
        (
        Select 1 As ApplicationId, 1 As RoleId, 'Parent Role A' As RoleName, Null As ParentRoleId
        Union All Select 1, 2, 'Parent Role B', Null
        Union All Select 1, 3, 'Parent Role C', Null
        Union All Select 1, 4, 'Child Role A-A', 1
        Union All Select 1, 5, 'Child Role A-B', 1
        Union All Select 1, 6, 'Child Role A-C', 1
        Union All Select 1, 7, 'Child Role A-A-A', 4
        Union All Select 1, 8, 'Child Role A-A-B', 4
        Union All Select 1, 9, 'Child Role A-A-C', 4
        )
        , aspnet_Roles_CTE ( ApplicationId, RoleId, RoleName, ParentRoleId, HierarchyLevel ) As
        (
        Select ApplicationId, RoleId, RoleName, ParentRoleId, 1 AS HierarchyLevel
        From aspnet_Roles
        Where RoleId = @RoleId
        Union All
        Select AR.ApplicationId, AR.RoleId, AR.RoleName, AR.ParentRoleId, HierarchyLevel + 1
        From aspnet_Roles As AR
            Join aspnet_Roles_CTE As CTE
                On CTE.ApplicationId = AR.ApplicationId
                    And CTE.RoleId = AR.ParentRoleId
         )
    Select ApplicationId, RoleId, RoleName, ParentRoleId, HierarchyLevel
    From aspnet_Roles_CTE
    

    Results:

    ApplicationId | RoleId | RoleName         | ParentRoleId | HierarchyLevel
    1             | 1      | Parent Role A    | NULL         | 1
    1             | 4      | Child Role A-A   | 1            | 2
    1             | 5      | Child Role A-B   | 1            | 2
    1             | 6      | Child Role A-C   | 1            | 2
    1             | 7      | Child Role A-A-A | 4            | 3
    1             | 8      | Child Role A-A-B | 4            | 3
    1             | 9      | Child Role A-A-C | 4            | 3