我们的应用程序具有Stories和Tags的概念。一个故事可以应用许多标签,一个标签可以应用于许多故事,从而形成多对多的关系。故事和标签这两个表与第三个StoriesToTags桥接。
映射文件的相关部分如下:
以下是从故事到标签的映射:
<class name="Story" table="Stories">
...
<set fetch="subselect" name="Tags" table="StoriesToTags">
<key>
<column name="StoryId" />
</key>
<many-to-many class="Tag">
<column name="TagId" />
</many-to-many>
</set>
</class>
以及从标签到故事的反向关系:
<class name="Tag" table="Tags">
...
<set fetch="subselect" inverse="true" name="Stories" table="StoriesToTags">
<key>
<column name="TagId" />
</key>
<many-to-many class="Story">
<column name="StoryId" />
</many-to-many>
</set>
</class>
正如您所看到的,我们使用subselect获取策略来避免N+1查询问题。一切都很好,直到你尝试使用LINQ:分页结果
IQueryable<Story> stories = GetStories(...).TakePage(pageNumber, pageSize);
运行此查询后,NHibernate将删除查询中未加载的所有故事的关系(StoriesToTags中的记录)。它似乎只在特定加载标签时发生(即触发子选择)。如果我们切换到联接或选择提取策略,则不会删除关系,但这会导致执行N+1查询。
我的最佳猜测是,NHibernate认为这些标签已经成为孤儿,但我们还没有对这些集合设置任何级联。此外,据我所知,设置级联没有任何效果。
这个过程在NHibernate 2.x和NHibernate.Linq下运行得很好。直到我们转到内置Linq支持的NHibernat 3.x,我们才发现删除问题。我不确定它有什么不同,但就其价值而言,我们使用的是带有标识密钥的SQL Server。
有什么想法吗?起初我觉得我在做一些愚蠢至极的事情,但我基本上尝试了映射的每一种排列,但我们似乎无法消除这个问题。
编辑:另一条有趣的信息。如果在关闭会话之前调用session.IsDirty()
,则不会出现问题。我怀疑这是因为在刷新之间收集的更改没有持续存在,但我无法很好地破译NHibernate的来源,无法确定。
如果你在实体的映射中设置了Cascade.None(),这将停止删除除实体之外的任何其他内容。
这可能会有所帮助:http://ayende.com/blog/1890/nhibernate-cascades-the-different-between-all-all-delete-orphans-and-save-update
你能给我们一些线索吗?我从未尝试过对多对多进行特定的提取,但我认为这与某种显式级联有关。