开发者

Preventing duplicate tag names with many-to-many NHibernate mapping

I'm using NHibernate to persist my Item class to a SQL database. The Item class has a property Item.Tags of type ICollection<Tag> which I am mapping using the following NHibernate configation.

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

Also here's my mapping for my Tag class, which simply has an int ID and a string name.

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

This all works fine, yet it allows duplicate tags to be created with the same name. Thus the code below will create three separate "foo" entries in the Tags table all with separate IDs.

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

This is obviously not how I want this to work. I could add a unique constraint to the Tags.Name column, but then the code above would only thrown an exception.

On the object model side I want to be able to simply add and remove tags from an Item by editing a collection of Tag or String. However, when my items are persisted to the database it should figure out what new tags need to be created and when it should be mapping to existing entries in the Tags table.

I've also considered changing this to a value based one-to-many relationship where Item.Tags would simply be a IList<String> and would map to a single Tags table with entries for every individual tag on every Item. This would work as well, but I think I would prefer the first, as it would make finding items with a specific tag more efficient. I'm pretty new to relational databases as well. So I'm not even sure if this 开发者_Python百科should be a concern.


Having two Tag entities with the same name is useless, as you'd have to search by name or the results wouldn't be what you expect.

So, the only possible solutions are the ones you've considered: making Tag.Name unique, or removing the entity completely and mapping tags as an ICollection.

Since space is cheap and you'll be indexing that column anyway, the latter is a simpler approach, although you lose a little flexibility if you want stackoverflow-like tags management (synonyms, etc)


If you want to try DDD, then the solution would look something like this:

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));
  }
}

Basically the Aggregate Root (Item) is responsible for enforcing the rule that Tags must be unique.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜