如何将 DevExpress XAF SecuritySystemUser 迁移到 PermissionPolicyUser
关于如何将现有的 XAF 安全系统迁移到新的权限策略的说明。
引言
我花了一些时间将现有的 XAF 安全系统迁移到新的、改进了很多的策略权限系统,我想与大家分享我的经验和代码,以供其他可能决定进行此项艰巨任务的人参考。
我的应用程序仍在开发中,并且大量使用安全系统,这就是我决定转向改进的安全系统的原因。
由于这些脚本(希望如此)会把你的数据库搞得天翻地覆,我强烈建议你不要在生产环境运行它们,但如果你确实必须这样做 - 那么我说一句显而易见的 - 确保你有备份。多个备份。最好是在不同的介质上,在不同的地方,最好是在不同的洲。
另外,所有脚本都是 Microsoft SQL Server 特有的,所以如果你使用的是其他数据库,你需要修改所有元数据查询。
话不多说,我们开始吧。
使用代码
所有代码都可以使用现有的 XAF 更新机制来实现
UpdateDatabaseBeforeUpdateSchema
- 这会在 XAF 尝试将类更改推送到数据库之前执行;以及UpdateDatabaseAfterUpdateSchema
- 这会在 XAF 完成类更改推送后执行,届时我们可以进行额外的后更新任务。
我需要处理两种不同的列,它们将提供一个很好的例子,供你参考类似的场景。
- 在我应用程序的初始版本中,我创建了一个通用的父类,该类具有连接到
SecuritySystemUser
的UserCreated
和UserModified uniqueidentifier
属性。我决定将这些属性更改为nvarchar
类型,并保留用户名。 - 我想要保留并迁移到
PermissionPolicyUser
的用户属性(例如 -Employee.SystemUser
列)。
整个过程可分为以下几个步骤:
- 在
UpdateDatabaseBeforeUpdateSchema
中,将所有uniqueidentifier
列更改为nvarchar
类型,并用用户名更新新列。 - 此外,重命名所有现有列(及其外键和索引),这将允许 XAF 正确创建与
PermissionPolicyUser
连接的新列。 - 允许 XAF 做它自己的事情。
- 在
UpdateDatabaseAfterUpdateSchema
中 - 将用户、角色和权限从旧表复制到新表。 - 最后运行脚本,将新列更新为之前的数据,并删除旧列/关系/索引。
我将所有脚本封装在存储过程中,这样可以更轻松、更安全地从 XAF 调用(而不是多条 SQL 语句)。此外,每个存储过程都保存为一个单独的文件,并作为嵌入资源添加到我的非特定模块中,以便我在 XAF 更新过程中可以访问它们。
第一步 - 准备脚本并在 UpdateDatabaseBeforeUpdateSchema 中执行它们。
以下代码将加载嵌入的脚本。
if (CurrentDBVersion < new Version("3.5.3.105"))
{
// This updates XAF splash screen
UpdateStatus("InitSecurity", "Migrate users", "This action could take several minutes ...");
batchSql = Func.GetSqlScript(this.GetType(), "Link.Module.DatabaseUpdate.Script.{0}.sql", "sp_sys_ConvertUsersToString", "");
foreach (string sql in Func.GetNonQueryFromBatch(batchSql))
{
if (!string.IsNullOrWhiteSpace(sql)) ExecuteNonQueryCommand(sql, false);
}
using (var conn = DB.DBCommon.NewMSSqlConnection)
{
conn.Open();
using (SqlCommand cmd = new SqlCommand(@"EXEC dbo.sp_sys_ConvertUsersToString", conn))
{
cmd.CommandTimeout = 0;
cmd.ExecuteNonQuery();
}
conn.Close();
}
}
我们使用了辅助函数 GetSqlScript
(用于加载嵌入的资源脚本)和 GetNonQueryFromBatch
(用于解析 GO 语句之间的各个块),因此它们如下所示:
public static string GetSqlScript(Type type, string ns, string scriptName, string version)
{
Assembly _assembly = type.Assembly;
StreamReader _textStreamReader;
string text = "";
try
{
string resourceName = "";
if (version == "") resourceName = string.Format(ns, scriptName);
else resourceName = string.Format(ns, version, scriptName);
if (resourceName != "")
{
using (_textStreamReader = new StreamReader(_assembly.GetManifestResourceStream(resourceName)))
{
text = _textStreamReader.ReadToEnd();
_textStreamReader.Close();
}
}
}
catch
{
throw new Exception("Error accessing resources!");
}
return text;
}
public static List<string> GetNonQueryFromBatch(string batchSql)
{
List<string> result = new List<string>();
string sql = string.Empty;
batchSql += "\nGO"; // make sure last batch is executed.
foreach (string line in batchSql.Split(new string[2] { "\n", "\r" }, StringSplitOptions.RemoveEmptyEntries))
{
if (line.ToUpperInvariant().Trim() == "GO")
{
result.Add(sql);
sql = string.Empty;
}
else
{
sql += line + "\n";
}
}
return result;
}
这里是 sp_sys_ConvertUsersToString
存储过程的代码(更多详情请参阅注释)。
ALTER PROCEDURE [dbo].[sp_sys_ConvertUsersToString]
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
BEGIN TRAN T1;
DECLARE @sql nvarchar(MAX)
DECLARE @sch nvarchar(50)
DECLARE @tbl nvarchar(200)
DECLARE @col nvarchar(200)
DECLARE @fk nvarchar(200)
-- I had extra indices in all tables (seemed like a good idea in the time) but I do not need them any more, so I decided to use this chance
-- and remove them.
-- This query generates SQL that will drop all indexes with LinkID in it's name
-- This is not directly related to subject, but if you have some extra work you have been waiting to do - this is a good chance to do it
select @sql = (
select 'DROP INDEX ' + o.name + '.' + i.name + char(10) as [text()]
from sys.indexes i join sys.objects o on i.object_id=o.object_id
where i.name LIKE '%LinkID%'
for xml path('')
)
-- PRINT @sql
EXEC sp_executesql @sql
-- Following query will locate all columns in all tables which reference SecuritySystemUser table - except columns in those tables which are actually
-- part of Security System - we do not want to touch those or we might mess everything up
DECLARE cur_tbl CURSOR FOR
select
tab1.name AS [table],
sch.name AS [schema_name],
col1.name AS [column],
obj.name AS FK_NAME
FROM sys.foreign_key_columns fkc
INNER JOIN sys.objects obj
ON obj.object_id = fkc.constraint_object_id
INNER JOIN sys.tables tab1
ON tab1.object_id = fkc.parent_object_id
INNER JOIN sys.schemas sch
ON tab1.schema_id = sch.schema_id
INNER JOIN sys.columns col1
ON col1.column_id = parent_column_id AND col1.object_id = tab1.object_id
INNER JOIN sys.tables tab2
ON tab2.object_id = fkc.referenced_object_id
INNER JOIN sys.columns col2
ON col2.column_id = referenced_column_id AND col2.object_id = tab2.object_id
WHERE
tab2.name = 'SecuritySystemUser'
and not tab1.name like '%SecuritySystem%'
-- This is a helper table variable which will hold all related indices
DECLARE @idxtbl TABLE (
index_name nvarchar(200),
index_description nvarchar(200),
index_keys nvarchar(200)
)
DECLARE @sql2 nvarchar(max)
OPEN cur_tbl
FETCH NEXT FROM cur_tbl INTO @tbl, @sch, @col, @fk
WHILE @@FETCH_STATUS = 0
BEGIN
-- There are two kinds of columns in my database
-- First, those that I wanted to turn into nvarchar (to store username) instead of uniqueidentifier (User Oid)
-- I decided it is more convenient to store user name in the first place
-- All those columns in my database are called UserCreated and UserModified
IF @col in ('UserCreated', 'UserModified')
BEGIN
-- First I create SQL which will add new nvarchar column with Str sufix (which will temporarly hold username)
SET @sql = 'ALTER TABLE ' + @sch + '.' + @tbl + ' ADD ' + @col + 'Str nvarchar(100) '
EXEC sp_executesql @sql
--PRINT @sql
-- Next SQL will actually update this new Str column by making a join to SecuritySystemUser
SET @sql = ' UPDATE q SET q.' + @col + 'Str = u.UserName FROM ' + @sch + '.' + @tbl + ' q INNER JOIN dbo.SecuritySystemUser u on u.Oid = q.' + @col
EXEC sp_executesql @sql
--PRINT @sql
-- Then I drop constraint for uniqueidentifier column
SET @sql = 'ALTER TABLE ' + @sch + '.' + @tbl + ' DROP CONSTRAINT ' + @fk
EXEC sp_executesql @sql
--PRINT @sql
DELETE FROM @idxtbl
-- This will load all indices for given table into table variable
INSERT INTO @idxtbl
EXEC sys.sp_helpindex @objname = @tbl
-- After constraint is dropped, I also drop all indices that Oid column might have
-- I use index_keys property to only filter indices defined for Oid column
SELECT
@sql = (
select
' DROP INDEX ' + @sch + '.' + @tbl + '.' + index_name + CHAR(10)
from @idxtbl
where index_keys = @col
for xml path('')
)
EXEC sp_executesql @sql
--PRINT @sql
-- ThenI drop Oid column
SET @sql = ' ALTER TABLE ' + @sch + '.' + @tbl + ' DROP COLUMN ' + @col
EXEC sp_executesql @sql
--PRINT @sql
-- And finally I rename Str column to original name of the column
SET @sql = ' EXEC sp_rename ''' + @sch + '.' + @tbl + '.' + @col + 'Str'', ''' + @col + ''', ''COLUMN'''
EXEC sp_executesql @sql
--PRINT @sql
END
ELSE
BEGIN
-- Second type of columns are User Oids I actually want to keep and convert to PermissionPolicyUser
-- But, in order to do that - I have to rename all existing columns, relationship or indicies
-- I do that because I want XAF to re-create this columns with relationship to PermissionPolicyUser
-- But I do not want to delete them, because I want to transfer data after XAF does it's job
-- That is why I rename all columns I want to keep (renaming both relationship and indices are also
-- required, otherwise XAF will fail to update with error that foreign key or index already exists)
SET @sql =
-- This SQL will add Old sufix to column I want to keep
'EXEC sp_rename ''' + @sch + '.' + @tbl + '.' + @col + ''', ''' + @col + 'Old'', ''COLUMN''; ' + CHAR(10) +
-- This SQL will rename foreign key related to column I want to keep
' EXEC sp_rename ''' + @fk + ''',''' + @fk + 'Old'', ''OBJECT'';' + CHAR(10)
DELETE FROM @idxtbl
-- Same as in previous case, I load indices for related table
INSERT INTO @idxtbl
EXEC sys.sp_helpindex @objname = @tbl
-- And create SQL to rename all indices connected to column I want to keep
SELECT
@sql2 = (
select
' EXEC sp_rename ''' + @sch + '.' + @tbl + '.' + index_name + ''', ''' + index_name + 'Old'', ''INDEX''; ' + CHAR(10)
from @idxtbl
where index_keys = @col
for xml path('')
)
SET @sql = @sql + @sql2
EXEC sp_executesql @sql
--PRINT @sql
END
FETCH NEXT FROM cur_tbl INTO @tbl, @sch, @col, @fk
END
CLOSE cur_tbl
DEALLOCATE cur_tbl
COMMIT TRAN T1
END
GO
因此,在 UpdateDatabaseBeforeUpdateSchema
中,我们同时更新存储过程并执行它。您可能会注意到我使用的是 ALTER PROCEDURE
而不是 CREATE PROCEDURE
。
这是因为我在 UpdateDatabaseBeforeUpdateSchema
的开头执行了一个脚本,该脚本会创建任何不存在的具有**空主体**的存储过程、函数和视图。
这使得“依赖地狱”不那么糟糕:-) 并确保每个过程/函数/视图都存在,从而允许我始终使用 ALTER 脚本,而不必太过担心。
这是该脚本的一个片段:
IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[sp_sys_ConvertUsersToString]') AND type = N'P')
EXEC sp_executesql N'CREATE PROCEDURE [dbo].[sp_sys_ConvertUsersToString] AS BEGIN SET NOCOUNT ON END'
GO
IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[sp_sys_MigrateUsersToPermissionPolicy]') AND type = N'P')
EXEC sp_executesql N'CREATE PROCEDURE [dbo].[sp_sys_MigrateUsersToPermissionPolicy] AS BEGIN SET NOCOUNT ON END'
GO
当然,还有其他方法可以做到这一点,但我发现这种方法最方便,并将需要担心的依赖性降到了最低。
第二步 - XAF 完成其魔法。
在上述代码执行完毕并处理完我想要转换为 nvarchar
并保留用户名的列后,我需要允许 XAF 推送所有类更改,并实际创建新的权限策略表,同时在我在其中使用了想要迁移的用户(如 Employee.SystemUser
)的类中创建列和关系。
这由框架本身处理,并在 UpdateDatabaseBeforeUpdateSchema
完成执行后自动开始。
第三步 - 后更新迁移。
好的,我们现在在 UpdateDatabaseAfterUpdateSchema
中。
需要注意的是,你需要切换到新的安全系统。你可以通过应用程序(WinApplication.cs)上的模块设计器,将 PermissionPolicyUser
设置为 UserType
,将 PermissionPolicyRole
设置为 RoleType
(更多详情请参阅 XAF 文档),或者你可以像这样在代码中进行:
public static WindowsFormsApplication CreateApplication()
{
WindowsFormsApplication winApplication = new WindowsFormsApplication();
winApplication.Security =
new SecurityStrategyComplex(
typeof(DevExpress.Persistent.BaseImpl.PermissionPolicy.PermissionPolicyUser),
typeof(DevExpress.Persistent.BaseImpl.PermissionPolicy.PermissionPolicyRole),
new WinChangeDatabaseStandardAuthentication()); // This is my authentication type which allows database selection on login, so you will want to use standard XAF authentication here
}
新的表和列已经创建,我们现在需要从旧的已重命名的列迁移数据。
为了做到这一点,我使用了另一个名为 sp_sys_MigrateUsersToPermissionPolicy
的存储过程。我使用相同的系统 - 存储过程放在一个文件中,作为嵌入资源添加,然后加载并执行。
这是 UpdateDatabaseAfterUpdateSchema
代码。
if (CurrentDBVersion < new Version("3.5.3.107"))
{
UpdateStatus("InitSecurity", "Migrate users", "This action might take several minutes ...");
// We need to manually transfer users because C# scripts can not migrate passwords
ExecuteNonQueryCommand(@"
INSERT INTO PermissionPolicyUser (Oid, UserName, StoredPassword, ChangePasswordOnFirstLogon, IsActive)
SELECT NEWID(), UserName, StoredPassword, ChangePasswordOnFirstLogon, IsActive
FROM SecuritySystemUser
WHERE GCRecord IS NULL", false);
MySecurity.ConvertToNewSecurityModel(this, ObjectSpace);
batchSql = Func.GetSqlScript(this.GetType(), "Link.Module.DatabaseUpdate.Script.{0}.sql", "sp_sys_MigrateUsersToPermissionPolicy", "");
foreach (string sql in Func.GetNonQueryFromBatch(batchSql))
{
if (!string.IsNullOrWhiteSpace(sql)) ExecuteNonQueryCommand(sql, false);
}
using (var conn = DB.DBCommon.NewMSSqlConnection)
{
conn.Open();
using (SqlCommand cmd = new SqlCommand(@"EXEC dbo.sp_sys_MigrateUsersToPermissionPolicy", conn))
{
cmd.CommandTimeout = 0;
cmd.ExecuteNonQuery();
}
conn.Close();
}
}
因此,我们再次更新存储过程代码,然后执行它,但首先我们使用 SQL 迁移用户,因为 C# 代码无法迁移密码。
这是 sp_sys_MigrateUsersToPermissionPolicy
存储过程(更多信息请参阅注释)。
ALTER PROCEDURE [dbo].[sp_sys_MigrateUsersToPermissionPolicy]
AS
BEGIN
-- This procedure needs to be executed AFTER XAF updates database
DECLARE @sql nvarchar(MAX)
DECLARE @sch nvarchar(50)
DECLARE @tbl nvarchar(200)
DECLARE @fk nvarchar(200)
DECLARE @col nvarchar(200)
-- We fetch all columns that are referencing old SecuritySystemUser
-- This are basically all columns we renamed in previous step
-- Just to sleep tighter, we exclude any UserCreated and UserModified columns
-- Technically, those references should not exists any more, but anything for better sleep, right?
DECLARE cur_tbl2 CURSOR FOR
select
tab1.name AS [table],
sch.name AS [schema_name],
obj.name AS FK_NAME,
col1.name AS [column]
FROM sys.foreign_key_columns fkc
INNER JOIN sys.objects obj
ON obj.object_id = fkc.constraint_object_id
INNER JOIN sys.tables tab1
ON tab1.object_id = fkc.parent_object_id
INNER JOIN sys.schemas sch
ON tab1.schema_id = sch.schema_id
INNER JOIN sys.columns col1
ON col1.column_id = parent_column_id AND col1.object_id = tab1.object_id
INNER JOIN sys.tables tab2
ON tab2.object_id = fkc.referenced_object_id
INNER JOIN sys.columns col2
ON col2.column_id = referenced_column_id AND col2.object_id = tab2.object_id
WHERE
tab2.name = 'SecuritySystemUser'
and not tab1.name like '%SecuritySystem%'
and (
col1.name not in ('UserCreated', 'UserModified')
)
-- Table variable to hold related indices
DECLARE @idxtbl TABLE (
index_name nvarchar(200),
index_description nvarchar(200),
index_keys nvarchar(200)
)
DECLARE @sql2 nvarchar(max)
OPEN cur_tbl2
FETCH NEXT FROM cur_tbl2 INTO @tbl, @sch, @fk, @col
WHILE @@FETCH_STATUS = 0
BEGIN
SET @sql = ''
-- Here we check if XAF actually created new columns properly
-- If it did, we create SQL which will join to SecuritySystemUser using old column Oid and then using UserName join to new PermissionPolicyUser
-- and update new column wich matching Oid - hence migrating old user to new one
IF COL_LENGTH(@tbl, REPLACE(@col, 'Old', '')) IS NOT NULL
SET @sql =
'UPDATE q SET q.' + REPLACE(@col, 'Old', '') + ' = pu.Oid FROM ' + @sch + '.' + @tbl + ' q INNER JOIN dbo.SecuritySystemUser u on u.Oid = q.' + @col + ' INNER JOIN PermissionPolicyUser pu ON pu.UserName = u.UserName; ' + CHAR(10)
-- After that we drop constraint from Old column
SET @sql = @sql + ' ALTER TABLE ' + @sch + '.' + @tbl + ' DROP CONSTRAINT ' + @fk + '; ' + CHAR(10)
DELETE FROM @idxtbl
INSERT INTO @idxtbl
EXEC sys.sp_helpindex @objname = @tbl
-- And also all related indicies
SELECT
@sql2 = (
select
' DROP INDEX ' + @tbl + '.' + index_name + '; ' + CHAR(10)
from @idxtbl
where index_keys = @col
for xml path('')
)
SET @sql = @sql + @sql2
-- Finally - we drop Old column and migration is done
SET @sql = @sql +
' ALTER TABLE ' + @sch + '.' + @tbl + ' DROP COLUMN ' + @col + '; ' + CHAR(10)
EXEC sp_executesql @sql
-- PRINT @sql
FETCH NEXT FROM cur_tbl2 INTO @tbl, @sch, @fk, @col
END
CLOSE cur_tbl2
DEALLOCATE cur_tbl2
END
GO
最后 - 这是 DevExpress 提供的代码(我稍微修改了一下,因为我实际上是在 SQL 中迁移用户,而原始代码不支持这一点),包装在 ConvertToNewSecurityModel:
中。
public static void ConvertToNewSecurityModel(ModuleUpdater updater, IObjectSpace objectSpace)
{
using (MySecurity mySecurity = new MySecurity(updater, objectSpace, updateAll: false))
{
foreach (SecuritySystemUser securitySystemUser in objectSpace.GetObjects<SecuritySystemUser>())
{
mySecurity.CopyUser(securitySystemUser);
}
foreach (SecuritySystemRole PermissionPolicyRole in objectSpace.GetObjects<SecuritySystemRole>())
{
mySecurity.CopyRole(PermissionPolicyRole, null);
}
objectSpace.CommitChanges();
}
}
private void CopyUser(SecuritySystemUser securitySystemUser)
{
PermissionPolicyUser permissionPolicyUser = ObjectSpace.FindObject<PermissionPolicyUser>(new BinaryOperator("UserName", securitySystemUser.UserName));
if (permissionPolicyUser == null)
{
permissionPolicyUser = ObjectSpace.CreateObject<PermissionPolicyUser>();
permissionPolicyUser.UserName = securitySystemUser.UserName;
permissionPolicyUser.IsActive = securitySystemUser.IsActive;
permissionPolicyUser.SetPassword("123");
permissionPolicyUser.ChangePasswordOnFirstLogon = securitySystemUser.ChangePasswordOnFirstLogon;
}
foreach (SecuritySystemRole securitySystemRole in securitySystemUser.Roles)
{
CopyRole(securitySystemRole, permissionPolicyUser);
}
}
private void CopyRole(SecuritySystemRole securitySystemRole, PermissionPolicyUser permissionPolicyUser)
{
PermissionPolicyRole permissionPolicyRole = ObjectSpace.FindObject<PermissionPolicyRole>(new BinaryOperator("Name", securitySystemRole.Name));
if (permissionPolicyRole == null)
{
permissionPolicyRole = ObjectSpace.CreateObject<PermissionPolicyRole>();
permissionPolicyRole.Name = securitySystemRole.Name;
permissionPolicyRole.PermissionPolicy = SecurityPermissionPolicy.DenyAllByDefault;
permissionPolicyRole.IsAdministrative = securitySystemRole.IsAdministrative;
permissionPolicyRole.CanEditModel = securitySystemRole.CanEditModel;
foreach (SecuritySystemTypePermissionObject securitySystemTypePermissionObject in securitySystemRole.TypePermissions)
{
CopyTypePermissions(securitySystemTypePermissionObject, securitySystemRole, permissionPolicyRole);
}
foreach (SecuritySystemRole parentRole in securitySystemRole.ParentRoles)
{
CopyParentRole(parentRole, permissionPolicyRole);
}
}
if (permissionPolicyUser != null && !permissionPolicyUser.Roles.Any(x => x.Name == permissionPolicyRole.Name))
{
permissionPolicyUser.Roles.Add(permissionPolicyRole);
}
}
private void CopyParentRole(SecuritySystemRole parentRole, PermissionPolicyRole permissionPolicyRole)
{
if (parentRole.IsAdministrative)
{
permissionPolicyRole.IsAdministrative = true;
}
if (parentRole.CanEditModel)
{
permissionPolicyRole.IsAdministrative = true;
}
foreach (SecuritySystemTypePermissionObject securitySystemTypePermissionObject in parentRole.TypePermissions)
{
CopyTypePermissions(securitySystemTypePermissionObject, parentRole, permissionPolicyRole);
}
foreach (SecuritySystemRole subParentRole in parentRole.ParentRoles)
{
CopyParentRole(subParentRole, permissionPolicyRole);
}
}
private void CopyTypePermissions(SecuritySystemTypePermissionObject securitySystemTypePermissionObject, SecuritySystemRole securitySystemRole, PermissionPolicyRole permissionPolicyRole)
{
PermissionPolicyTypePermissionObject permissionPolicyTypePermissionObject = ObjectSpace.FindObject<PermissionPolicyTypePermissionObject>(new BinaryOperator("TargetType", securitySystemTypePermissionObject.TargetType));
permissionPolicyTypePermissionObject = ObjectSpace.CreateObject<PermissionPolicyTypePermissionObject>();
permissionPolicyTypePermissionObject.TargetType = GetTargetType(securitySystemTypePermissionObject.TargetType);
permissionPolicyTypePermissionObject.Role = permissionPolicyRole;
if (securitySystemTypePermissionObject.AllowRead)
{
permissionPolicyTypePermissionObject.ReadState = SecurityPermissionState.Allow;
}
if (securitySystemTypePermissionObject.AllowWrite)
{
permissionPolicyTypePermissionObject.WriteState = SecurityPermissionState.Allow;
}
if (securitySystemTypePermissionObject.AllowCreate)
{
permissionPolicyTypePermissionObject.CreateState = SecurityPermissionState.Allow;
}
if (securitySystemTypePermissionObject.AllowDelete)
{
permissionPolicyTypePermissionObject.DeleteState = SecurityPermissionState.Allow;
}
if (securitySystemTypePermissionObject.AllowNavigate)
{
permissionPolicyTypePermissionObject.NavigateState = SecurityPermissionState.Allow;
}
foreach (SecuritySystemObjectPermissionsObject securitySystemObjectPermissionsObject in securitySystemTypePermissionObject.ObjectPermissions)
{
CopyObjectPermissions(securitySystemObjectPermissionsObject, permissionPolicyTypePermissionObject);
}
foreach (SecuritySystemMemberPermissionsObject securitySystemMemberPermissionsObject in securitySystemTypePermissionObject.MemberPermissions)
{
CopyMemberPermission(securitySystemMemberPermissionsObject, permissionPolicyTypePermissionObject);
}
permissionPolicyRole.TypePermissions.Add(permissionPolicyTypePermissionObject);
}
private void CopyMemberPermission(SecuritySystemMemberPermissionsObject securitySystemMemberPermissionsObject, PermissionPolicyTypePermissionObject permissionPolicyTypePermissionObject)
{
PermissionPolicyMemberPermissionsObject permissionPolicyMemberPermissionsObject = ObjectSpace.CreateObject<PermissionPolicyMemberPermissionsObject>();
permissionPolicyMemberPermissionsObject.TypePermissionObject = permissionPolicyTypePermissionObject;
if (securitySystemMemberPermissionsObject.AllowRead)
{
permissionPolicyMemberPermissionsObject.ReadState = SecurityPermissionState.Allow;
}
if (securitySystemMemberPermissionsObject.AllowWrite)
{
permissionPolicyMemberPermissionsObject.WriteState = SecurityPermissionState.Allow;
}
permissionPolicyMemberPermissionsObject.Members = securitySystemMemberPermissionsObject.Members;
permissionPolicyMemberPermissionsObject.Criteria = securitySystemMemberPermissionsObject.Criteria;
permissionPolicyTypePermissionObject.MemberPermissions.Add(permissionPolicyMemberPermissionsObject);
}
private void CopyObjectPermissions(SecuritySystemObjectPermissionsObject securitySystemObjectPermissionsObject, PermissionPolicyTypePermissionObject permissionPolicyTypePermissionObject)
{
PermissionPolicyObjectPermissionsObject permissionPolicyObjectPermissionsObject = ObjectSpace.CreateObject<PermissionPolicyObjectPermissionsObject>();
permissionPolicyObjectPermissionsObject.TypePermissionObject = permissionPolicyTypePermissionObject;
if (securitySystemObjectPermissionsObject.AllowRead)
{
permissionPolicyObjectPermissionsObject.ReadState = SecurityPermissionState.Allow;
}
if (securitySystemObjectPermissionsObject.AllowWrite)
{
permissionPolicyObjectPermissionsObject.WriteState = SecurityPermissionState.Allow;
}
if (securitySystemObjectPermissionsObject.AllowDelete)
{
permissionPolicyObjectPermissionsObject.DeleteState = SecurityPermissionState.Allow;
}
if (securitySystemObjectPermissionsObject.AllowNavigate)
{
permissionPolicyObjectPermissionsObject.NavigateState = SecurityPermissionState.Allow;
}
permissionPolicyObjectPermissionsObject.Criteria = securitySystemObjectPermissionsObject.Criteria;
permissionPolicyTypePermissionObject.ObjectPermissions.Add(permissionPolicyObjectPermissionsObject);
}
private Type GetTargetType(Type currentType)
{
if (currentType == null) return null;
Type outType;
if (!SecurityAssociationClassDictionary.TryGetValue(currentType, out outType))
{
outType = currentType;
}
return outType;
}
private static Dictionary<Type, Type> SecurityAssociationClassDictionary = new Dictionary<Type, Type>(){
{ typeof(SecuritySystemUser),typeof(PermissionPolicyUser) },
{ typeof(SecuritySystemRole),typeof(PermissionPolicyRole) },
{ typeof(SecuritySystemTypePermissionObject ),typeof(PermissionPolicyTypePermissionObject ) },
{ typeof(SecuritySystemObjectPermissionsObject ),typeof(PermissionPolicyObjectPermissionsObject ) },
{ typeof(SecuritySystemMemberPermissionsObject ),typeof(PermissionPolicyMemberPermissionsObject ) }
};
技巧
我遇到了一个相当奇怪的情况,上述代码未能加载旧的 SecuritySystemUser 和 SecuritySystemRole,因为系统已切换到新的权限策略类,所以旧的似乎不再被 XAF 注册了。
通常,我会通过在非特定模块设计器中找到这些类,找到所需的类型,然后简单地选择“在应用程序中使用”来解决这个问题。
但出于某种原因,我在任何地方都找不到这些类,所以我将以下代码放在了 Module.cs 构造函数中:
public LinkModule()
{
AdditionalExportedTypes.AddRange(
new Type[] { typeof(SecuritySystemUser), typeof(SecuritySystemRole) });
InitializeComponent();
}
这解决了问题,旧的类被正确填充了。
终点线
就这样 :-)
你的场景肯定会与我的不同,但过程是相同的 - 实际的 SQL 脚本需要根据你的具体情况进行调整。
如果还需要更多细节,请不要犹豫,给我写一两句话。
祝你迁移顺利!