[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

Reply Children
  • LinkUpdateTest.dgn

    This DGN file contains two squares, the square on the left has an engineering link while the one on the right has a design link. I'm attempting to create is a find and replace URL tool that will update part or all of the URL in Design and Engineering Links.

    I have no problem updating the engineering links using a MicroStation VBA macro and will us that approche for the current job, but would like to complete the tool sometime in the near future at which point I'll include the option to convert the Engineering links to Design links.

    I also have another project coming up that will require Tag values to be updated so am keen to get the code required for editing tags sorted.

    Note that Engineering links are essentially stored as Tags (tag set name:- internet). 

  • Note that Engineering links are essentially stored as Tags

    The relevant information is provided in C++ header file ..\SDK\include\Mstn\MdlApi\tagdata.h.

    I'll include the option to convert the Engineering links to Design links

    Design Links are provided by an ECSchema.  The DgnECHostType Enumeration declares and defines value DgnECHostType.DesignLinkHowever, AFAIK there is no public API, in any language, that deals with Design Links.  Only the C++ MicroStationAPI provides a Design Link API: here's an example.

    Please make a new post for questions about Design Links.

     
    Regards, Jon Summers
    LA Solutions