My class has a field of type Dictionary<string, List<string>>. What's the best way to map it with NHibernate? I'd better leave it as a field, don't want to expose it.
Thanks a lot!
ulu
You can't directly map it. There are two rules to consider:
Always use interfaces for collections (eg. IList<T>, IDictionary<K,V>)
NH does not support nested collections. I've never seen an application for it before
and never heard someone requesting it.
Put your list of string into a class and use interfaces:
class StringList
{
IList<string> Strings { get; private set; }
}
class Entity
{
private IDictionary<string, StringList> stringDict;
}
You might even see some advantages of having such a class.
Mapping:
<class name="Entity">
...
<map name="stringDict" table="Entity_StringDict" access="field">
<key column="Entity_FK"/>
<index column="Key" type="System.String"/>
<composite-element class="StringList">
<bag name="Strings" table="Entity_StringDict_Strings">
<key column="Entity_StringDict_FK"/>
<element type="System.String" column="String"/>
</bag>
</composite-element>
</map>
</class>
Maps to three Tables:
Table Entity
Table Entity_StringDict
Column Entity_FK
Column Key
Table Entity_StringDict_Strings
Column Entity_StringDict_FK
Column String
Related
I'm working with an existing database that has the following structure. Changing the database schema is a last resort.
Products
Id
Name
ParentProducts
ParentId
ChildId
I don't want an entity for ParentProducts, I have the following for the children property (still need to test it, but that's the concept).
<bag name="Children" lazy="true" table="dbo.ParentProducts" cascade="save-update" inverse="true" >
<key column="[ChildId]"></key>
<many-to-many column="[ProductId]" class="Product" />
</bag>
What I'm struggling with is how do I create a Parent property? I'd like to do something like the following, but table isn't a valid attribute for many-to-one.
<many-to-one name="Parent" column="[ParentId]" table="dbo.ParentRelated" class="Policy" />
I could create a bag and only ever look at the first item, but that's more of a hack.
Any ideas?
Thanks
Creating a bag is the easiest solution. And you can give it a clean interface:
protected virtual ICollection<Product> Parents { get; set; }
public virtual Product Parent
{
get
{
return Parents.SingleOrDefault();
}
set
{
Parents.Clear();
Parents.Add(value);
}
}
With this, the rest of the code doesn't need to be aware of the DB/mapping structure.
I have a bidirectional relationship in NHibernate:
<class name="Child" table="Children">
<many-to-one name="Parent" column="ParentId" not-null="true" />
</class>
<class name="Parent">
<set name="Children" lazy="true" table="Children" cascade="all">
<key column="ParentId" not-null="true" />
<one-to-many class="Child" />
</set>
</class>
How do I save it without setting inverse="true" and setting Parent property on Child?
I do not want to do this because it does not make much sense from the POCO perspective.
Alternatively, is it possible to intercept Add called on NHibernate proxy collection (Children)?
In that case, I would just put the Parent setting logic here.
Unless you're willing to make the foreign key nullable and accept an insert followed by an update, that's the way bidirectional one-to-many works in NHibernate.
I do have a generic implementation of this pattern that you can use... it's a proof of concept; it can be useful or not, depending on how you look at it, as it kinda breaks the POCO approach, but... well, here it is:
public interface IHaveParent<T>
{
T Parent { get; set; }
}
public interface IHaveMany<T>
{
ICollection<T> Children { get; }
}
public static class OneToManyHelper
{
public static void AddChild<TParent, TChild>(this TParent parent,
TChild child)
where TChild : IHaveParent<TParent>
where TParent : IHaveMany<TChild>
{
parent.Children.Add(child);
child.Parent = parent;
}
}
With this, you can all AddChild on any parent.
The problem with intercepting Add calls is that you'd always need to instantiate your collections using a special method (which, again, is not POCO).
I've been wrecking my mind on how to get my tagging of entities to
work. I'll get right into some database structuring:
tblTag
TagId - int32 - PK
Name
tblTagEntity
TagId - PK
EntityId - PK
EntityType - string - PK
tblImage
ImageId - int32 - PK
tblBlog
BlogId - int32 - PK
class Image
Id
EntityType { get { return "MyNamespace.Entities.Image"; }
IList<Tag> Tags;
class Blog
Id
EntityType { get { return "MyNamespace.Entities.Blog"; }
IList<Tag> Tags;
The obvious problem I have here is that EntityType is an identifer but
doesn't exist in the database. If anyone could help with the this
mapping I'd be very grateful.
You don't need the entity type. Take a look at any-type mapping (it stores the type name in the database in the relation table, but you don't need it in the entity model).
See this blog post by ayende.
Edit: tried to write an example.
You could have an own table for each tagged object, this is easy and straight forward, you don't even need any types:
<class name="Tag">
<!-- ... -->
<property name="Name"/>
</class>
<class name="Image">
<!-- ... -->
<bag name="Tags" table="Image_Tags">
<key column="Image_FK"/>
<many-to-many class="Tag" column="TagId "/>
</bag>
</class>
Tried to use some advanced features to map it into a single table, but I think it doesn't work this way:
<class name="Tag">
<!-- ... -->
<property name="Name"/>
<bag name="Objects" table="tblTagEntity" access="noop">
<key column="TagId"/>
<many-to-any id-type="System.Int64" meta-type="System.String">
<meta-value
value="IMAGE"
class="Image"/>
<meta-value
value="BLOG"
class="Blog"/>
<column name="EntityType"/>
<column name="EntityId"/>
</many-to-any>
</bag>
</class>
<class name="Image">
<!-- ... -->
<bag name="Tags" table="tblTagEntity" where="EntityType='IMAGE'">
<key column="EntityId"/>
<many-to-many class="Tag" column="TagId "/>
</bag>
</class>
The tricks here are:
access="noop" to specify the foreign key without having a property in the entity model, see this post.
where="EntityType='IMAGE'" to filter the loaded data.
The problem is that most probably the EntityType is not set to any useful value. This could be fixed somewhere, but I don't think that it is worth the effort.
Someone else has probably a better idea.
Edit 2: another (working) solution
make the association table an entity:
in short:
Tag => TagEntity: not mapped or one-to-many inverse (noop)
TagEntity => Tag: many-to-one
TagEntity => Object: any
Object => TagEntity: one-to-many inverse
This should work straight forward.
classes:
class Tag
{
string Name { get; set; }
}
class TagEntity
{
Tag Tag { get; set; }
object Entity { get; set; }
}
class Image
{
IList<TagEntity> tags { get; private set; }
}
The only drawback seems to be that you have to make sure that the bidirectional associations are consistent without loading to much data. Note that inverse collections are not stored.
Edit 2: Performance notes
When you add / remove tags, you could do a trick. TagEntity has a reference to the tagged entity. The Entity also has a list of TagEntities, but this is marked as inverse. (This means, they are loaded, but not stored.)
You can add and remove tags without loading the Entity an without loading all the tags.
Adding:
Get Tag to add (or load proxy if you have the id of the tag)
Load Entity (just proxy, using session.Load, no db access here)
create new TagEntity, assign tag and entity-proxy
save TagEntity
Removing:
Get TagEntity to remove
delete TagEntity.
Within the session, you don't have this tag assigned to/removed from the TagEntity. This works fine assumed that you only add or remove tags within this transaction.
I you define a list of TagEntities on the Tag, you can do the same, without loading all the TagEntities just to add or remove one.
You could make EntityType an Enum in your code. And/or, you could try making EntityType an actual entity in your database (tblEntityType).
Got Stefans final solution to work! Here's my final mappings:
Image
<bag name="TagEntites" table="tblTagEntity" cascade="all" fetch="join" inverse="true" where="EntityType='EntityImage'">
<key column="EntityId"></key>
<one-to-many class="TagEntity" />
</bag>
TagEntity
<id name="Id">
<column name="TagEntityId"></column>
<generator class="identity" />
</id>
<any name="Entity" id-type="System.Int32" meta-type="System.String">
<meta-value value="EntityImage" class="Image" />
<column name="EntityType"></column>
<column name="EntityId"></column>
</any>
<many-to-one name="Tag" class="Tag" cascade="all" fetch="join">
<column name="TagId"></column>
</many-to-one>
I have the following class
public class Person
{
private IList<Person> _children;
public IEnumerable<Person> Children { get; }
public void AddChild(Person child)
{
// Some business logic and adding to the internal list
}
}
What changes would I have to make for NHibenrate to be able to persist the Child collection (apart from making everything virtual, I know that one).
Do I have to add a setter to the children property which does something like a _children.Clear(); _children.AddRange(value). Currently the model expresses my intent quite nicely but I'm not sure how much alteration is need for NH to be able to help me out with persistence.
NHibernate is able to map private fields. Access and naming strategies are discussed in the property section of the reference documentation.
Making your public members virtual is required for proxies to work. These will usually be runtime-generated subclasses of your entity classes.
In this example mapping the field _children will be Children in HQL and Criteria queries.
<class name="Person" table="person">
<bag name="Children" access="field.camelcase-underscore">
<key column="parentid" />
<one-to-many class="Person" />
</bag>
</class>
I'm looking to create a many to many relationship using NHibernate. I'm not sure how to map these in the XML files. I have not created the classes yet, but they will just be basic POCOs.
Tables
Person
personId
name
Competency
competencyId
title
Person_x_Competency
personId
competencyId
Would I essentially create a List in each POCO for the other class? Then map those somehow using the NHibernate configuration files?
You can put the many-to-many relation to either class, or even to both. This is up to your domain model. If you map it to both, one of them is inverse.
class Person
{
// id ...
IList<Competency> Competencies { get; private set; }
// you domain model is responsible to manage bidirectional dependencies.
// of course this is not a complete implementation
public void AddCompetency(Competency competency)
{
Competencies.Add(competency);
competency.AddPerson(this);
}
}
class Competency
{
// id ...
IList<Person> Persons { get; private set; }
}
Mapping:
<class name="Person">
<id ....>
<bag name="Competencies" table="Person_x_Competency">
<key column="personId"/>
<many-to-many class="Competency" column="competencyId"/>
</bag>
</class>
<class name="Competency">
<id ....>
<bag name="Persons" table="Person_x_Competency" inverse="true">
<key column="competencyId"/>
<many-to-many class="Person" column="personId"/>
</bag>
</class>
Only make it bidirectional if you really need it.
By the way: it is much better to write the classes first and create the database design afterwards. The database can be exported from the mapping files. This is very useful.