玩转C科技.NET

从学会做人开始认识这个世界!http://tinyurl.com/volnet http://bit.ly/KMzi2

导航

<2012年1月>
25262728293031
1234567
891011121314
15161718192021
22232425262728
2930311234

统计

公告

Microsoft MVP!
2008.10-2010.10
Subscribe to this feed
Contact volnet online!

MSN群MyMSDN技术讨论群
群号:www.msdn@hotmail.com
Windows Live Alerts
LiveMessenger:
<My Library>

昵称:volnet(可以叫我大V)
园龄:7年3个月
粉丝:26
关注:10

搜索

 

常用链接

最新随笔

我的标签

随笔分类(182)

随笔档案(161)

文章分类(15)

文章档案(15)

相册

家园建设

积分与排名

  • 积分 - 238739
  • 排名 - 333

最新评论

阅读排行榜

评论排行榜

推荐排行榜

2012年1月3日 #

[FIM]两张表结构相同完美同步,如何做?ObjectAlreadyExistsException

假设有这么一个场景:
 
    表1和表2,假设结构相同(这不是重点),两张表都以userId作为Key。目标是从表1的数据同步到表2。
 
    实现:1、表1中有,表2中没有的数据,从表1同步到表2。
 
              2、表1中有,表2中也有的数据,以表1的各个字段为准。
 
              3、表1中没有,表2中有的数据,在表2中继续保留。
 
做法1:
 
    两个MA,分别以userId作为Anchor,DistinguishedName,并且字段分别映射。
 
    为MV编写Provisioning代码,并设置选项为启用MVExtension。
 
    同步顺序:表1 FullImport -> 表2 FullImport -> 表2 FullSync -> 表1 FullSync -> 表2 Export
 
    存在问题:
 
           当表2中原本存在数据,且该数据已经在表1中存在的时候,该条记录将引发ObjectAlreadyExistsException异常,该条记录没有处理,但正确的行为是不是应该是Join?遇到这个情况(异常)正确的解法是什么?
 
做法2:(改进做法1)
 
    考虑到有ObjectAlreadyExistsException异常,因此打算在Provisioning代码中将此异常抛弃,算作正常行为(因为此时对象确实存在),这样FIM应该按照自行逻辑进行Join操作?但是事实上,并非我所想(应该是理解有误),MV数据里面既有表1的数据,也有表2的数据,互不相干。

解决方案:

    当表1中的数据在表2种已经存在的时候(第一次同步)会抛出ObjectAlreadyExistException,应先关闭MV扩展(Tools->Options->清空第一个复选框),执行表2的FullImport->FullSync,然后再打开MV扩展,执行正常同步(表1的FullSync)。

    在实际操作过程中,其实通常并不需要先关闭MV扩展,只需要将表2先做一次FullSync,这样表1再做FullSync的时候,就可以避免这个问题了。

posted @ 2012-01-03 05:03 volnet(可以叫我大V) 阅读(33) 评论(0) 编辑

[FIM]如何从A导入数据,同步到B,在A系统中删除数据,在B系统中删除数据

问题描述:

从A导入数据,同步到B,在A系统中删除数据,在B系统中删除数据。

前提:A、B已经完成一次FULL_IMPORT、FULL_SYNC,假设在A中的数据在B中的数据(不考虑过滤),都一一对应。

按照之前的思路,在A中删除一条记录后,通过FULL_IMPORT的时候,在CS_A中会删除记录A,通过FULL_SYNC,因为CS_B中的DATA_1仍然与MV保持Connected,因此包括MV在内一直都会存在该条记录。

image

解决方案:

1、 首先要让MV中把这条记录删除才可能导致B系统删除该数据。

之所以出现以上现象的原因是在于MV的默认配置所决定的,右键对应的MV,“Configuration Object Deletion Rule…”,这是改变MV对象删除规则的。该设置默认为第一项,也就是考虑到所有MA都有可能成为源,当某MV对象与所有MA都断开联系后,才删除对应的MV,类似C++的引用计数。我们这里的需求是,一旦A中删除,就将其他系统中的该记录删除,因此这里就需要配置成第二项,任何一个被勾选的对象被删除之后,都删除该MV对象。

1­­­­­

2、 完成了第一步还不够的,因为它只提到了删除MV中的对象,但并不会删除CS_B中的对象,因此这时CS_B中的对象是孤立无援的,在FIM中是disconnected object,这样的对象会在下次同步的时候,继续参与规则讨论。因此我们要把这种没有户口的对象删掉。做法就是在对应的MA的配置中,配置“Configure Deprovisioning”,指示在一个CS对象与MV失去联系后的行为,选择第三项“Stage a delete on the object for the next export run”,该选项表明在失去联系后在接下来的export操作执行的时候删除数据。这里会删除connected data source中的数据也就是DB_A,但是不会删除CS_A中的数据,因为这一步操作在下一次FULL_IMPORT的时候是会被处理掉的。

2

3、 执行FULL_SYNC、EXPORT,数据依次从MV、DB_B中删除,检查DB_A,确实删掉了,执行B.FULL_IMPORT,CS_B中的数据也删除了,至此完成了这条记录的整个删除生命周期。

posted @ 2012-01-03 04:57 volnet(可以叫我大V) 阅读(26) 评论(0) 编辑

[FIM]如何通过Provision Code来向另一张表新增数据(附错误实例)

最近在做FIM的时候,发现Provisioning的项无法被添加了,然后发现自己在重构的时候,出现了一点儿画蛇添足。

首先说一下为什么要做Provisioning,原因是这样的,在FIM同步里面,假设有一个数据源,另一个目标数据源,目的是从一个数据源将数据导入到目标数据源中。默认情况下,FIM只处理两边都存在的数据,假设有这样两个数据:

表名:DB1.Table1

数据:

aUser, aUserName, aMobile

bUser, bUserName, bMobile

表名:DB2.Table2

bUser, bUserName1, bMobile1

cUser, cUserName1, cMobile1

默认情况下,两张表中,只有INNER JOIN的关系,也就是说bUser被关联了。这样在同步的时候,bUserName将会覆盖bUserName1,bMobile将会覆盖bMobile1,而aUser不会加入到DB2.Table2中。

而在实际的情况通常是DB1.Table1表代表一个类似HR的系统,新增一个员工的时候,我们希望新来的员工会出现在所有的第三方系统中,因此这个aUser就需要被强加到DB2.Table2中。

这需要我们为FIM启用Provisoion代码:(Tools->Options->Enable metaverse rules extension + Enable Provisioning Rules Extension)

image

然后我们在MVExtension的代码中实现接口void IMVSynchronization.Provision(MVEntry mventry)。

在我们进行同步的时候,我们需要先将两个数据源的数据都导入(Import)各自的CS中,为了区分不同的数据源的数据,我们为他们分别指定了dataSource的属性,这里我们用一个常量来表示。

当我们执行哪个MA同步的时候,这个dataSource的值(mventry[“dataSource”])就会是对应MA设置的那个常量,我们也就因此可以在MVExtensions代码中区分不同数据的来源了。

运行各个MA的时候,各个MA所对应的CS数据会被逐条通过mventry传入MVExtension中进行Provision。也就是说当我们执行DB1.Table1_MA的FULL_SYNC的时候,aUser和bUser都会被导入。当bUser导入的时候,我们可以想象,这个时候,如果我们不做任何事情,就离开这个Provision,按照默认的配置,Join-Rules将被激活,DB1.Table1_MA中的bUser将和DB2.Table2_MA中的bUser建立关联。于是会有一条Join-Rules:

image

那么aUser呢?如果不做Provisioning,它将被忽略。这个时候,aUser的ConnectedMAs中只有DB1.Table1_MA,而没有DB2.Table2_MA,因为它还不曾与之关联。

下面是一段标准的将DB1.Table1_MA中的aUser添加到DB2.Table2_MA的方法,当遇到aUser的mventry的时候,我们取得ConnectedMAs,然后将它添加到DB2.Table2_MA的CS中,并创建Connector。

private void ProvisioningFIMSys1User1(MVEntry mventry)
{
    try
    {
        Logger.Get().Write("-----ProvisioningFIMSys1User1 start-----");
        ConnectedMA MA = null;
        if (TryGetMA(mventry, MAs.FIMSys1_User1_MA.NAME, out MA))
        {
            string dn = string.Empty;
            Logger.Get().Write(string.Format("MA.Connectors.Count == {0}", MA.Connectors.Count));
            if (MA.Connectors.Count == 0) // create new one
            {
                CSEntry csEntry = null;
                Logger.Get().Write(string.Format("mventry[\"{0}\"].IsPresent = {1}", MVs.Person.Attributes.accountName, mventry[MVs.Person.Attributes.accountName].IsPresent));
                if (mventry[MVs.Person.Attributes.accountName].IsPresent)
                {
                    dn = mventry[MVs.Person.Attributes.accountName].StringValue;
                    Logger.Get().Write(string.Format("dn.0 = {0}", dn));
                    csEntry = MA.Connectors.StartNewConnector(MVs.Person.NAME);
                    // create DN for csEntry.
                    csEntry.DN = MA.CreateDN(dn);
                    csEntry.CommitNewConnector();
                    csEntry[MAs.FIMSys1_User1_MA.Attributes.UniqueID].StringValue = mventry[MVs.Person.Attributes.accountName].StringValue;
                    csEntry[MAs.FIMSys1_User1_MA.Attributes.UserName].StringValue = mventry[MVs.Person.Attributes.displayName].StringValue;
                    csEntry[MAs.FIMSys1_User1_MA.Attributes.Mobile].StringValue = mventry[MVs.Person.Attributes.mobile].StringValue;
                    Logger.Get().Write("-----csEntry.CommitNewConnector(); -----");
                }
            }
        }
    }
    catch (Exception ex)
    {
        Logger.Get().Write("Exception:" + ex.ToString());
        throw ex;
    }
    finally
    {
        Logger.Get().Write("-----ProvisioningFIMSys1User1 end-----");
    }
}

这个TryGetMA是一个用来获取MA的方法:

public bool TryGetMA(MVEntry mventry, string MAname, out ConnectedMA ma)
{
    ma = null;
    bool result = false;
    if (mventry == null)
        result = false;
    else
    {
        ma = mventry.ConnectedMAs[MAname];
        Logger.Get().Write("GET MA: " + MAname);
        if (ma == null)
            result = false;
        else
            result = true;
    }
    return result;
}

下面看看我做了什么蠢事,在之前它是这样写的(不正确):

public bool TryGetMA(MVEntry mventry, string MAname, out ConnectedMA ma)
{
    ma = null;
    bool result = false;
    if(mventry == null)
        result = false;
    else
    {
        if (ExistsMA(mventry, MAname))
        {
            ma = mventry.ConnectedMAs[MAname];
            Logger.Get().Write("GET MA: " + MAname);
        }
        if(ma == null)
            result = false;
        else
            result = true;
    }
    return result;
}

仔细看一下这个ExistsMA:

private bool ExistsMA(MVEntry mventry, string MAname)
{
    if (mventry != null && mventry.ConnectedMAs.Count > 0)
    {
        foreach (ConnectedMA ma in mventry.ConnectedMAs)
        {
            if (ma.Name == MAname) return true;
        }
    }
    return false;
}

倒不是这个方法抛出了异常。而是因为当这个mventry在还没有与该MA建立关联的时候,我就试图去寻找它,当然返回的结果一定是false,于是我永远都无法获得这个MA。

posted @ 2012-01-03 04:53 volnet(可以叫我大V) 阅读(18) 评论(0) 编辑

使用Live Messenger联系我
关闭