jeremytammik / RevitLookup

Interactive Revit RFA and RVT project database exploration tool to view and navigate BIM element parameters, properties and relationships.
http://thebuildingcoder.typepad.com
MIT License
1.03k stars 293 forks source link

Build Automation Version is breaking Revit 2025 #246

Closed ricaun closed 3 days ago

ricaun commented 1 month ago

Hello,

I'm messing with the RevitLookup project to make work with Microsoft.Extensions.Hosting version 8 and was able to make work.

Great, I build the installation everything works and builds fine. But when I install the version 2025 does not work...?

Something related to the SetVersion in the Build automation when having others configuration to build make something and breaks.

Here is the video showing the issue: RevitLookup 2025 - Build Version Error

And the repo with that project: https://github.com/ricaun/RevitLookup/tree/dev-context

I'm using the AssemblyLoadContext to create a new context to load the plugin in there, looks promising to fix the assembly hell and #210.

Like that I can load whatever dll I want in the context. Like version Microsoft.Extensions.Hosting 8.0.0.

RevitLookup - DI 8 0 0

And here is a video with RevitLookup running with that approach: Dll Hell and RevitLookup 2025 - Experiment

Nice3point commented 1 month ago

Hi Luiz. AssemblyLoadContext is a really useful thing that we should add. However, I still have no idea what is wrong with SetVersion in your branch. I'll be able to do it as soon as I finish the visualisation feature

ricaun commented 1 month ago

Hi Luiz. AssemblyLoadContext is a really useful thing that we should add. However, I still have no idea what is wrong with SetVersion in your branch. I'll be able to do it as soon as I finish the visualisation feature

No worries, I never use this SetVersion, usually I set the csproj with same minor and build version for all the Revit version. Maybe cleaning the build before each SetVersion do the trick, because only breaks when using SetVersion with another Revit build.

Nice3point commented 1 month ago

SetVersion useful when you have a modular project with 10 dlls, and it is more handy to change the version in one place instead of editing csproj 10 times 😉

ricaun commented 1 month ago

SetVersion useful when you have a modular project with 10 dlls, and it is more handy to change the version in one place instead of editing csproj 10 times 😉

If you want different versions between dll make sense, I usually set everything with the same version using Directory.Build.props.

<Project>
  <PropertyGroup>
    <Version>1.2.3</Version>
  </PropertyGroup>
</Project>

And in the csproj something like this or without Version:

<PropertyGroup>
  <Version Condition="'$(Version)' == ''">1.0.0</Version>
</PropertyGroup>
Nice3point commented 1 week ago

изображение

This feature will be integrated into Nice3point.Revit.Toolkit, and all plugins using it will work in a separated context. Need some time to testing to share this out. We dont need to update our plugins, just update Toolkit version

this is good news, it would be great if the Revit team would make this the default in new Revit versions

ricaun commented 1 week ago

This feature will be integrated into Nice3point.Revit.Toolkit, and all plugins using it will work in a separated context. Need some time to testing to share this out. We dont need to update our plugins, just update Toolkit version

this is good news, it would be great if the Revit team would make this the default in new Revit versions

Neat! I don't think you should force the Nice3point.Revit.Toolkit to work in a separated context by default. I'm a big fan of optional and explicit configuration, like a different class or something.

If you create a preview repo/package I can make some stress tests.

Nice3point commented 6 days ago

I don't think you should force the Nice3point.Revit.Toolkit to work in a separated context by default

Not running add-ins in a separate context is a bad idea. What are the benefits of being optional? Dependency conflicts? I think it should be enabled always. And at Revit level, instead of adding support to each plugin. Currently the library option requires that the plugin that is loaded first has the latest version of Toolkit (because latest version has AssemblyContext and loads on addin startup). Yes this is a flaw, but it will be regardless of what the library name is, at the moment we are creating the context for ourselves. In the future Revit should do it.

2025.0.1-preview.1.0 pushed, you can test.

<PackageReference Include="Nice3point.Revit.Toolkit" Version="$(RevitVersion).*-*"/>

ExternalCommand, ExternalApplication, ExternalDbApplication supported and isolated

You don't need to do anything on the add-in side, just inherit the entry point from ExternalCommand instead of IExternalCommand, same for Application

works fine with Nuke

ricaun commented 6 days ago

I agree should be done by Revit, maybe each AddInId with his own Context.

By default everything loads in a same context in Revit 2025 that's the normal behavior, if you create a new context in your toolkit by default and something snick and loads in the main context because Revit gonna load in there, conflict could happen.

Like using IExternalCommandAvailability that is not included in your list of isolated class.

I know that if you update the right now the RevitLookUp gonna work fine, just updating the package. But you know that you cannot use the IExternalCommand anymore only ExternalCommand, basically all the strong interfaces between Revit and the plugin is banned.

If is optional the develop gonna know the requirements and the limitations of the enabling AssemblyContext feature, that's not the normal behavior of loading addin in Revit.

Or you can force to create a context anyway and the developer learns the way, is your library you can do whatever you want. 😆

Nice3point commented 6 days ago

But you know that you cannot use the IExternalCommand anymore only ExternalCommand

You can use IExternalCommand, why not? ExternalCommand simply implements this interface and adds new conveniences such as resolving library conflicts. If you need to, you can use IExternalCommand + ResolveHelper https://github.com/Nice3point/RevitToolkit?tab=readme-ov-file#resolvehelper as before instead of a separate context. This will help to load any dependency from the plugin folder, but will not protect against incompatible version conflicts. However, AssemblyLoadContext is more preferable, and it was partly developed for plugins

If is optional the develop gonna know the requirements and the limitations of the enabling AssemblyContext feature, that's not the normal behavior of loading addin in Revit.

This carries no restrictions for the developer, none whatsoever. Even if it uses IExternalCommandAvailability, which I didn't add a separated context to, it will just use Default, it won't raise an exception or anything else

Creating context at the library level on the contrary will make the developer's job easier, because it will just work under the hood, and you won't have to think about isolation in the plugin itself. it will make life a lot easier

ricaun commented 5 days ago

You can use IExternalCommand but gonna use the Default context, that's my point. If you have the Plugin context the code should never use the Default context, that's the main reason you create your own context to safely load the plugin and dependencies in there.

If in the Default context some method request a class in the Plugin context what gonna happen?

A simple issue example would be a static integer, two values is possible, one in the Default context and another in Plugin context. Is not an exception but should exist only one possible value for a static propriety.

That's a huge benefit of using AssemblyLoadContext but the developer need to be aware of the limitations of using that in the plugin.

For me using AssemblyLoadContext is a workaround to fix the dependence problem, if Autodesk implement that inside Revit would be the proper solution. Until there I would use AssemblyLoadContext wisely.

Nice3point commented 5 days ago

Thank you for the clarification. We need to investigate further in which cases it is necessary to switch the context, in addition to ExternalCommand and ExternalApplication. If Autodesk implements it on their side, there will be no such problems, because the whole dll will be loaded into a separate context, instead of the current situation when the plugin has to create a context for itself

ricaun commented 5 days ago

Is only necessary to switch the context in the strong Revit interfaces. That basically the interfaces registered by the ClassName that is a plain text.

This interfaces, IExternalCommand, IExternalCommandAvailability, IExternalApplication and IExternalDBApplication, by default gonna be created using the ClassName in the Default context. Is necessary to recall each one in the Plugin context to isolate Revit and the plugin references.

The rest of the interfaces use the instance object and gonna be already inside the Plugin context.

Nice3point commented 3 days ago

https://github.com/jeremytammik/RevitLookup/releases/tag/2025.0.8