使用多对多NHibernate映射防止重复的标记名称

时间:2010-10-21 00:22:35

标签: c# .net nhibernate nhibernate-mapping

我正在使用NHibernate将我的Item类持久化到SQL数据库。 Item类具有Item.Tags类型的属性ICollection<Tag>,我使用以下NHibernate配置进行映射。

<set name="Tags" table="TagLinks" cascade="all" lazy ="false">
  <key column="ItemId" />
  <many-to-many class="Tag" column="TagID" />
</set>

这里也是我的Tag类的映射,它只有一个int ID和一个字符串名称。

<class name="RCL.Tag" table="Tags" lazy="false" >
    <id name ="TagID" column="TagID" >
        <generator class="native"/>
    </id>
    <property name="Name" not-null="true"/>
</class>

这一切都很好,但它允许使用相同的名称创建重复的标签。因此,下面的代码将在Tags表中创建三个单独的“foo”条目,所有条目都具有单独的ID。

myItem1.Tags.Add(new Tag("Foo"));
myItem2.Tags.Add(new Tag("Foo"));
myItem3.Tags.Add(new Tag("Foo"));

这显然不是我希望这个工作的方式。我可以为Tags.Name列添加一个唯一的约束,但是上面的代码只会抛出异常。

在对象模型方面,我希望能够通过编辑ItemTag的集合来简单地添加和删除String中的代码。但是,当我的项目持久存储到数据库时,它应该确定需要创建哪些新标记以及何时应该映射到Tags表中的现有条目。

我还考虑将此更改为基于值的一对多关系,其中Item.Tags只是IList<String>并且将映射到单个Tags表,其中包含每个单独标记的条目每Item。这也可以,但我认为我更喜欢第一个,因为这样可以更有效地查找具有特定标签的项目。我对关系数据库也很陌生。所以我甚至不确定这是否应该引起关注。

2 个答案:

答案 0 :(得分:0)

拥有两个具有相同名称的Tag实体是没用的,因为您必须按名称搜索,否则结果将不是您所期望的。

因此,唯一可能的解决方案是您考虑过的解决方案:使Tag.Name唯一,或者完全删除实体并将标记映射为ICollection。

由于空间很便宜并且你无论如何都会索引该列,后者是一种更简单的方法,但如果你想要类似stackoverflow的标签管理(同义词等),你会失去一点灵活性

答案 1 :(得分:0)

如果您想尝试DDD,那么解决方案将如下所示:

public class Item
{
  public IList<Tag> Tags {get;set;}
  public void AddTag(String tagName)
  {
    var tagAlreadyExists = Tags.Any(a=>String.Compare(a.Name,tagName,StringComparison.OrdinalIgnoreCase)==0;
    if (tagAlreadyExists )
      return; // Or throw exception
    Tags.Add(new Tag(tagName));
  }
}

基本上,聚合根(项)负责执行标记必须唯一的规则。