In the previous blog post,
How to Sync Users and Roles across Multiple servers in Kentico, you can find out how we mapped the users from one system to another, to save you a click, I’ve included a picture of how the environment is set up for syncing. As a reminder, please note that the code is for Kentico v8. Most of it will still apply to later versions, but it may need some adjustment. If you want to see the code that is running for the scheduled task that calls this, head back to
part 1 of this series.
Step 4 - Build the User Role Map
Once we’ve set up the User Roles to sync over to the different boat sites, we need to sync over the relationships between those users/roles, but now it gets a little complex. Why? Because we can't use IDs for synchronization, and the only thing that maps those two ... is a table of IDs.
So, we need to see a map of how the user roles look on the boat by ID and how the port would look if it used those same IDs. We also need the same map of the port and how the boat would look if it used the IDs for those.
It will look something like this when we are done so that all we have to do is compare the two maps to find out which ones we need to add and which ones to delete.
PortUserRoleMap |
BoatUserRoleMap |
BoatUserID: 60
PorttUserID: 6
BoatRoleID: 3
PortRoleID: 2
|
BoatUserID: 60
PorttUserID: 6
BoatRoleID: 3
PortRoleID: 2 |
BoatUserID: 60
PorttUserID: 6
BoatRoleID: 5
PortRoleID: 4
|
BoatUserID: 60
PorttUserID: 6
BoatRoleID: 5
PortRoleID: 4 |
BoatUserID: 61
PorttUserID: 5
BoatRoleID: 3
PortRoleID: 2
|
|
|
BoatUserID: 61
PorttUserID: 5
BoatRoleID: 5
PortRoleID: 4
|
This will tell us that we need to add the third RoleMap into our boat since it was added on the port, and that we need to delete the fourth one from our boat since it was deleted from the port. Below is the code that I used to actually create the mapping table.
private List<UserRoleMap> GetBoatUserRoleMaps(List<UserRoleInfo> boatRoleInfos)
{
List<UserRoleMap> userRoleMap=
boatRoleInfos
.Join(_mappedUsers,
boatRoleInfo => boatRoleInfo .UserID,
mappedUsers => mappedUsers.BoatUser.UserID,
(boatRoleInfo , mappedUsers) => new UserRoleMap
{
PortUserID = mappedUsers.PortUser.UserID,
BoatUserID = mappedUsers.BoatUser.UserID,
BoatRoleID = boatRoleInfo .RoleID
})
.Join(_mappedRoles,
userRoleMaps=> userRoleMaps.BoatRoleID,
roleMaps => roleMaps.BoatRole.RoleID,
(userRoleMaps, roleMaps) => new UserRoleMap
{
BoatUserID = userRoleMaps.BoatUserID,
PortUserID = userRoleMaps.PortUserID,
BoatRoleID = userRoleMaps.BoatRoleID,
PortRoleID = roleMaps.PortRole.RoleID
}).ToList();
return userRoleMap;
}
private List<UserRoleMap> GetPortUserRoleMaps(List<UserRoleInfo> portRoleInfos)
{
List<UserRoleMap> joinedRoles = portRoleInfos
.Join(_mappedUsers,
pri => pri.UserID,
maps => maps.PortUser.UserID,
(pri, maps) => new UserRoleMap
{
PortUserID = maps.PortUser.UserID,
BoatUserID = maps.BoatUser.UserID,
PortRoleID = pri.RoleID
}).ToList();
return joinedRoles
.Join(_mappedRoles,
urMaps => urMaps.PortRoleID,
roleMaps => roleMaps.PortRole.RoleID,
(urMaps, roleMaps) => new UserRoleMap
{
BoatUserID = urMaps.BoatUserID,
PortUserID = urMaps.PortUserID,
PortRoleID = urMaps.PortRoleID,
BoatRoleID = roleMaps.BoatRole.RoleID
}).ToList();
}
Step 5 - Sync the User Roles
The nice thing about this is that we don't need to worry about updating anything since it's just a mapping table of IDs. Adding is just looking for anything that is in our port map that isn't in our boat map. Deleting is just looking for anything that is in our boat map that isn't in our port map.
private List<UserRoleMap> AddUserRoles()
{
return _portUserRoleMaps
.Where(rm => !_boatUserRoleMaps.Any(
bm => bm.BoatRoleID == rm.BoatRoleID &&
bm.BoatUserID == rm.BoatUserID &&
bm.PortRoleID == rm.PortRoleID &&
bm.PortUserID == rm.PortUserID))
.Select(aur =>
{
UserRoleInfoProvider.AddUserToRole(aur.BoatUserID, aur.BoatRoleID);
return aur;
}).ToList();
}
private List<UserRoleMap> DeleteUserRoles()
{
return _boatUserRoleMaps
.Where(rm => !_portUserRoleMaps.Any(
bm => bm.BoatRoleID == rm.BoatRoleID &&
bm.BoatUserID == rm.BoatUserID &&
bm.PortRoleID == rm.PortRoleID &&
bm.PortUserID == rm.PortUserID))
.Select(aur =>
{
UserRoleInfoProvider.RemoveUserFromRole(aur.BoatUserID, aur.BoatRoleID);
return aur;
}).ToList();
}
The great thing about this method of user import is that it works irrespective of GUIDs, it's all based on the username (or whatever property/set of properties you want to use the maps for).
Let me know what you think!
Well, that’s it, I’ve given the basic structuring on how you can create your own Kentico User Importer with roles. If you wanted to, it wouldn’t be too hard to apply this to non-Kentico applications for any user/role with this mapping structure. I’d be happy to hear if anyone used this approach for their importer. If you have, please drop a comment below and let me know how it went!