Using LINQ for Optimal Performance

LINQ stands for a Language-Integrated Query and it is part .NET Framework since version 3.5. It implements deferred execution which means you can chain the query statements and it will do nothing until you actually iterate over the result. You can either use LINQ as a specific language or you can use extension methods from System.Linq which extend IEnumerable<T> interface and can get arguments as lambda expressions. We prefer the later approach but it is equivalent. Following example shows both variants to do the same. The result of both queries is enumeration of the globally unique IDs of the walls which have any opening.

//expression using LINQ
var ids =
    from wall in model.Instances.OfType<IIfcWall>()
    where wall.HasOpenings.Any()
    select wall.GlobalId;
//equivalent expression using chained extensions of IEnumerable and lambda expressions
var ids =
    model.Instances
    .Where<IIfcWall>(wall => wall.HasOpenings.Any())
    .Select(wall => wall.GlobalId);

You can see in the code that we called Where() function directly on IModel.Instances. IEntityCollection implementations implement overloads for most of Linq data retrieval methods like Where<T>(), Count<T>(), FirstOrDefault<T>() and OfType<T>() and it is optimized on the lowest level for fast data access. All these methods return IEnumerable<T> so you can chain it with other methods to perform further selections, aggregation, sorting and other operations. IEntityCollection functions also use deferred execution so it fits very well with Linq concepts. You should only force it to enumerate if you are going to use the result more than once. You can do that by calling one of ToList<T>(), ToArray<T>()or ToDictionary<T>() methods.

xBIM uses type of entities internally as the first level filter so you should always ask for the most specific type you can. Keep in mind that IModel.Instances contains all entities in the model which is typically hundreds of thousands of objects! So you don't want to iterate over all of them to do anything. See following good and bad example which do the same but not quite the same:

public static void SelectionWithLinq()
{
    const string ifcFilename = "SampleHouse.ifc";
    var model = IfcStore.Open(ifcFilename);
    using (var txn = model.BeginTransaction())
    {
        var requiredProducts = new IIfcProduct[0]
            .Concat(model.Instances.OfType<IIfcWallStandardCase>())
            .Concat(model.Instances.OfType<IIfcDoor>())
            .Concat(model.Instances.OfType<IIfcWindow>());

        //This will only iterate over entities you really need (9 in this case)
        foreach (var product in requiredProducts)
        {
            //Do anything you want here...
        }

        txn.Commit();
    }
}
public static void SelectionWithoutLinqIsSLOW()
{
    const string ifcFilename = "SampleHouse.ifc";
    var model = IfcStore.Open(ifcFilename);
    using (var txn = model.BeginTransaction())
    {
        //this will iterate over 47309 entities instead of just 9 you need in this case!
        foreach (var entity in model.Instances)
        {
            if (entity is IIfcWallStandardCase ||
                entity is IIfcDoor ||
                entity is IIfcWindow)
            {
                //You may want to do something here. Please DON'T!
            }
        }
        txn.Commit();
    }
}

Some of the companies using xBIM Toolkit: