Background
In my previous post I was facing a type issue that someone helped me.
Now I tried to run my script in Revit and I get an error.
I dont know if its because its inside Revit environment, bur Im used to python and pyRevit where the error message is much more readable and clearer regarding what I need to fix.
This script is not exactly new, I wrote it in python (works and in use) and now I want to learn C#.
What Ive tried
After reading the error message my guess is that the error has something to do with .AsString() method
I tried both .AsString() and .AsStringValue() when I want to get the Parameter's value, and also made sure that the storage type is actually string.
My questions
1. Is this how you normally get error messages in C#?
2. What do you think about my error? please see code below.
Code
using System;
using Autodesk.Revit.Attributes;
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;
using Autodesk.Revit.UI.Selection;
using System.Collections.Generic;
using System.Security.Claims;
namespace UntaggedDevicesInSheet
{
[Transaction(TransactionMode.Manual)]
[Regeneration(RegenerationOption.Manual)]
public class Class1 : IExternalCommand
{
public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
{
// variables
UIApplication uiApp = commandData.Application;
Document doc = uiApp.ActiveUIDocument.Document;
View activeView = doc.ActiveView;
var devicesDict = new Dictionary<string, Dictionary<string, List<ElementId>>>();
// creating a combined filter out of categories of devices we want to check if they're tagged or not
ICollection<BuiltInCategory> device_categories = [BuiltInCategory.OST_FireAlarmDevices, BuiltInCategory.OST_SecurityDevices];
List<ElementCategoryFilter> device_category_filters = [];
foreach (BuiltInCategory category in device_categories)
{
device_category_filters.Add(new ElementCategoryFilter(category));
}
LogicalOrFilter devices_combined_filter = new LogicalOrFilter((IList<ElementFilter>)device_category_filters);
// creating a combined filter out of categories of device tags we want
ICollection<BuiltInCategory> tag_categories = [BuiltInCategory.OST_FireAlarmDevices, BuiltInCategory.OST_SecurityDevices];
List<ElementCategoryFilter> tag_category_filters = [];
foreach (BuiltInCategory category in tag_categories)
{
device_category_filters.Add(new ElementCategoryFilter(category));
}
LogicalOrFilter tags_combined_filter = new LogicalOrFilter((IList<ElementFilter>)tag_category_filters);
// get all viewports in sheet
IEnumerable<Element> allViewports = new FilteredElementCollector(doc, activeView.Id).OfClass(typeof(Viewport)).WhereElementIsNotElementType().ToElements();
foreach (Element viewPort in allViewports)
{
string detailNum = viewPort.get_Parameter(BuiltInParameter.VIEWPORT_DETAIL_NUMBER).AsValueString();
// get & sort devices by category
ICollection<Element> allDevicesInViewport = new FilteredElementCollector(doc, viewPort.Id).WherePasses(devices_combined_filter).WhereElementIsNotElementType().ToElements();
// sort devices by category
List<Element> fireAlarmDevices = [];
List<Element> securityDevices = [];
foreach (Element device in allDevicesInViewport)
{
string categoryName = device.Category.Name;
if (categoryName == "Fire Alarm Devices")
{
fireAlarmDevices.Add(device);
}
else if (categoryName == "Security Devices")
{
securityDevices.Add(device);
}
}
// get & sort tags by category
IEnumerable<Element> allTagElements = new FilteredElementCollector(doc, viewPort.Id).WherePasses(tags_combined_filter).WhereElementIsNotElementType().ToElements();
IEnumerable<IndependentTag> allTags = allTagElements.Cast<IndependentTag>();
List<IndependentTag> allFireAlarmTags = [];
List<IndependentTag> allSecurityTags = [];
foreach (IndependentTag tag in allTags)
{
string catergoryName = tag.Category.Name;
if (catergoryName == "Fire Alarm Device Tags")
{
allFireAlarmTags.Add(tag);
}
else if (catergoryName == "Security Device Tags")
{
allSecurityTags.Add(tag);
}
}
// Get all TAGGED fire alarm + security devices tags
List<ElementId> taggedFireDevicesIds = allFireAlarmTags.Select(tag => tag.GetTaggedLocalElement().Id).ToList();
List<ElementId> taggedSecurityDevicesIds = allSecurityTags.Select(tag => tag.GetTaggedLocalElement().Id).ToList();
// Get all UNTAGGED fire alarm + security devices tags
List<ElementId> untaggedFireDevices = fireAlarmDevices.Where(fireDevice => !taggedFireDevicesIds.Contains(fireDevice.Id)).Select(fireDevice => fireDevice.Id).ToList();
List<ElementId> untaggedSecurityDevices = securityDevices.Where(securityDevice => !taggedFireDevicesIds.Contains(securityDevice.Id)).Select(securityDevice => securityDevice.Id).ToList();
devicesDict[detailNum] = new Dictionary<string, List<ElementId>>
{
{ "fire", untaggedFireDevices },
{ "security", untaggedSecurityDevices }
};
}
string finalReport = "";
foreach (var kvp in devicesDict)
{
int fireLen = kvp.Value["fire"].Count;
int securityLen = kvp.Value["security"].Count;
if (fireLen > 0 | securityLen > 0)
{
string viewportNum = kvp.Key;
finalReport += $"Viewport #{viewportNum}:\n" +
$"{fireLen} Untagged Fire devices\n" +
$"{securityLen} Untagged security devices\n" +
new string('-', 20) + "\n";
}
}
TaskDialog.Show("Viewport Information", finalReport);
return Result.Succeeded;
}
}
}
Solved! Go to Solution.
Solved by ricaun. Go to Solution.
Weird stuff. I suggest you explore running non-Revit-API code in the C# debugger first, before moving on to your Revit add-in. You should be able to set a breakpoint in the first line of code of your Execute method, and then stop there when you launch your external command. Your error message looks strange:
Where is the type "System.Span' 1" coming from? Who is requesting such a thing?
Can you debug your code at all? Can you set a breakpoint in the debugger? Can you step into your code?
Once you can do those things, you will be able to see which line of code is causing the exception.
For what its worth, before This script I opened up a console app with Visual Studio and ran some C# code lines successfully.
After that I went through Autodesk's Lesson 1: The Basic Plug-in and the add-in worked just fine...
I can't seem to get it to compile. I'm no C# expert, and I tend to forget how things work if I haven't used them in a while.
But I don't understand the use of the square brackets in lines 34 and 35, and other similar lines. I also don't understand the use of the $ in the string starting on line 114.
And I can't seem to get the compiler to understand them either 😞
The code uses some modern C#, your can enable in your project by adding this in the csproj.
<PropertyGroup>
<LangVersion>latest</LangVersion>
</PropertyGroup>
@StrRevitEngwhat version are you build the code: .NET Core or .NET Framework? And what Revit version are you using?
Im using Revit 2021 and my the VS project is .net 8.0 ... Im guessing now that its a nono...
Where am I suppose to add it? in the manifest? at which part?
Revit version 2021 to 2024 is net framework 4.8, you need to update your csproj to the correct framework.
Inside your csproj you need to replace the TargetFramework to net48. And you could add the LangVersion config as well.
Something like this:
<PropertyGroup>
<TargetFramework>net48</TargetFramework>
<LangVersion>latest</LangVersion>
</PropertyGroup>
This should fix the 'System.TypeLoadException: Could not load type 'System.Span' 1'' error.
You probably need to install in your machine the 4.8 framework.
Inside the Visual Studio 2022 -> Tools -> Get Tools and Features...
Inside Modifying -> Individual components -> .NET Framework 4.0 targeting pack
Select and install 😀
Thanks for helping, I did what you've said but still I dont see that 4.8 option in my project, or when trying to set up a new project... I also tried resetting my PC and VS.
Looks like is not possible to change .NET Core to .NET Framework using the Application properties, but you can change directly if you edit the .csproj.
When creation a new project you need to select the .NET Framework version.
Manually editing the .csproj to change TargetFramework is the only way if you want to use the same project.
In Visual Studio you can double click in the name of the project and the .csproj open when your project was created with .NET Core template, or right click and Edit Project File.
Is always a good idea to make a backup before changing the .csproj file.
Thanks for your help.
My script is running and working thanks to you.
The weird thing is, is that when I did Autodesk's add-in tutorial - I did it with the .net 8.0, and It worked properly.
In my original project I even changed the C# syntax to match the 4.8 version, But what made it finally work is creating a new project with the 'Class Library' you suggested.
The problem is that since Revit 2025, Autodesk switching from .Net Framework to .Net Standard. I believe that you read and tried the latest tutorial so that it instructed you to use .Net Standard (.Net 8.0). Maybe it is a coincidence that you run it on the compatible version of Revit.
This time, you did the same thing and run it in Revit 2021 which still using .Net Framework. That 's why it did not work. Normally, you can specify if it is a .Net Standard or a .Net Framework when you create a new class library project. Hope this clarify things for you.
Can't find what you're looking for? Ask the community or share your knowledge.