[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 have modified the code as follows:

    if (tagElement.SetTagValue(newValue) == BentleyStatus.Success) { 
        try { 
            tagElement.ReplaceInModel(tagElement);
            LogDetails.AppendLine($"Updating Engineering link {tagElement.TagName} from '{oldValue}' to '{newValue}'");
        } catch (DgnPlatformNETException) {
            LogDetails.AppendLine($"Failed to save updated Engineering link {tagElement.TagName} from '{oldValue}' to '{newValue}'");
        }
    } else {
        LogDetails.AppendLine($"Failed to updated Engineering link {tagElement.TagName} from '{oldValue}' to '{newValue}'");
    }

    It is now throwing a generic exception originating from StatusHandler.HandelStatus(num) as num is a non zero value (BentleyStatus.Error, 0x8000). I suspect that it is due to it being a component of a complex element as the Model should be read/write.

    So where to from here?

    Which managed .NET code example shows how to modify a component of a complex element and which elements constitute the complex element in the case?

  • Hi Dave,

    can you post some simple DGN as example to ensure it would be clear how your complex element is structured and to what element the tag is attached to?

    With regards,

      Jan

  • Which managed .NET code example shows how to modify a component of a complex element

    I don't know if there is such an example.  The idiomatic way to modify a complex element is...

    • Read the element into memory
    • Find the component to modify
    • Replace the component with a new element
    • Rewrite the entire complex element

    However, a tag element is not part of its host element.  The host element is not affected when you attach a tag.  A tag is associated with its host through the Dependency Engine using the host's Element ID.  The tag's host Element ID is stored in the tag element.  More here.

    I don't think there's any guidance on this, but I assume that it's best to attach a tag to the outermost header of a complex element.

     
    Regards, Jon Summers
    LA Solutions

Reply
  • Which managed .NET code example shows how to modify a component of a complex element

    I don't know if there is such an example.  The idiomatic way to modify a complex element is...

    • Read the element into memory
    • Find the component to modify
    • Replace the component with a new element
    • Rewrite the entire complex element

    However, a tag element is not part of its host element.  The host element is not affected when you attach a tag.  A tag is associated with its host through the Dependency Engine using the host's Element ID.  The tag's host Element ID is stored in the tag element.  More here.

    I don't think there's any guidance on this, but I assume that it's best to attach a tag to the outermost header of a complex element.

     
    Regards, Jon Summers
    LA Solutions

Children
No Data