Tối ưu hóa truy vấn trong Revit API
- Tại sao FilteredElementCollector lại quan trọng?
- Các loại bộ lọc (ElementFilter) và độ hiệu quả
- Chiến lược tối ưu hóa truy vấn với FilteredElementCollector
ElementCategoryFilter
: Lọc cácElement
theoBuiltInCategory
hoặcCategory
. Đây là bộ lọc được sử dụng thường xuyên nhất và cực kỳ hiệu quả.// Ví dụ: Lấy tất cả các bức tường FilteredElementCollector collector = new FilteredElementCollector(doc); ICollection<Element> walls = collector.OfCategory(BuiltInCategory.OST_Walls).ToElements();
ElementClassFilter
: Lọc cácElement
theo kiểu C# của chúng (ví dụ:Wall
,Door
,FamilyInstance
,Level
).// Ví dụ: Lấy tất cả các đối tượng tường (Revit.DB.Wall) FilteredElementCollector collector = new FilteredElementCollector(doc); ICollection<Element> walls = collector.OfClass(typeof(Wall)).ToElements(); // Hoặc kết hợp cả Class và Category (rất mạnh) collector = new FilteredElementCollector(doc); ICollection<Element> doors = collector.OfClass(typeof(FamilyInstance)) .OfCategory(BuiltInCategory.OST_Doors) .ToElements();
ElementIsElementTypeFilter
: Lọc để chỉ lấy các loại đối tượng (Type Elements - ví dụ:WallType
,DoorType
).// Ví dụ: Lấy tất cả các loại tường FilteredElementCollector collector = new FilteredElementCollector(doc); ICollection<Element> wallTypes = collector.WhereElementIsElementType().ToElements();
ElementIsViewIndependentFilter
: Lọc cácElement
độc lập với View (như Levels, Grids).BoundingBoxIntersectsFilter
,BoundingBoxContainsFilter
,BoundingBoxIsInsideFilter
: Các bộ lọc dựa trên hình học vùng giới hạn (Bounding Box) để kiểm tra sự giao cắt, chứa đựng hoặc nằm trong một vùng nhất định. Các bộ lọc này thường được kết hợp với mộtOutline
hoặcBoundingBoxXYZ
.LogicalAndFilter
/LogicalOrFilter
: Kết hợp nhiều bộ lọc. Khi kết hợp Quick Filters, kết quả vẫn là một Quick Filter hiệu quả.ParameterFilter
: Lọc cácElement
dựa trên giá trị của các tham số (Parameters). Đây là một bộ lọc rất mạnh nhưng thường là Slow Filter nếu không được tối ưu. Tuy nhiên, một số loạiParameterFilter
có thể được tối ưu hóa nếu sử dụng đúngStorageType
vàBuiltInParameter
.- Mẹo tối ưu: Nếu bạn cần lọc theo giá trị tham số, hãy cố gắng kết hợp nó với một hoặc nhiều Quick Filters trước.
// Ví dụ kém hiệu quả (nếu không có Quick Filter trước): // Lấy tất cả các Element có tham số "Comments" chứa "Important" ParameterValueProvider provider = new ParameterValueProvider(new ElementId(BuiltInParameter.ALL_MODEL_INSTANCE_COMMENTS)); FilterStringRuleEvaluator evaluator = new FilterStringContains(); FilterRule rule = new FilterStringRule(provider, evaluator, "Important", false); // false = case-insensitive ElementFilter filter = new ElementFilter(rule); FilteredElementCollector collector = new FilteredElementCollector(doc); ICollection<Element> importantElements = collector.WherePasses(filter).ToElements(); // Có thể chậm
- Ví dụ hiệu quả hơn (kết hợp Quick Filter):
// Lấy tất cả các cửa có tham số "Comments" chứa "Important" FilteredElementCollector collector = new FilteredElementCollector(doc); ICollection<Element> importantDoors = collector.OfCategory(BuiltInCategory.OST_Doors) // Quick Filter trước .WherePasses(filter) // Sau đó mới đến Slow Filter .ToElements();
ElementLevelFilter
: Lọc cácElement
nằm trên mộtLevel
cụ thể. Thường được sử dụng cùng với các Quick Filter khác.FamilyInstanceFilter
: Lọc cácFamilyInstance
của mộtFamilySymbol
cụ thể.StructuralInstanceUsageFilter
,StructuralWallUsageFilter
, v.v.Sử dụng
WhereElementIsNotElementType()
nếu bạn chỉ muốn các thể hiện (instance) vàWhereElementIsElementType()
nếu bạn chỉ muốn các loại (type).Nếu bạn chỉ cần kiểm tra sự tồn tại của một
Element
chứ không cần toàn bộ danh sách, hãy sử dụngAny()
,FirstElement()
, hoặcFirstOrDefault()
thay vìToElements()
.// Chỉ kiểm tra xem có bất kỳ bức tường nào không bool hasWalls = new FilteredElementCollector(doc).OfClass(typeof(Wall)).Any(); // Lấy bức tường đầu tiên tìm thấy Wall firstWall = new FilteredElementCollector(doc).OfClass(typeof(Wall)).Cast<Wall>().FirstOrDefault();
Khi làm việc với Revit API, một trong những tác vụ phổ biến và quan trọng nhất là truy xuất các phần tử (Elements) từ mô hình. Việc này thường được thực hiện thông qua lớp FilteredElementCollector
. Tuy nhiên, nếu không được sử dụng đúng cách, việc truy vấn dữ liệu có thể trở nên chậm chạp, đặc biệt với các mô hình lớn, ảnh hưởng nghiêm trọng đến hiệu suất của Add-in. Bài viết này sẽ đi sâu vào cách tối ưu hóa các truy vấn bằng FilteredElementCollector
để đảm bảo Add-in của bạn hoạt động nhanh chóng và hiệu quả.
FilteredElementCollector
lại quan trọng?
Tại sao FilteredElementCollector
là công cụ chính để tìm kiếm và lọc các Element
trong một đối tượng Document
của Revit. Nó cung cấp một loạt các phương thức lọc mạnh mẽ, cho phép bạn chỉ định chính xác loại Element
hoặc các thuộc tính mà bạn muốn truy xuất.
Tuy nhiên, việc không hiểu rõ cơ chế hoạt động của các bộ lọc có thể dẫn đến các truy vấn không hiệu quả, buộc Revit phải duyệt qua hàng ngàn Element
không cần thiết, gây lãng phí tài nguyên và thời gian.
ElementFilter
) và độ hiệu quả
Các loại bộ lọc (Revit API cung cấp nhiều loại ElementFilter
khác nhau, mỗi loại có một cơ chế hoạt động riêng và mức độ hiệu quả khác nhau. Hiểu rõ sự khác biệt này là chìa khóa để tối ưu hóa.
Các bộ lọc được chia thành hai nhóm chính: Quick Filters (Bộ lọc nhanh) và Slow Filters (Bộ lọc chậm).
Quick Filters (Bộ lọc nhanh)
Đây là những bộ lọc hoạt động hiệu quả nhất vì chúng sử dụng dữ liệu đã được lập chỉ mục (indexed data) trong cơ sở dữ liệu của Revit. Khi bạn sử dụng Quick Filter, Revit không cần phải duyệt qua từng Element
một để kiểm tra điều kiện.
Các Quick Filters phổ biến:
Nguyên tắc vàng: Luôn cố gắng sử dụng Quick Filters trước để giảm thiểu số lượng Element
mà Revit phải kiểm tra.
Slow Filters (Bộ lọc chậm)
Các bộ lọc này đòi hỏi Revit phải kiểm tra từng Element
một để xác định xem nó có đáp ứng điều kiện lọc hay không. Điều này làm cho chúng kém hiệu quả hơn đáng kể so với Quick Filters, đặc biệt trong các mô hình lớn.
Các Slow Filters phổ biến:
FilteredElementCollector
Chiến lược tối ưu hóa truy vấn với Để đảm bảo các truy vấn của bạn nhanh và hiệu quả, hãy áp dụng các chiến lược sau:
Ưu tiên Quick Filters
Luôn bắt đầu truy vấn của bạn với một hoặc nhiều Quick Filters để giảm thiểu đáng kể số lượng Element
mà các bộ lọc tiếp theo phải xử lý. Revit sẽ đánh giá các bộ lọc theo thứ tự bạn thêm vào FilteredElementCollector
.
// BAD (có thể duyệt qua tất cả các phần tử rồi mới lọc theo loại)
// FilteredElementCollector collector = new FilteredElementCollector(doc)
// .WhereElementIsNotElementType() // Slow Filter
// .OfClass(typeof(Wall)); // Quick Filter
// GOOD (lọc theo Class trước, hiệu quả hơn)
FilteredElementCollector collector = new FilteredElementCollector(doc)
.OfClass(typeof(Wall)) // Quick Filter trước
.WhereElementIsNotElementType(); // Sau đó mới là Slow Filter (nếu cần)
LogicalAndFilter
hoặc chuỗi phương thức
Kết hợp các bộ lọc bằng Khi cần áp dụng nhiều điều kiện, bạn có thể xâu chuỗi các phương thức lọc (OfCategory().OfClass().WherePasses()
) hoặc sử dụng LogicalAndFilter
. Revit thường tự động tối ưu hóa khi bạn xâu chuỗi các Quick Filters.
// Lấy tất cả các thể hiện của cửa (FamilyInstance)
FilteredElementCollector collector = new FilteredElementCollector(doc);
ICollection<Element> doorInstances = collector.OfClass(typeof(FamilyInstance)) // Quick Filter (theo Class)
.OfCategory(BuiltInCategory.OST_Doors) // Quick Filter (theo Category)
.ToElements();
.ToElements()
quá sớm
Tránh sử dụng .ToElements()
sẽ thực thi truy vấn và tải tất cả các Element
phù hợp vào bộ nhớ. Nếu bạn có nhiều bộ lọc hoặc thao tác tiếp theo, hãy cố gắng áp dụng tất cả các bộ lọc trước khi gọi .ToElements()
.
Chỉ lấy những gì bạn cần
Cache các kết quả truy vấn nếu có thể
Nếu bạn cần truy xuất cùng một tập hợp Element
nhiều lần trong cùng một phiên chạy Add-in, hãy cân nhắc lưu trữ kết quả vào một biến List<Element>
hoặc Dictionary<ElementId, Element>
thay vì lặp lại truy vấn mỗi lần. Điều này sẽ tránh được việc Revit phải thực hiện lại quá trình lọc tốn kém.
Bạn đã từng gặp phải vấn đề về hiệu suất khi truy vấn dữ liệu trong Revit API chưa? Hãy chia sẻ kinh nghiệm của bạn ở phần bình luận dưới đây nhé!