我正在使用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列添加一个唯一的约束,但是上面的代码只会抛出异常。
在对象模型方面,我希望能够通过编辑Item
或Tag
的集合来简单地添加和删除String
中的代码。但是,当我的项目持久存储到数据库时,它应该确定需要创建哪些新标记以及何时应该映射到Tags表中的现有条目。
我还考虑将此更改为基于值的一对多关系,其中Item.Tags
只是IList<String>
并且将映射到单个Tags表,其中包含每个单独标记的条目每Item
。这也可以,但我认为我更喜欢第一个,因为这样可以更有效地查找具有特定标签的项目。我对关系数据库也很陌生。所以我甚至不确定这是否应该引起关注。
答案 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));
}
}
基本上,聚合根(项)负责执行标记必须唯一的规则。