Let's consider the following structure of entities:
On Product we have this mapping:
<bag name="ProductDetailCells" inverse="true" cascade="none" lazy="false">
<key column="ProductId" />
<one-to-many class="ProductDetailCell" />
</bag>
which means that I am always loading at least some of the cells.
Here is a quick test:
[Test]
public void normal_load_does_2_selects()
{
Product p = Repository<Product>.LoadById(726);
}
and the result in SQL Server Profiler:
It runs one select to get the Product and one select to get the ProductDetailCells for the Product. For 300 products we will obtain 301 database queries. If the ProductDetailCell table contains many rows, this will be a performance disaster as soon as we try to load a list of Products.
In my case I always needed to load a cell based on the type of column (ProductDetailColumnId), and I also knew that there is only one ProductDetailCell per ProductColumnType for each Product. So I tried to find a solution to avoid the performance hit.
Here is another test:
[Test]
public void optimized_load_does_1_select()
{
Product p = new ProductRepository().InitialiseCriteriaFor(726)
.WithProductDetailCellsOfType(enumProductDetailColumn.SubProductColumn)
.LoadProduct();
}
I am using a fluent repository, like the one presented here.
Here is the WithProductDetailCellsOfType method:
public ProductRepository WithProductDetailCellsOfType(enumProductDetailColumn type)
{
_criteria
.CreateCriteria("ProductDetailCells", JoinType.LeftOuterJoin)
.Add(Expression.Eq("Column.Id", (int)type));
return this;
}
and here is the result in SQL Server Profiler:
There is only one select, because this time it uses a left join with ProductDetailCell:
FROM dbo.Product this_ left outer join dbo.ProductDetailCell productdet1_ on this_.ProductId=productdet1_.ProductId
What I did is I used the knowledge that I always need ProductDetailCells based on the ProductDetailColumnType, and that there will be only one result each time. So I forced a left join instead of a separate select. This would not have worked if I had two ProductDetailCells per ProductDetailColumnType for each Product, because it would have doubled the returned rows. But in this case, it worked and it made a big difference.
This is only a punctual case, but the general idea is more important: always watch what your ORM is doing, at least until you learn it very well. Most of the times you can do the same thing in at least 2 different ways, and the ORM can't always make the best decisions on its own. It will get you what you want one way or the other, but sometimes you must say how.