.NET API - Object losing it's viewmodel

Hi there,

im trying to update one(!) single object in a speckle stream.
That means one, for example, one wall recieves a additional attribute which is send but its the only thing that should change from the commit before.

However the addition of data works fine, but If I update via the .NET API the model can’t be viewed again as the geometry seems vanished.

Here is my current code approach without the data adding as the problem stays even when no data is added:

Main-Method

public async Task CommitAdditionalData(string streamid, string id, AdditionalData data, string commitMessage)
        {
            try
            {
                var branch = await _speckleClient.BranchGet(streamid, "main", 1); // get branch
                var objectId = branch.commits.items[0].referencedObject; // take last commit
                var parent = await GetObjectAsync(streamid, objectId); // get parent object 

                //var updatedObject = UpdateObjectInStream(parent, id, "@AdditionalData", data); // update parent object

                var status = await CommitObject(streamid, parent); // commit the object to speckle
            }
            catch (System.Exception ex)
            {

                throw;
            }
        }

        public async Task<Base> GetObjectAsync(string streamId, string id)
        {
            try
            {
                var transport = new ServerTransport(_speckleClient.Account, streamId);
                dynamic data = await Operations.Receive(
                  id,
                  remoteTransport: transport,
                  disposeTransports: false,
                  serializerVersion: SerializerVersion.V2
                );
                return data;
            }
            catch (System.Exception ex)
            {
                throw;
            }
        }

        public async Task<bool> CommitObject(string streamId, Base obj, string commitMessage = "CommitScanData", string sourceApplication = "My Server")
        {
            var sendTransport = new ServerTransport(_speckleClient.Account, streamId);
            try
            {
                string objectId = await Operations.Send(@obj, new List<ITransport>() { sendTransport }, false, serializerVersion: SerializerVersion.V2);

                CommitCreateInput commitInput = new CommitCreateInput
                {
                    branchName = "main",
                    message = commitMessage,
                    sourceApplication = sourceApplication,
                    streamId = streamId,
                    objectId = objectId,
                    totalChildrenCount = (int)obj.totalChildrenCount
                };
                await _speckleClient.CommitCreate(commitInput);
                return true;
            }
            catch (Exception ex)
            {
                return false;
            }
        }

Anyone knows what im doing wrong? I read the docs and haven’t found anything useful for my problem.

1 Like

Hi, this is one part of Speckle that needs to be (1) clarified and (2) improved for this specific usecase. Some parts of the answer are in this thread. Anyway, a tl;dr:

Speckle is immutable storage for object graphs. That means that if you want to change one object within that graph, the change needs to bubble up to the parent - and we need to resave the parent object with its new identity. It’s actually a super cool problem, but one that we haven’t yet gotten to attack - so thanks for pushing us to do this :slight_smile:

Depending on your specific usecase:
A) if you only need that data to make its way back into a host application, e.g. Revit, I’d use the trick that excel does: Excel | Speckle Docs

B) if you need a persistent record single source of truth in a Speckle commit, it’s a bit more tricky, but doable. The steps would be:

  1. get the whole referenced object of that specific commit
  2. within that object, modify the sub-object you want to add a prop to
  3. save the new whole object to speckle and create a new commit

For some pseudocode:


let myWholeCommitObject = await SpeckleClient.GetObject( commit.referencedObject );

let objectIWantToChange = myWholeCommitObject["@walls"].firstOrDefault( obj => obj.applicationId == xxx);

objectIWantToChange["foo"] = "bar"; // or whatever else you want to change; it's important that you do not break the reference within the parent object; so if you need to replace an item in the list of walls with a new one, do it carefully :) 

let newId = await SpeckleClient.saveObject( myWholeCommitObject );
let newCommit = { referencedObject: newId, etc. }; 
let newCommitId = await SpeckleClient.CommitCreate(newCommit);

Apologies for possibly mixing up two programming languages above - let me know if I need to clarify the intention!

2 Likes

Hi @dimitrie ,

thanks for your answer! :slight_smile:

However, I think I already did that you said for mutating my object. The saving already works fine but something messes the objects, even when not mutating the data.

In the code above I get the “parent” object (like you said in [1]) and them I’m, without modifying anything, pushing it back to speckle. The problem here is that the geometry gets lost, so there is, at the momemt, no problem with mutating the data. Maybe its better I’ll post some screenshots so its clear whats the problem.

The commit via the Speckle Revit Connector:

The commit after getting that object and pushing it to Speckle again.

As you can see the model is missing after the push trough the c# api and the “speckle type” changed from Revit.Walls (whatever) to just “Base”. I don’t know if thats the default behaviour.

Hm, indeed!

This is not cool, and shouldn’t really happen - unless the object model (kit) is not loaded in app domain where you’re doing the change. If we don’t find a type to deserialise to, Speckle will do its best and use Base. Can you make sure there’s a Speckle.Objects.Wall.Revit.etc accessible in your app (esp. where the receiving is happening)?

Hi @dimitrie

No I have’nt included Speckle.Objects nor Speckle.Objects.Converter.Revit2022.
I included it now but it seems that he can’t load the assembly. He tries to load Objects.Converter.Revit2022 but I cant even find that assembly in Visual Studio.

The only thing I find is Objects.Converter.Revit (shoudn’t that be Speckle.Objects.Converter.Revit?).
Also the dll’s are named Objects.dll and Objects.Converter.Revit2022.dll and are definitly in that folder.

Theoretically you should need only Objects.dll. Have you tried referencing it in the project via nuget? NuGet Gallery | Speckle.Objects 2.3.0

I removed the Revit.Converter. Now it says:

InnerException = {“Could not load file or assembly ‘Objects, Version=2.3.0.9631, Culture=neutral, PublicKeyToken=null’.”:null}

And yes, I referenced it via the nuget-package.
I just tried reinstalling everything & restarting Visual Studio without any success.

The exceptions triggers here:

                var x = new Objects.Primitive.Interval(); // works. builds a object from Objects.Primitive;
                if (true) ;

                var transport = new ServerTransport(_speckleClient.Account, streamId);
                Base data = await Operations.Receive(
                  id,
                  remoteTransport: transport,
                  disposeTransports: false,
                  serializerVersion: SerializerVersion.V2
                ); // crash -> InnerException	{"Could not load file or assembly 'Objects, Version=2.3.0.9631, Culture=neutral, PublicKeyToken=null'.":null}	System.Exception {System.IO.FileLoadException}

Hi,

i’ve spoke to a developer of our company and he had a simliar problem which he also already posted on github.

The problem seems to be with the different dlls on a local machine while debugging in Visual Studio.
After I’ve deleted C:\Users{user}\AppData\Roaming\Speckle\Kits\Objects the server worked without any problems.

So please reopen this github issue as this is still a bug.

Assembly load exception for “Objects.dll” if both Revit connector and docker image of the speckle server exists · Issue #642 · specklesystems/speckle-sharp (github.com)

1 Like

Thanks for sorting this out, and posting the solution to it 🙇‍♂️ (I closed that issue as there wasn’t any new activity on it). It’s more of dev environment problem, rather than an explicit bug. Not sure how we could provide a bullet proof way of solving it.

We could add extra functionality that would perhaps prevent any automatic loading of kits/converters in core, and enforce it to rely only on the installed references - do you think that would allow you to have the revit connector installed & be able to debug your server application?

Hi @dimitrie,

I think the problem is loading assemblies from local folder: C:\Users{user}\AppData\Roaming\Speckle\Kits\Objects.

All applications which use Speckle.Core trying to load assembly “Objects.dll” from this folder if it is exists. Is there a specific reason to load this assembly from this folder?

image

Yes, well, there are reasons - some of which i’m regretting partly :smiley:

Because kits (object models + conversions) are hot swappable in Speckle, they are never hard-referenced from connectors, and they are dynamically loaded at runtime from that folder.

This is definitively a PITA for your use case, and I’m thinking to allow developers to stop/prevent that automagic loading - it’s basically an undocumented side effect currently (that works brilliantly for desktop connectors, but not for server apps :sweat_smile: )

Would that help?

It works also with Server apps actually, as long as you do not use any Connectors :slight_smile: . Do you think a flag in KitManager.cs would help to let KitManager know whether the application runs in local development mode?

I was thinking more of a generic flag that you could set - regardless of developer mode or not - that would just prevent automatic kit loading!

(PS: I’ve reopened https://github.com/specklesystems/speckle-sharp/issues/642)

2 Likes