HELP - How to prevent Studio from mis-resolving System.Activities?

Background / Context

I am currently working on a project to create a set of design-time tools (leveraging the Studio API SDK) to improve the developer experience in Studio. This project should be publicly available here: GitHub - yashbrahmbhatt/LazyFramework.DX: Supercharge your UiPath development experience with LazyFramework.DX! This package uses the power of UiPath's Studio SDK to provide delightful design-time tools for developers. Let’s be real: debugging and managing configuration files can be painful. LazyFramework.DX aims to make your life easier, and dare we say, fun!.

So far, I’d say it’s still in the PoC phase as there’s only 1 service/module that is created and even that doesn’t have all of the functionality I intend to support (AKA use at your own risk). Also I wrote most of it this weekend, so definitely IMMENSE amounts of cleanup and optimization is still required before I’d be satisfied with it.

If you’re just looking to help me and don’t care about what this project aims to achieve, scroll to the last header section titled ‘THE PROBLEM’.

The Vision

There were really only 2 modules that I had in mind when starting this project, maybe 2.5:

AutoConfig (named ‘Athena’)

  • Summary: Reads your excel or json config file and maps the values to a C# class. The idea is fuck Generics like Dictionaries that we have all been victims of. No longer would we need to double check what the key value in the config was. No longer would we update/write a config key in our xamls only to be englightened after 10 mins of testing that “The given key ‘{key}’ was not present in the Dictionary”. This was made possible with the 23.10 release with the introduction of Coded Workflows, and more importantly Coded Source Files. Since UiPath implemented a service in Studio that compiles .cs files and reflects their classes into the current AppDomain (or something like that, idk .NET), we are able to remove our dependency on generics. The two main generics I was looking to target were Config variables and QueueItem.SpecificContent or Data variables.

Here are some screenshots for what that looks like currently on a REFramework templated project:

  • Config.xlsx
  • AutoGenerated Config Files
    This image shows a file directory with expanded "ConfigClasses" folder containing two files: "BaseConfig.cs" and "Config.cs". (Captioned by AI)
    Base Class - To minimize the effort for adoption, I needed to implement a constructor that uses the output of the REFramework’s InitAllSettings workflow, so every Config class needs to be able to be initialized from an Excel/Dictionary<string, object>

    Class(es) based on Config settings

    *I know the category attribute doesn’t do anything useful since attributes are generally for the runtime, this will be updated to XML comments for each member based on the descriptions set in the config after I solve the major issue below.

File Watcher (named ‘Odin’)

  • Summary: A service internal to this package that lets the other modules subscribe to file system events and act accordingly. Currently, Athena uses it, and all planned modules all plan on using it.

Logger (named ‘Hermes’)

  • Summary: Since this project can’t log to Studio’s output window, I had to create another window for the logs of specifically this extension.

AutoDoc (Name TBD, thinking of Seshat or Nabu)

  • Summary: Reads your XAML files and automatically generates documentation about that workflow in the form of markdown (other output types can probably be supported in the future) in real time. Although not a part of the current source code, I think I’ve proven it out as it’s a workflow/folder I inject into all of my projects and it’s worked really well so far.

It started off as UiPath Workflows for parsing .xaml files and the project.json but compiled code source files have so much faster execution, so I converted it. I’m talking like 5 mins → 40s kinds of optimization from what I assume is just a compilation vs reflection difference.

Whenever it is run, the root ‘Documentation’ folder gets deleted and repopulated with markdown files that represent all the workflows.


* MD files don’t show up in the project panel

These markdown files then become useful when connected with a GIT Provider like AzDO or GitHub, where you are able to create Wiki’s from a folder in a branch of your repo and navigate through them as documentation.

Project MD file:

Workflow MD file:

All of the information is right there in the .xaml files, and like all developers, I hate rewriting it out myself when its within the code already. So I thought I could automate that translation.

This could be extended significantly to support all sorts of outputs like MSWord, PDF, etc.

2.5 Multi-‘Project’ Projects

  • Summary: This one I’ve done 0 implementation on and is the most subjective to personal preference, so thats why its a .5. Ever since multiple entry points were supported by UiPath (I don’t even remember how long ago they did that), I’ve wanted to host multiple UiPath projects (including libraries, test projects, etc.) within 1 Studio instance. I can’t really justify it as well as the previous points, but this seems like a useful tool to be able to use. Especially when RPA implementers are getting more sophisticated with CI/CD pipelines. The idea being that you have a folder structure like this:
  • Main Project (Where this service would run)
    • Library 1
      • Library1.project.json
    • Project 2
      • Entry Point 1
      • Entry Point 2
        • Project2.project.json
    • Test Project 3
      • Entry Point 3
        • Project3.project.json
  • project.json

We want to be able to compile each of those projects differently, but it would tremendously help the developer experience to be able to modify them without additional Studio instances or managing inter-dependencies between the projects. Unfortunately, UiPath instaces are bound to a single project.json. The workaround I was thinking of was:

  1. Hook Into UiPath’s Project Template feed
  2. Set up a Project Initialization function that takes the template and creates an ‘instance’ of it in your project as a folder inside of root.
  3. Since we have a FileWatcher instance already initialized for the other modules, this module can also subscribe for file changes and update every project.json accordingly in real time.
  4. This, combined with the UiPath CLI, would allow you to automatically segregate different projects for build/release, while maintaining a simplified and consolidated dev environment.

Again, this isn’t fully thought out yet and may never be implemented, but that was the idea; essentially providing a .sln or solution level support within UiPath Studio.

THE PROBLEM

  • Summary - If you use the package as it currently is, you will get this error whenever you compile the project. This includes at design-time when you’re trying to debug your code. This problem makes the entire project useless, as without the ability to debug workflows with Studio, the other functionalities are meaningless or get overshadowed by this problem.

I believe the root of this problem is some sort of coordination between:

  1. The target framework
  2. The dependencies
  3. How Studio works

This entire project heavily leverages the UiPath Studio Activities SDK (SDK - Studio Activities SDK). As part of the requirements to use that SDK and the implementation that UiPath recommends for it, we need to be able resolve the following types, which are not included in the UiPath Studio Activities SDK:

This project currently targets net6.0-windows, net7.0-windows, and net8.0-windows since those are the targets of the last few versions of UiPath Studio and those targets don’t allow you to import the System.Activities assembly from .NET since System.Activities is not compatible with them.

The only way to resolve those types that I’ve found is to use the UiPath.Workflow package (available here: GitHub - UiPath/CoreWF: WF runtime ported to work on .NET 6) and/or its derivative like System.Activities.Metadata as a dependency.

I believe the reason that we get that error during compilation when the LazyFramework.DX package is added as a dependency is due the how I think that UiPath Studio works. I believe it works by reflecting particular methods of particular classes, and so when it tries to do this when the LazyFramework.DX package is installed, it resolves the System.Activities.Activity class to the class defined in the UiPath.Activities.Metadata dependency instead of the default assembly included with Studio. UiPath.Activities.Metadata’s implementation of the Activity class doesn’t have a constructor that works with the way UiPath Studio’s ActivityFactory works, and so we see the error message in the screenshot above.

I’ve tried trying to create an ‘extern alias’ for the System.Activities.Metadata package, but that doesn’t seem to work :frowning:. Anyways, this is a long enough post as it is, please leave a comment if you have any ideas to help resolve this or if you think that the functionalities this project aims to provide sound like something you would use OR if you have ideas for any other functionality you would like to see at design-time.

ANY HELP WOULD BE GREATLY APPRECIATED <3

Thanks,
Yash Brahmbhatt

Hi Yash, this looks like an interestring project, definitely pushing what you should be doing through the activities API.

Can I ask what Studio version you are using?
And if possible could you provide some steps (or a branch) to reproduce the issue you mention? I’ve tried packaging your repo, adding it to a process in the latest Studio and compiling it and everything seems to work.

Regarding the packages you mention, I think the safest would be to include them as private assets, so that your nupkg doesn’t reference them, and allows Studio to resolve the proper version.

Something like:

  <PackageReference Include="System.Activities.Metadata" Version="6.0.0-20231204.1" PrivateAssets="All"/>
  <PackageReference Include="UiPath.Activities.Api" Version="24.10.1" PrivateAssets="All" /> 
  <PackageReference Include="System.Activities.Core.Presentation" Version="..." PrivateAssets="All"/>

Hey Marius,

Thanks for following up!

Yes! I managed to fix it with the private assets flag shortly after I made this post, so that’s probably why you didn’t see any issues :). Sorry I’m not very experienced in .NET, seems like a silly mistake to make.

It’s all running really well for the functionalities that I was planning to support, once I added debouncing for the UI update dispatches. Definitely see some performance bottlenecks I will need to figure out.

Anyways, do you know if there’s a community or resource group that is interested in the Activities API that I might be able to ask questions to? Like anyone leveraging the API more for the use case of Studio Extensions rather than enabling functionality for Activity packages?

I know this isn’t probably the use case you guys had in mind when creating the Activities API, but how could I resist spending days automating tasks that would probably take me minutes/hours to complete? Especially as an RPA developer. :rofl:

1 Like

Glad you got it sorted :blush:

About the community - not sure where to point you - @loginerror, any thoughts here?
All that comes to mind in terms of examples would be the open-source activities repo, you could use that for some inspiration - GitHub - UiPath/Community.Activities: Repository of Windows Workflow Foundation Activities for UiPath Community

Hi @eyashb

The closest space would actually be this forum. I feel like our RPA Discussions category is a great fit for these types of discussions.

You bring up an interesting point though - the visibility of more advanced topics, so that people who are interested in the matter (and have the knowledge to help) can easily find them.

Something for @loredana_ifrim and our @Forum_Moderators_2024 to chime in on :slight_smile: I could see the RPA Discussions space being a good fit myself, because it has an additional filtering by a tag:
image

But maybe others have different ideas :slight_smile:

1 Like

Not to dampen your enthusiam, but I would challenge this idea that you are doing as you are trying to solve a problem without thinking why its a problem and I’d argue also just moving the problem of spelling an asset or key incorrectly.

I too have gone down this route and did work to develop what you did, leveraging coded workflows to serve as the config, and then I eventually realized, rather than just fuck the generics as you said, fuck the config.

What I don’t think you have realized (yet) is that the config itself is a relic of the past, we do not need it and infact using a centralized tool to map things like assets or queue names just prevents you using great built in features in the Orchestrator aswell as things like the universal search in Studio to get all assets used.

Let me give you some examples.
When using an asset for a website URL, lets say in a workflow to log in, why is it that we decide we should load that asset elsewhere in the config? Why not just grab the asset immediately before we use it?
Convention says we shouldn’t but what else
With the new features added to Studio over the years all the arguments for using a config to centralize these things are gone. Infact if you do just ‘hard code’ your assets where you need them the Orchestrator validates if they are there for you!

Here is an example, it checks if the asset is available and warns you if it is not. It does the same with the Queue if you also nuke the config and just set the value in the queue. Now before you complain on that, doing that is indeed easy IF you restructure some of the REFramework.

For example, make a dedicated workflow thats job is just to ‘Get the next Transaction’, by default the only thing in it is the activty to get the next transaction item where you can just select the value from the dropdown and boom, you are done. Because this approach also much better follows SOLID principles aswell you can also easily have it get the next transaction from somewhere other than a classic queue, for example looping over files in the folder, processing them one by one, just add that simple logic to this workflow and you are done, avoiding all this different ‘types’ of REFrameworks people mess around with. Need extra filtering logic in this one? Or to check other criteria than default IF you should get the next item, again, no problem, you have an easily flexible workflow to do it.

I have gone on a slight tangent there, but my point is, you are overdesigning an overly complex solution to a problem without thinking laterally about why are we even working this way in the first place and introducing deeply complex code that will go wrong rather than taking it back to basics, applying good coding design to the architecture and elimating the problem rather than trying to work around it.

I have done this and its massively successful.

I urge you to reconsider diving so deeply into this.

1 Like

Thank you for pointing me in the right direction! I will make future posts aroudn this sort of stuff there :slight_smile:

Thank you for the detailed post! It’s an extremely good question that you’ve raised that I hadn’t given much thought to before…why not fuck the entire config? You could even have a workflow called LoadThings with every asset, queue, credential, storage bucket, etc. you need in your process and map to a global variable or something if you DO want to load them before they are needed or if they’re reused to avoid multiple calls.

I think I agree with you in that the ideal design would be to leverage UiPath’s built in tools for centralized configuration management. This would fall in line with UiPath’s Solution Management/Pipeline infrastructure as well!

However, there are still use cases where I cannot rely on UiPath’s built in tools for centralized configuration management. Consider the below scenarios:
1. Configuration is used by Library (ie. URL for web app)
Configuration management from UiPath’s side is not enabled at the library level, and thus if I have a GetAsset(myAppUrl, myFolder) activity or function call in my library before opening the browser, this isn’t captured through UiPath’s Package Requirements you’ve shown in your screenshot. Seems like UiPath just does a shallow search directly on the compiled entry point’s requirements, not extending to requirements of types or activities that are used. See screenshots below:
Library:


Process:

Orchestrator:

As you can see, the library’s asset requirement of ‘ShouldLoadAsset’ does not show up under the main process’ package requirements. This means that we need to make that asset value an argument in the library, and therefore a configuration in the calling process. While this is extremely doable and typically standard for libraries, I bring this up to start the discussion around gaps in UiPath’s ‘Package Requirements’ functionality which you suppose remove the need for centralized configuration management within projects completely.
2. UiPath doesn’t validate Storage Bucket files, only the bucket itself.
While UiPath’s package requirements validate the existence of the bucket that you are looking to use, they do not validate the actual files present in that bucket with the file paths referenced in your code. This creates a gap that can lead to errors when the bucket doesn’t contain the necessary files. Common use cases for storage bucket files likely include mapping files or text resources like email templates. So if I relied on UiPath’s Package Requirements functionality, I might be halfway through a transaction before I realize a mapping file doesn’t exist, at which point my bot could’ve made changes to the target system data that actually increased work for the downstream consumers of the business process.
3. Unnecessary elevation to Asset.
There’s typically a large number of configurations within a process that do not differ between environments. Using UiPath’s package requirements functionality as you suggested requires me to elevate every process’ settings to assets, and thus risk duplicating those configurations across every tenant I’m using OR hardcoding them. Why would I want to do that if the underlying configuration doesn’t actually change depending on the environment or if its reused? That overcrowds the Assets tab in orchestrator making maintenance a nightmare because you’ll have framework-level variables that need to be elevated to Assets. For example, suppose you have a ProcessesToKill variable that’s an array of strings used to kill process images with those names. You then have assets like 001_ProcessesToKill, 002_ProcessesToKill, etc. that you need to define across every tenant the processes run on. This seems unnecessary and frankly, annoying to deal with. I definitely don’t want to hardcode them.

These were my initial thoughts, maybe I’ll have more, but I love this discussion! Thank you so much for even responding, your post was extremely valuable for me to reassess my initial intentions. :slight_smile:

EDIT: I almost forgot. You can’t leverage the Package Requirements tab for CICD Pipelines implemented via UiPath or anywhere else (AzDO?)…You have to manually define everything you want to deploy. Without validation. So again, something I can’t rely on UiPath’s Package requirements functionality for.