Revit : Get All ViewSheet is very slow - c#

for a plugin I need to get all the viewsheet in the rvt file and display informations from them in an xaml dialog
but my process is very very slow the first time I use it
(with the debuger : 500 ms for 83 viewplan , it is very slow without the debuger too)
(if I execute my code again, the execution is istantaneous)
my code bellow
can you help me ?
thanks in advance
Luc
protected IEnumerable<Element> GetAllEl(Document document)
{
var filteredElementCollector = new FilteredElementCollector(document);
filteredElementCollector = filteredElementCollector
.OfCategory(BuiltInCategory.OST_Sheets)
.WhereElementIsNotElementType()
.OfClass(typeof(ViewSheet));
var fcElements = filteredElementCollector.ToElements();
return fcElements;
}

I do not think there is currently a known generic solution for that problem.
Here is a recent discussion with the development team on this:
Question: for a given element id, we need to find the list of sheet ids displaying it.
Current solution: we loop through all the sheets and views and use FilteredElementCollector( doc, sheet.Id)
With the results from that, we perform one more call to FilteredElementCollector( doc, view.Id) and look for the element id.
Issue: the current solution takes a lot of time and displays a Revit progress bar saying Generating graphics.
Is there any better way to know if a given element id is available in the sheet or not?
For example, something like this would be very useful:
getAllSheets(ElementId) // returns array of sheet id
hasGuid(ElementId,sheetId) // return true/false
Does the API provide any such methods, to check whether a given ElementId is available in the sheet?
Answer: So the goal is to find a view that displays a particular element on a sheet?
Many model elements could be visible on multiple views, while most annotation elements are typically present only in one view.
What type of elements are you checking for?
And what will you do with that info?
Response: the goal is to find a view that displays a particular element on a sheet.
It can be any type of element.
Answer: Here are some previous related discussions:
Determining Views Showing an Element
The inverse, Retrieving Elements Visible in View
Response: The problem is that the first call to FilteredElementCollector( doc, viewId ) shows generating graphics in the progress bar.
Only the first time search does so. The second time, search on the same view has no issues with performance.
Answer: The first time is slow because in order to iterate on the elements visible in a view the graphics for that view must be generated.
I can't think of a workaround to get a precise answer.
You might be able to skip sheets which don't have model views in their viewport list to save a bit of time.
Some sheets may only have drafting views and schedules and annotations.
The development team provided a very helpful suggestion which helped work around the generating graphics call in a special case,
to Loop through sheets - generating graphics.
Maybe you can optimise in a similar manner for your specific case?

I think you may be over-filtering the ElementCollector. In my add-in, I just use this code to get the view sheets: new FilteredElementCollector(_doc).OfClass(typeof(ViewSheet));

Related

Get all Nodes and Relationships with Propperties outgoing from one StartNode

I'm completely new in the GraphDatabase world and also in writing Cypher Statements.
I have a project, where I want to store wiring diagram information inside a graph database (Neo4J). There are different types of Nodes like f.e. WiringDiagram [WD] Node (will be my start node in many cases) and regarding this WD Node all components like fuseboxes, ICU's or sensors are linked via relationships. Plugs also can contain Pins, and Pins are connected via Connectionlines.
The first version is already stored in the Neo4j Database, have a look at the following image.
Now I have a question which way is best to post-process this data. I want the data extracted for one specific wiring Diagram.
So if I would say I want all information about WiringDiagram with ID 123, I should get all components, Pins and Connectenlines which are there. How should the Cypher look like here?
I want the data best in C# Data models (if possible). Because afterwards I want to try to generate an SVG out of the data.
As you can see in the image, the cypher statement looks currently like this. "MATCH (w:WiringDiagram)<-[r:partOf]-(n)-[*2..]-(l) RETURN * LIMIT 50" But with this statement I get strange results in my C# Project...
I would be happy about any help. I'm also open to go forward with another programming language if it fits better for this approach. Happy to hear any suggestions
I found an APOC function which currently does what I want to.
Get All nodes after the searched one, and give the complete subgroup with relationships back.
Looks currently good.
Any suggestion how to store this data back in data models in C#? (whats best way?)
var result = tx.Run($#"MATCH (p:WiringDiagram {{wiringid:1}})
CALL apoc.path.subgraphAll(p, {{
relationshipFilter:
""partOf|has_pin|connectedWith"",
filterStartNode:false,
minLevel: 0,
maxLevel: 10
}})
YIELD nodes, relationships
RETURN nodes, relationships;");
And after that I've got two lists, one with all the nodes, and one with all relationships with start/endnode id

Remove list values based on series of other values

I have a situation wherein a List object is built off of values pulled from a MSSQL database. However, this particular table is mysteriously getting an errant record or two tossed in. Removing the records cause trouble even though they have no referential links to any other tables, and will still get recreated without any known user actions taken. This causes some trouble as it puts unwanted values on display that add a little bit of confusion. The specific issue is that this is a platform that allows users to run a search for quotes, and the filtering allows for sales rep selection. The select/dropdown field is showing these errant values, and they need to be removed.
Given that deleting the offending table rows does not provide a desirable result, I was thinking that maybe the best course of action was to modify the code where the List object is created and either filter the values out or remove them after the object is populated. I'd like to do this in a clean, scalible fashion by providing some kind of appendable data object where I could just add in a new string value if something else cropped up as opposed to doing something clunky that adds new code to find the value and remove it each time.
My thought was to create a string array, and somehow loop through that to remove bad List values, but I wasn't entirely certain that was the best way to approach this, and I could not for the life of me think of a clean approach for this. I would think that the best way would be to add a filter within the Find arguments, but I don't know how to add in an array or list that way. Otherwise I figured to loop through the values either before or after the sorting of the List and remove any matches that way, but I wasn't sure that was the best choice of actions.
I have attached the current code, and would appreciate any suggestions.
int licenseeID = Helper.GetLicenseeIdByLicenseeShortName(Membership.ApplicationName);
List<User> listUsers;
if (Roles.IsUserInRole("Admin"))
{
//get all users
listUsers = User.Find(x => x.LicenseeID == licenseeID).ToList();
}
else
{
//get only the current user
listUsers = User.Find(x => (x.LicenseeID == licenseeID && x.EmailAddress == Membership.GetUser().Email)).ToList();
}
listUsers.Sort((x, y) => string.Compare(x.FirstName, y.FirstName));
-- EDIT --
I neglected to mention that I did not develop this, I merely inherited its maintenance after the original developer(s) disappeared, and my coworker who was assigned to it left the company. I'm not really really skilled at handling ASP.NET sites. Many object sources are hidden and unavailable for edit, I assume due to them being defined in a DLL somewhere. So, for any of these objects that are sourced from database tables, altering the tables will not help, since I would not be able to get the new data anyway.
However, I did try to do the following to filter out the undersirable data:
List<String> exclude = new List<String>(new String[] { "value1" , "value2" });
listUsers = User.Find(x => x.LicenseeID == licenseeID && !exclude.Contains(x.FirstName)).ToList();
Unfortunately it only resulted in an error being displayed to the page.
-- EDIT #2 --
I got the server setup to accept a new event viewer source so I could write info to the Application log to see what was happening. Looks like this installation of ASP.NET does not accept "Contains" as an action on a List object. An error gets kicked out stating that the method is not available.
I will probably add a bit to the table and flag Errant rows and then skip them when I query the table, something like
&& !ErrantData
Other way, that requires a bit more upkeep but doesn't require db change, would be to keep a text file that gets periodically updated and you read it and remove users from list based on it.
The bigger issue is unknown rows creeping in your database. Changing user credentials and adding creation timestamps may help you narrow down the search scope.

Copy values from pivot table to sheet in specific cell

I have some data that is displayed in a pivot table, from that pivot table I currently copy and paste them in another table with the corresponding current day, with so many operation numbers it takes about 1hr to complete this process daily, I'm trying to make a macro in which will help me in making this process in less time
This is the pivot table in which I copy the data
and here is where I paste it (in this table I use it to create some graphs)
Both tables consist of many different numbers of operation, as you can imagine this is very tedious.
Any help or ideas that can help me solve this is appreciated, I'm willing to try some other alternatives like fomulas, vba (preferred), even closedxml for c#.
I dont know if im explaining my self?
This is my logic.
Look for operation number
Copy Sum of "Sx"
Look in other sheet for the same operation number.
Paste value in corresponding day.
Look for same operation number with next "Sx"
.
.
End
Right now I'm just focusing on Operation1, since in Operation2 I use another sheet in which I do the same thing. (will probably use the same code as Operation1).
To clarify I'm not asking for someone to just give me the solution (although it is appreciated ), but any guide on this is helpful.
You do not need VBA to do this. Use GETPIVOTDATA.
Consider below replicated data:
In above sample, I copied some of your data in your screen shot, made a pivot out of it, put it in the same sheet and also put the table you need to populate below it. Then I use GETPIVOTDATA. I feel I need to show you how it is done even though it is well explained in the link I posted.
So we use this formula:
=GETPIVOTDATA("Sum of "&$I18,$I$2,"Date",J$17,"Opt",$I$16,"Id",$J$16)
in Cell J14. Take note that instead of using Day 1, Day2.. etc on your destination table, we used the actual date. Why? Because we need that in our GETPIVOTDATA Formula. Then copy the formula to the rest of the cells.
Result:
Now, GETPIVOTDATA errors out if it does not find anything which matches the criteria you supplied, so you might want to incorporate error handling using IFERROR statement.
Final Formula:
=IFERROR(GETPIVOTDATA("Sum of "&$I18,$I$2,"Date",J$17,"Opt",$I$16,"Id",$J$16),0)
Although you prefer VBA, I don't think a better approach than the built-in Excel functionality is more suitable in your case.

Finding all the documents in a Lotus Notes database

I'm working on a c# application that extracts metadata from all the documents in a Lotus Notes database (.nsf file, no Domino server) thusly:
NotesDocumentCollection documents = _notesDatabase.AllDocuments;
if (documents.Count>0)
{
NotesDocument document = documents.GetFirstDocument();
while (document!=null)
{
//processing
}
}
This works well, except we also need to record all the views that a document appears in. We iterate over all the views like this:
foreach (var viewName in _notesDatabase.Views)
{
NotesView view = _notesDatabase.GetView(viewName);
if (view != null)
{
if (view.AllEntries.Count > 0)
{
folderCount = view.AllEntries.Count;
NotesDocument document = view.GetFirstDocument();
while (document!=null)
{
//record the document/view cross reference
document = view.GetNextDocument(document);
}
}
Marshal.ReleaseComObject(view);
view = null;
}
}
Here are my problems and questions:
We fairly regularly encounter documents in a view that were not found in NotesDatabase.AllDocuments collection. How is that possible? Is there a better way to get all the documents in a notes database?
Is there a way to find out all the views a document is in without looping through all the views and documents? This part of the process can be very slow, especially on large nsf files (35 GB!). I'd love to find a way to get just a list of view name and Document.UniversalID.
If there is not a more efficient way to find all the document + view information, is it possible to do this in parallel, with a separate thread/worker/whatever processing each view?
Thanks!
Answering questions in the same order:
I'm not sure how this is possible either unless perhaps there's a special type of document that doesn't get returned by that AllDocuments property. Maybe replication conflicts are excluded?
Unfortunately there's no better way. Views are really just a saved query into the database that return a list of matching documents. There's no list of views directly associated with a document.
You may be able to do this in parallel by processing each view on its own thread, but the bottleneck may be the Domino server that needs to refresh the views and thus it might not gain much.
One other note, the "AllEntries" in a view is different than all the documents in the view. Entries can include things like the category row, which is just a grouping and isn't backed by an actual document. In other words, the count of AllEntries might be more than the count of all documents.
Well, first of all, it's possible that documents are being created while your job runs. It takes time to cycle through AllDocuments, and then it takes time to cycle through all the views. Unless you are working on a copy or replica of the database that is is isolated from all other possible users, then you can easily run into a case where a document was created after you loaded AllDocuments but before you accessed one of the views.
Also, is it may be possible that some of the objects returned by the view.getXXXDocument() methods are deleted documents. You should probably be checking document.isValid() to avoid trying to process them.
I'm going to suggest using the NotesNoteCollection as a check on AllDocuments. If AllDocuments were returning the full set of documents, or if NotesNoteCollection does (after selecting documents and building the collection), then there is a way to do this that is going to be faster than iterating each view.
(1) Read all the selection formulas from the views, removing the word 'SELECT' and saving them in a list of pairs of {view name, formula}.
(2) Iterate through the documents (from the NotesNoteCollection or AllDocuments) and for each doc you can use foreach to iterate through the list of view/formula pairs. Use the NotesSession.Evaluate method on each formula, passing the current document in for the context. A return of True from any evaluated formula tells you the document is in the view corresponding to the formula.
It's still brute force, but it's got to be faster than iterating all views.

In Umbraco 6, what is the best way to save and publish a list of nodes with ContentService?

I'm writing a simple function that updates/creates nodes from an XML data-source (about 400 nodes) and I'm wondering what the best way to save and publish all the nodes is. I've noticed that you can Save a list of nodes but there's no SaveAndPublish equivalent.
Should I just iterate over the list and call SaveAndPublish for each node or is there a better way? If there is an alternative, is there any difference in terms of performance?
Any answers would be greatly appreciated!
You are correct there is no Publish or SaveAndPublish option that takes in an IEnumerable like the Save method. It could be handy as it could save some lines of code.
The most valid option currently to achieve what you want is to do the following.
var cs = ApplicationContext.Current.Services.ContentService;
foreach(var content in yourListOfContentItems)
{
cs.SaveAndPublish(content);
}
Saving your list before publishing by calling Save method isn't really going to make any differences to you as if Umbraco detects there is new content in your list it processes each individually. And from what I can tell doing that and then calling Publish after is not going to save you any cycles either because the Publish method calls the same SaveAndPublishDo method that SaveAndPublish calls. So might as well go straight for the end result.

Categories