Tutorials

Labels
AJAX(112) App Studio(7) Apple(1) Application Builder(245) Application Factory(207) ASP.NET(95) ASP.NET 3.5(45) ASP.NET Code Generator(72) ASP.NET Membership(28) Azure(18) Barcode(2) Barcodes(3) BLOB(18) Business Rules(1) Business Rules/Logic(140) BYOD(13) Caching(2) Calendar(5) Charts(29) Cloud(14) Cloud On Time(2) Cloud On Time for Windows 7(2) Code Generator(54) Collaboration(11) command line(1) Conflict Detection(1) Content Management System(12) COT Tools for Excel(26) CRUD(1) Custom Actions(1) Data Aquarium Framework(122) Data Sheet(9) Data Sources(22) Database Lookups(50) Deployment(22) Designer(177) Device(1) DotNetNuke(12) EASE(20) Email(6) Features(101) Firebird(1) Form Builder(14) Globalization and Localization(6) How To(1) Hypermedia(2) Inline Editing(1) Installation(5) JavaScript(20) Kiosk(1) Low Code(3) Mac(1) Many-To-Many(4) Maps(6) Master/Detail(36) Microservices(4) Mobile(63) Mode Builder(3) Model Builder(3) MySQL(10) Native Apps(5) News(18) OAuth(9) OAuth Scopes(1) OAuth2(13) Offline(20) Offline Apps(4) Offline Sync(5) Oracle(11) PKCE(2) Postgre SQL(1) PostgreSQL(2) PWA(2) QR codes(2) Rapid Application Development(5) Reading Pane(2) Release Notes(183) Reports(48) REST(29) RESTful(29) RESTful Workshop(15) RFID tags(1) SaaS(7) Security(81) SharePoint(12) SPA(6) SQL Anywhere(3) SQL Server(26) SSO(1) Stored Procedure(4) Teamwork(15) Tips and Tricks(87) Tools for Excel(2) Touch UI(93) Transactions(5) Tutorials(183) Universal Windows Platform(3) User Interface(338) Video Tutorial(37) Web 2.0(100) Web App Generator(101) Web Application Generator(607) Web Form Builder(40) Web.Config(9) Workflow(28)
Archive
Blog
Tutorials
Saturday, December 1, 2012PrintSubscribe
Minimal Membership Provider for Oracle

Requirements

A minimal membership provider requires a dedicated table to keep track of user names and passwords.

'Users' table diagram.

This is a sample “Users” table with “identity” primary key. It is necessary to create a sequence and trigger to update the identity key every time a new user is created.

SQL:

create sequence users_seq;

create table 
users
(
    user_id 
int not null primary key,
    user_name varchar2(128) not null,
    password varchar2(128) not null
);

create or replace trigger 
users_trigger
    before 
insert
    on 
users
   
for each 
row
declare

    u_id users.user_id%type;
begin
    select users_id_seq.nextval into u_id from dual;
    :new.user_id := u_id;
end users_trigger;
/

Here is how the table may look if a “unique identifier” primary key is implemented. Oracle will automatically assign a unique identifier when a new user is created.

SQL:

create table users
(
    user_id 
raw(16) default sys_guid() not null primary key,
    user_name varchar2(128) not null,
    password varchar2(128) not null
);

User roles are hardcoded in the minimal Role Provider implementation.

Configuration

Create a table in your database using one of the scripts specified above.

Select the project name on the start page of the application generator and choose Settings.

Proceed to Authentication and Membership.

Select “Enable custom membership and role providers” option and enter the following configuration settings.

table Users=Users
column [int|uiid] UserID = user_id
column [text] UserName = user_name
column [text] Password = password

role Administrators = admin
role Users = admin, user
role Everybody = *

The configuration maps logical table Users required for membership provider implementation to the physical database table Users. It also defines three user roles – Administrators, Users, and Everybody.

Generate the project to see the membership provider in action.

Tuesday, November 27, 2012PrintSubscribe
Advanced User Manager for Custom Membership and Role Provider: Implementing the Business Rules

Instead of allowing the application framework to handle the CRUD operations, let’s prevent the default behavior and implement several business rules. These business rules with use the ASP.NET Membership API to insert, update, and delete users.

Insert

In the Project Explorer, right-click on Users / Business Rules node, and press New Business Rule.

Creating a new business rule for Users data controller.

Assign the following properties:

Property Value
Type C# / Visual Basic
Command Name Insert
Phase Before

Press OK to save the business rule. On the toolbar, press Browse to regenerate the web app and create the business rule file. When finished, right-click on Users / Business Rules / Insert (Code / Before) – r101 node and press Edit Rule in Visual Studio.

'Edit Rule in Visual Studio' context menu option for a code business rule.

Replace the content of the file with the following:

C#:

using System;
using System.Web;
using System.Web.Security;
using MyCompany.Data;
using MyCompany.Security;

namespace MyCompany.Rules
{
    public partial class UsersBusinessRules : MyCompany.Data.BusinessRules
    {

        /// <summary>
        /// This method will execute in any view before an action
        /// with a command name that matches "Insert".
        /// </summary>
        [Rule("r101")]
        public void r101Implementation(
                    FieldValue userID,
                    string userName,
                    string password,
                    string email,
                    string comment,
                    string passwordQuestion,
                    string passwordAnswer,
                    bool isApproved,
                    DateTime? lastActivityDate,
                    DateTime? lastLoginDate,
                    DateTime? lastPasswordChangedDate,
                    DateTime? creationDate,
                    bool? isLockedOut,
                    DateTime? lastLockedOutDate,
                    int? failedPasswordAttemptCount,
                    DateTime? failedPasswordAttemptWindowStart,
                    int? failedPasswordAnswerAttemptCount,
                    DateTime? failedPasswordAnswerAttemptWindowStart,
                    string roles,
                    string confirmPassword,
                    int? roleID)
        {
            // prevent execution of CRUD operations by the application framework
            PreventDefault();
            // ensure that "Password" and "ConfirmPassword" match
            if (password != confirmPassword)
                throw new Exception(Localize("PasswordAndConfirmationDoNotMatch",
                    "Password and confirmation do not match"));
            // create a user with the Membership API
            MembershipCreateStatus status;
            MembershipUser newUser = Membership.CreateUser(userName, password, email, 
                passwordQuestion, passwordAnswer, isApproved, out status);
            // analyze and report any errors
            if (status != MembershipCreateStatus.Success)
            {
                string error = null;
                switch (status)
                {
                    case MembershipCreateStatus.DuplicateEmail:
                        error = "Duplicate email address.";
                        break;
                    case MembershipCreateStatus.DuplicateProviderUserKey:
                        error = "Duplicate provider key";
                        break;
                    case MembershipCreateStatus.DuplicateUserName:
                        error = "Duplicate user name.";
                        break;
                    case MembershipCreateStatus.InvalidAnswer:
                        error = "Invalid password recovery answer.";
                        break;
                    case MembershipCreateStatus.InvalidEmail:
                        error = "Invalid email address.";
                        break;
                    case MembershipCreateStatus.InvalidPassword:
                        error = string.Format("Invalid password. Requires at least {0} " +
                            "characters and {1} non-alphanumeric characters.",
                            Membership.Provider.MinRequiredPasswordLength,
                            Membership.Provider.MinRequiredNonAlphanumericCharacters);
                        break;
                    case MembershipCreateStatus.InvalidProviderUserKey:
                        error = "Invalid provider user key.";
                        break;
                    case MembershipCreateStatus.InvalidQuestion:
                        error = "Invalid password recovery question.";
                        break;
                    case MembershipCreateStatus.InvalidUserName:
                        error = "Invalid user name.";
                        break;
                    case MembershipCreateStatus.ProviderError:
                        error = "Provider error.";
                        break;
                    case MembershipCreateStatus.UserRejected:
                        error = "User has been rejected.";
                        break;
                }
                throw new Exception(error);
            }
            // assign "Comment" to the new user
            if (!(String.IsNullOrEmpty(comment)))
            {
                newUser.Comment = comment;
                Membership.UpdateUser(newUser);
            }
            // assign "Roles" to the new user
            if (!(String.IsNullOrEmpty(roles)))
            {
                string[] newRoles = Convert.ToString(roles).Split(',');
                foreach (string role in newRoles)
                    if (!(String.IsNullOrEmpty(role)))
                        System.Web.Security.Roles.AddUserToRole(
                            userName, role);
            }
        }
    }
}

Visual Basic:

Imports MyCompany.Data
Imports System
Imports System.Collections.Generic
Imports System.Data
Imports System.Linq
Imports System.Text.RegularExpressions
Imports System.Web
Imports System.Web.Security

Namespace MyCompany.Rules

    Partial Public Class UsersBusinessRules
        Inherits MyCompany.Data.BusinessRules

        ''' <summary>
        ''' This method will execute in any view before an action
        ''' with a command name that matches "Insert".
        ''' </summary>
        <Rule("r101")> _
        Public Sub r101Implementation( _
                    ByVal userID As FieldValue, _
                    ByVal userName As String, _
                    ByVal password As String, _
                    ByVal email As String, _
                    ByVal comment As String, _
                    ByVal passwordQuestion As String, _
                    ByVal passwordAnswer As String, _
                    ByVal isApproved As Nullable(Of Boolean),
                    ByVal isLockedOut As Nullable(Of Boolean), _
                    ByVal roles As String, _
                    ByVal confirmPassword As String)
            ' prevent execution of CRUD operations by the application framework
            PreventDefault()
            ' ensure that "Password" and "ConfirmPassword" match
            If (password <> confirmPassword) Then
                Throw New Exception(Localize("PasswordAndConfirmationDoNotMatch",
                                             "Password and confirmation do not match"))
            End If
            Dim status As MembershipCreateStatus
            ' create a user with the Membership API
            Dim newUser As MembershipUser = Membership.CreateUser(userName,
                                  password,
                                  email,
                                  passwordQuestion,
                                  passwordAnswer,
                                  isApproved,
                                  status)
            ' analyze and report any errors
            If (status <> MembershipCreateStatus.Success) Then
                Dim [error] As String = ""
                Select Case status
                    Case MembershipCreateStatus.DuplicateEmail
                        [error] = "Duplicate email address."
                    Case MembershipCreateStatus.DuplicateProviderUserKey
                        [error] = "Duplicate provider key"
                    Case MembershipCreateStatus.DuplicateUserName
                        [error] = "Duplicate user name."
                    Case MembershipCreateStatus.InvalidAnswer
                        [error] = "Invalid password recovery answer."
                    Case MembershipCreateStatus.InvalidEmail
                        [error] = "Invalid email address."
                    Case MembershipCreateStatus.InvalidPassword
                        [error] = String.Format("Invalid password. Requires at least {0} " +
                            "characters and {1} non-alphanumeric characters.",
                            Membership.Provider.MinRequiredPasswordLength,
                            Membership.Provider.MinRequiredNonAlphanumericCharacters)
                    Case MembershipCreateStatus.InvalidProviderUserKey
                        [error] = "Invalid provider user key."
                    Case MembershipCreateStatus.InvalidQuestion
                        [error] = "Invalid password recovery question."
                    Case MembershipCreateStatus.InvalidUserName
                        [error] = "Invalid user name."
                    Case MembershipCreateStatus.ProviderError
                        [error] = "Provider error."
                    Case MembershipCreateStatus.UserRejected
                        [error] = "User has been rejected."
                End Select
                Throw New Exception([error])
            End If
            ' assign "Comment" to the new user
            If (Not (String.IsNullOrEmpty(comment))) Then
                newUser.Comment = comment
                Membership.UpdateUser(newUser)
            End If
            ' assign "Roles" to the new user
            If (Not String.IsNullOrEmpty(roles)) Then
                Dim newRoles() As String = Convert.ToString(roles).Split(",")
                For Each role As String In newRoles
                    If (Not String.IsNullOrEmpty(role)) Then
                        System.Web.Security.Roles.AddUserToRole(userName, role)
                    End If
                Next

            End If
        End Sub
    End Class
End Namespace

Save the file.

Update

Create another business rule with the following properties:

Property Value
Type C# / Visual Basic
Command Name Update
Phase Before

Save the business rule, and generate the web app. Edit the rule in Visual Studio. You may need to press Refresh in the Solution Explorer toolbar of Visual Studio for the rule to appear.

Refresh button on the Solution Explorer may need to be pressed in order for the rule to appear.

Replace the code base with the following.

C#:

using System;
using System.Web.Security;
using MyCompany.Data;
using MyCompany.Security;

namespace MyCompany.Rules
{
    public partial class UsersBusinessRules : MyCompany.Data.BusinessRules
    {

        /// <summary>
        /// This method will execute in any view before an action
        /// with a command name that matches "Update".
        /// </summary>
        [Rule("r102")]
        public void r102Implementation(
                    FieldValue userID,
                    string userName,
                    FieldValue password,
                    FieldValue email,
                    FieldValue comment,
                    FieldValue passwordQuestion,
                    FieldValue passwordAnswer,
                    FieldValue isApproved,
                    DateTime? lastActivityDate,
                    DateTime? lastLoginDate,
                    DateTime? lastPasswordChangedDate,
                    DateTime? creationDate,
                    FieldValue isLockedOut,
                    DateTime? lastLockedOutDate,
                    int? failedPasswordAttemptCount,
                    DateTime? failedPasswordAttemptWindowStart,
                    int? failedPasswordAnswerAttemptCount,
                    DateTime? failedPasswordAnswerAttemptWindowStart,
                    FieldValue roles)
        {
            // prevent execution of CRUD operations by the application framework
            PreventDefault();
            // get user object by name
            MembershipUser user = Membership.GetUser(userName);
            if (user != null)
            {
                // update "Email" if changed
                if (email.Modified)
                {
                    user.Email = Convert.ToString(email.Value);
                    Membership.UpdateUser(user);
                }
                // update "Is Approved" if changed
                if (isApproved.Modified)
                {
                    user.IsApproved = Convert.ToBoolean(isApproved.Value);
                    Membership.UpdateUser(user);
                }
                // unlock user account if necessary
                if (isLockedOut.Modified)
                {
                    if (Convert.ToBoolean(isLockedOut.Value))
                    {
                        Result.Focus("IsLockedOut", Localize("UserCannotBeLockedOut",
                            "User cannot be locked out. If you want to prevent this " +
                            "user from being able to login then simply mark user as" +
                            " \'Not Approved\'."));
                        throw new Exception(Localize("ErrorSavingUser",
                            "Error saving user account."));
                    }
                    user.UnlockUser();
                }
                // update "Comment" if changed
                if (comment.Modified)
                {
                    user.Comment = Convert.ToString(comment.Value);
                    Membership.UpdateUser(user);
                }
                // update "Roles" if changed
                if (roles.Modified)
                {
                    string[] newRoles = Convert.ToString(roles.Value).Split(',');
                    string[] oldRoles = System.Web.Security.Roles.GetRolesForUser(
                        user.UserName);
                    foreach (string role in oldRoles)
                        if (!(String.IsNullOrEmpty(role)) && (Array.IndexOf(
                            newRoles, role) == -1))
                            System.Web.Security.Roles.RemoveUserFromRole(
                                user.UserName, role);
                    foreach (string role in newRoles)
                        if (!(String.IsNullOrEmpty(role)) &&
                            (Array.IndexOf(oldRoles, role) == -1))
                            System.Web.Security.Roles.AddUserToRole(
                                user.UserName, role);
                }
            }
        }
    }
}

Visual Basic:

Imports MyCompany.Data
Imports System
Imports System.Web.Security

Namespace MyCompany.Rules

    Partial Public Class UsersBusinessRules
        Inherits MyCompany.Data.BusinessRules

        ''' <summary>
        ''' This method will execute in any view before an action
        ''' with a command name that matches "Update".
        ''' </summary>
        <Rule("r102")> _
        Public Sub r102Implementation( _
                    ByVal userID As FieldValue, _
                    ByVal userName As String, _
                    ByVal password As FieldValue, _
                    ByVal email As FieldValue, _
                    ByVal comment As FieldValue, _
                    ByVal passwordQuestion As FieldValue, _
                    ByVal passwordAnswer As FieldValue, _
                    ByVal isApproved As FieldValue, _
                    ByVal lastActivityDate As Nullable(Of DateTime), _
                    ByVal lastLoginDate As Nullable(Of DateTime), _
                    ByVal lastPasswordChangedDate As Nullable(Of DateTime), _
                    ByVal creationDate As Nullable(Of DateTime), _
                    ByVal isLockedOut As FieldValue, _
                    ByVal lastLockedOutDate As Nullable(Of DateTime), _
                    ByVal failedPasswordAttemptCount As Nullable(Of Integer), _
                    ByVal failedPasswordAttemptWindowStart As Nullable(Of DateTime), _
                    ByVal failedPasswordAnswerAttemptCount As Nullable(Of Integer), _
                    ByVal failedPasswordAnswerAttemptWindowStart As Nullable(Of DateTime), _
                    ByVal roles As FieldValue, _
                    ByVal confirmPassword As String, _
                    ByVal roleID As Nullable(Of Integer))
            ' prevent execution of CRUD operations by the application framework
            PreventDefault()
            ' get user object by name
            Dim user As MembershipUser = Membership.GetUser(userName)
            If Not (user Is Nothing) Then
                ' update "Email" if changed
                If email.Modified Then
                    user.Email = Convert.ToString(email.Value)
                    Membership.UpdateUser(user)
                End If
                ' update "Is Approved" if changed
                If isApproved.Modified Then
                    user.IsApproved = Convert.ToBoolean(isApproved.Value)
                    Membership.UpdateUser(user)
                End If
                ' unlock user account if necessary
                If isLockedOut.Modified Then
                    If (Convert.ToBoolean(isLockedOut.Value)) Then
                        Result.Focus("IsLockedOut", Localize("UserCannotBeLockedOut",
                            "User cannot be locked out. If you want to prevent this " +
                            "user from being able to login then simply mark user as " +
                            "'Not Approved'."))
                        Throw New Exception(Localize("ErrorSavingUser",
                                "Error saving user account."))
                    End If
                    user.UnlockUser()
                End If
                ' update "Comment" if changed
                If comment.Modified Then
                    user.Comment = Convert.ToString(comment.Value)
                    Membership.UpdateUser(user)
                End If
                ' update "Roles" if changed
                If roles.Modified Then
                    Dim newRoles() As String = Convert.ToString(roles.Value).Split(",")
                    Dim oldRoles() As String = System.Web.Security.Roles.GetRolesForUser(
                        user.UserName)
                    For Each role As String In oldRoles
                        If Not (String.IsNullOrEmpty(role) And (Array.IndexOf(
                                                                newRoles, role) = -1)) Then
                            System.Web.Security.Roles.RemoveUserFromRole(user.UserName, role)
                        End If
                    Next
                    For Each role As String In newRoles
                        If Not (String.IsNullOrEmpty(role) And (Array.IndexOf(
                                                                oldRoles, role) = -1)) Then
                            System.Web.Security.Roles.AddUserToRole(user.UserName, role)
                        End If
                    Next
                End If
            End If
        End Sub
    End Class
End Namespace

Save the file.

Delete

Create another business rule:

Property Value
Type C# / Visual Basic
Command Name Delete
Phase Before

Save, regenerate the web app, and open the business rule file in Visual Studio. Replace the code:

C#:

using System;
using System.Data;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using System.Web;
using System.Web.Security;
using MyCompany.Data;
using MyCompany.Security;

namespace MyCompany.Rules
{
    public partial class UsersBusinessRules : MyCompany.Data.BusinessRules
    {

        /// <summary>
        /// This method will execute in any view before an action
        /// with a command name that matches "Delete".
        /// </summary>
        [Rule("r103")]
        public void r103Implementation(
                    FieldValue userID,
                    string userName,
                    string password,
                    string email,
                    string comment,
                    string passwordQuestion,
                    string passwordAnswer,
                    bool? isApproved,
                    DateTime? lastActivityDate,
                    DateTime? lastLoginDate,
                    DateTime? lastPasswordChangedDate,
                    DateTime? creationDate,
                    bool? isLockedOut,
                    DateTime? lastLockedOutDate,
                    int? failedPasswordAttemptCount,
                    DateTime? failedPasswordAttemptWindowStart,
                    int? failedPasswordAnswerAttemptCount,
                    DateTime? failedPasswordAnswerAttemptWindowStart)
        {
            // prevent execution of CRUD operations by the application framework
            PreventDefault();
            // delete the user account
            MembershipUser user = Membership.GetUser(userName);
            Membership.DeleteUser(user.UserName);
            // instruct the client library to display the previous view
            Result.ShowLastView();
            // instruct the client library to show a confirmation message
            Result.ShowMessage(String.Format(Localize("UserHasBeenDeleted",
                "User \'{0}\' has been deleted."), user.UserName));
        }
    }
}

Visual Basic:

Imports MyCompany.Data
Imports System
Imports System.Collections.Generic
Imports System.Data
Imports System.Linq
Imports System.Text.RegularExpressions
Imports System.Web
Imports System.Web.Security

Namespace MyCompany.Rules
    
    Partial Public Class UsersBusinessRules
        Inherits MyCompany.Data.BusinessRules
        
        ''' <summary>
        ''' This method will execute in any view before an action
        ''' with a command name that matches "Delete".
        ''' </summary>
        <Rule("r103")> _
        Public Sub r103Implementation( _
                    ByVal userID As FieldValue, _
                    ByVal userName As String, _
                    ByVal password As String, _
                    ByVal email As String, _
                    ByVal comment As String, _
                    ByVal passwordQuestion As String, _
                    ByVal passwordAnswer As String, _
                    ByVal isApproved As Nullable(Of Boolean), _
                    ByVal lastActivityDate As Nullable(Of DateTime), _
                    ByVal lastLoginDate As Nullable(Of DateTime), _
                    ByVal lastPasswordChangedDate As Nullable(Of DateTime), _
                    ByVal creationDate As Nullable(Of DateTime), _
                    ByVal isLockedOut As Nullable(Of Boolean), _
                    ByVal lastLockedOutDate As Nullable(Of DateTime), _
                    ByVal failedPasswordAttemptCount As Nullable(Of Integer), _
                    ByVal failedPasswordAttemptWindowStart As Nullable(Of DateTime), _
                    ByVal failedPasswordAnswerAttemptCount As Nullable(Of Integer), _
                    ByVal failedPasswordAnswerAttemptWindowStart As Nullable(Of DateTime), _
                    ByVal roles As String, _
                    ByVal confirmPassword As String, _
                    ByVal roleID As Nullable(Of Integer))
            ' prevent execution of CRUD operations by the application framework
            PreventDefault()
            ' delete the user account
            Dim user As MembershipUser = Membership.GetUser(userName)
            Membership.DeleteUser(user.UserName)
            ' instruct the client library to display the previous view
            Result.ShowLastView()
            ' instruct the client library to show a confirmation message
            Result.ShowMessage(String.Format(Localize("UserHasBeenDeleted",
                "User {0} has been deleted."), user.UserName))
        End Sub
    End Class
End Namespace

Save the code.

Tuesday, November 27, 2012PrintSubscribe
Advanced User Manager for Custom Membership and Role Provider: Adding “Roles” Tab

Let’s add a “Roles” tab on the Users page to allow management of roles. When a role is selected, users corresponding to the role will be displayed below. In order to create this “implied” master-detail relationship, a “UsersInRole” view must be created that will use a custom command to extract the RoleID.

Creating “Users In Role” View

Right-click on Users / Views / grid1 view node, and press Copy.

Copying 'grid1' view of Users controller.

Right-click on Users / Views node, and press Paste. A duplicate of the view will be created.

Pasting a view onto Users controller.     A duplicate of 'grid1' called 'v100' has been created.

Double-click on Users / Views / v100 node. Next to the Command property, select the New Command icon.

Creating a new command.

Assign these values.

Property Value
Id command2
Text
select
    "Users"."UserID" "UserID"
    ,"Users"."UserName" "UserName"
    ,"Users"."Password" "Password"
    ,"Users"."Email" "Email"
    ,"Users"."Comment" "Comment"
    ,"Users"."PasswordQuestion" "PasswordQuestion"
    ,"Users"."PasswordAnswer" "PasswordAnswer"
    ,"Users"."IsApproved" "IsApproved"
    ,"Users"."LastActivityDate" "LastActivityDate"
    ,"Users"."LastLoginDate" "LastLoginDate"
    ,"Users"."LastPasswordChangedDate" "LastPasswordChangedDate"
    ,"Users"."CreationDate" "CreationDate"
    ,"Users"."IsLockedOut" "IsLockedOut"
    ,"Users"."LastLockedOutDate" "LastLockedOutDate"
    ,"Users"."FailedPasswordAttemptCount" "FailedPasswordAttemptCount"
    ,"Users"."FailedPasswordAttemptWindowStart" 
        "FailedPasswordAttemptWindowStart"
    ,"Users"."FailedPasswordAnswerAttemptCount" 
        "FailedPasswordAnswerAttemptCount"
    ,"Users"."FailedPasswordAnswerAttemptWindowStart" 
        "FailedPasswordAnswerAttemptWindowStart"
    ,UserRoles.RoleID RoleID
from "dbo"."Users" "Users"
left join UserRoles on Users.UserID = UserRoles.UserID
Type Text
Event Select

Press OK to create the command and insert it into the property. Also make the following changes:

Property Value
Label Users In Role
Show In View Selector false

Press OK to save. In the Project Explorer, right-click on Users / Views / v100 and press Rename.

Renaming the view 'v100' using the context menu option.

Change the name to “UsersInRole” and press Enter on your keyboard to save.

View ID has been changed to 'UsersInRole'.

Adding “RoleID” Field

Right-click on Users / Fields node, and press New Field.

New Field context menu option for Users controller.

Assign the following properties:

Property Value
Name RoleID
Type Int32
The field is hidden from users. true
Label Role ID
Allow Query-by-Example true

Press OK to save the field. Drop Users / Fields / RoleID field node onto Users / Views / UsersInRole to create a data field and append it to the view.

Dropping RoleID field onto view 'UsersInRole'.     'RoleID' data field created in view 'UsersInRole'.

Adding Roles Tab to Users Page

Switch to the Controllers tab in the Project Explorer. While holding Ctrl key, click on Roles and Users controller nodes in that order. Right-click and press Copy.

Copying the Roles and Users controllers.

Switch back to the Pages tab. Right-click on Users / container1 node, and press Paste. The controllers will be instantiated as views in the container.

Pasting onto 'container1' of Users page.      The copied controllers have been instantiated as views in the container.

Double-click on Users / container1 / view1 (Users) data view node.

Selecting 'view1' on the Users page.

Change the following properties.

Property New Value
Activator Tab
Text Users
Search By First Letter true

Press OK to save. Double-click on Users / container1 / view2 (Roles) node.

Selecting 'view2' on the Users page.

Make the following changes.

Property New Value
Activator Tab
Text Roles

Save the data view. Double-click on Users / container1 / view3 (Users) node.

Selecting 'view3' on the Users page.

Make these changes:

Property New Value
View UsersInRole
Activator Tab
Text Roles

Save the data view. Expand Users / container1 / view3 (Users, UsersInRole) / UsersInRole node. Drop RoleID data field node onto Users / container1 / view2 (Roles) to establish a master-detail relationship.

Dropping 'RoleID' data field of UsersInRole view onto 'view2' in Users page.      A master-detail relationship has been created between 'view3' and 'view2'.