[CONNECT C#] - Issue with TagElement.ReplaceInModel(null)

I'm trying to update the URL for Engineering Links in a collection of DGN files. The problem is that I seam to be creating duplicates of the TAG when writing the updated element back into the model.

After processing a file, following the engineering link still takes you to the old address and not the new one.

/// <summary>
/// Scans model looking for tag elements processing them withScans model looking for tag elements, processing them with 
/// <see cref="TagElementProcessor(Element, DgnModelRef)"/>
/// </summary>
/// <param name="model">The model to scan</param>
private void UpdateEngineeringLinks(DgnModel model) {
    var criteria = new ScanCriteria();
    var elementTypes = new MSElementType[] { MSElementType.Tag };
    criteria.AddElementTypes(elementTypes);
    criteria.SetModelRef(model);
    var status = criteria.Scan(TagElementProcessor);
}

/// <summary>
/// Call back method for UpdateEngineeringLinks model scan. <see cref="Bentley.DgnPlatformNET.ScanDelegate"/>
/// <para>
/// If the element is an internet url tag, get the current value and then use <see cref="UpdateValue(string, out bool)"/>
/// to perform a replace of the matching part. Then if updated, set as the new value for the tag.
/// </para>
/// </summary>
/// <param name="el">The current element that matches the scan criteria</param>
/// <param name="modelRef">The model</param>
/// <returns></returns>
public StatusInt TagElementProcessor(Element el, DgnModelRef modelRef) {
    if(el.ElementType == MSElementType.Tag) {
        var model = modelRef.GetDgnModel();

        if (model.IsReadOnly) {
            return StatusInt.Error;
        }

        TagElement tagElement = (TagElement)el;
        if(tagElement.TagSetName.Equals( "internet", StringComparison.InvariantCultureIgnoreCase)
            && tagElement.TagName.Equals("url", StringComparison.InvariantCultureIgnoreCase)) {

            var oldValue = tagElement.GetTagValue().ToString();
            var newValue = UpdateValue(oldValue, out bool updated);

            if (updated) {
                LogDetails.AppendLine($"Updating Engineering link {tagElement.TagName} from '{oldValue}' to '{newValue}'");
                tagElement.SetTagValue(newValue);
                tagElement.ReplaceInModel(null);
            } else {
                LogDetails.AppendLine($"Skipping Engineering link {tagElement.TagName} with value '{oldValue}'");
            }

            return StatusInt.Success;
        }
    }

    return StatusInt.Error;
}

Am I missing something? (easily done given the poor quality of the API documentation).

Parents
  • Hi,

    Am I missing something?

    Well ... you did.

    easily done given the poor quality of the API documentation

    Actually not in this case, because despite of it's not explained in C# documentation and just basic (but not bad) description is available, it's used in a correct way in SDK examples.

    The problem is that I seam to be creating duplicates of the TAG when writing the updated element back into the model.

    The elements are added to the model because you ask API to do it.

    Instead of

    tagElement.ReplaceInModel(null);

    try to use this (as it's in SDK examples)

    tagElement.ReplaceInModel(tagElement);

    I think (but did not test it in C++) it's the same both in native and managed APIs: When oldRef (in NET it's toReplace) is not specified, the element is treated as new and is added to the model.

    For a curiosity, as often to check API code explains a lot. Using JustDecompile, some code erased because of readibility:

    public unsafe StatusInt ReplaceInModel(Element toReplace)
    {
        ...
        try
        {
            if (toReplace != null)
            {
                this.VerifyReadOnlyElementData();
                /* modopt(System.Runtime.CompilerServices.IsExplicitlyDereferenced) */ byte* byteu0026u0020modoptu0028Systemu002eRuntimeu002eCompilerServicesu002eIsExplicitlyDereferencedu0029 = (long)0;
                EditElementHandle* editElementHandlePointer = (EditElementHandle*)(&toReplace.ElementHandle[0]);
                int num = <Module>.Bentley.DgnPlatform.EditElementHandle.ReplaceInModel(elementHandle, <Module>.Bentley.DgnPlatform.ElementHandle.GetElementRef(editElementHandlePointer));
                StatusHandler.HandleStatus(num);
                statusInt.m_status = num;
                model = statusInt;
            }
            else
            {
                model = this.AddToModel();
            }
        }
        ...
        return model;
    }

    Much better than any documentation, isn't it? :-))))

    With regards,

      Jan

  • I was obviously miss lead by the following documentation

    Remarks

    If toReplace is null or equal to this , the current state of the element replaces the persistent state in the model.

    If toReplace is not currently a persistent element, then ReplaceInModel has the same effect as calling AddInModel.

    If toReplace is from a different model, ReplaceInModel does whatever is necessary to use this element in the target model.

    If the toReplace Element is not this or null , that Element becomes a nonpersistent element.

    ReplaceInModel throws an exception if the original element is in a read-only model or is a component of a complex element.

Reply
  • I was obviously miss lead by the following documentation

    Remarks

    If toReplace is null or equal to this , the current state of the element replaces the persistent state in the model.

    If toReplace is not currently a persistent element, then ReplaceInModel has the same effect as calling AddInModel.

    If toReplace is from a different model, ReplaceInModel does whatever is necessary to use this element in the target model.

    If the toReplace Element is not this or null , that Element becomes a nonpersistent element.

    ReplaceInModel throws an exception if the original element is in a read-only model or is a component of a complex element.

Children
No Data