I have the following database schema:
http://lh4.ggpht.com/_SDci0Pf3tzU/SdM3XnAmmxI/AAAAAAAAEps/Ie3xW3ZVNfQ/s400/styleerror.png
And this is my mapping file:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" namespace="CodeSmithSampel.Generated.BusinessObjects" assembly="CodeSmithSampel">
<class name="CodeSmithSampel.Generated.BusinessObjects.Store, CodeSmithSampel" table="store" lazy="true">
<id name="Id" column="Id">
<generator class="native" />
</id>
<property name="Name" column="Name" />
<bag name="Employees" lazy="true" cascade="all-delete-orphan" inverse="true" >
<key column="Store_id"></key>
<one-to-many class="Employee"></one-to-many>
</bag>
<bag name="Products" table="storeproduct" lazy="true" cascade="all" inverse="true" >
<key column="Store_id"></key>
<many-to-many column="Product_id" class="Product" />
</bag>
</class>
</hibernate-mapping>
And ths is my Store entity class:
public partial class Store : BusinessBase<int>
{
#region Declarations
private string _name = String.Empty;
private IList<Employee> _employees = new List<Employee>();
private IList<Product> _products = new List<Product>();
#endregion
#region Constructors
public Store() { }
#endregion
#region Methods
public override int GetHashCode()
{
System.Text.StringBuilder sb = new System.Text.StringBuilder();
sb.Append(this.GetType().FullName);
sb.Append(_name);
return sb.ToString().GetHashCode();
}
#endregion
#region Properties
public virtual string Name
{
get { return _name; }
set
{
OnNameChanging();
_name = value;
OnNameChanged();
}
}
partial void OnNameChanging();
partial void OnNameChanged();
public virtual IList<Employee> Employees
{
get { return _employees; }
set
{
OnEmployeesChanging();
_employees = value;
OnEmployeesChanged();
}
}
partial void OnEmployeesChanging();
partial void OnEmployeesChanged();
public virtual IList<Product> Products
{
get { return _products; }
set
{
OnProductsChanging();
_products = value;
OnProductsChanged();
}
}
partial void OnProductsChanging();
partial void OnProductsChanged();
#endregion
}
The product class:
public partial class Product : BusinessBase<int>
{
#region Declarations
private float _price = default(Single);
private string _name = null;
private IList<Store> _stores = new List<Store>();
#endregion
#region Constructors
public Product() { }
#endregion
#region Methods
public override int GetHashCode()
{
System.Text.StringBuilder sb = new System.Text.StringBuilder();
sb.Append(this.GetType().FullName);
sb.Append(_price);
sb.Append(_name);
return sb.ToString().GetHashCode();
}
#endregion
#region Properties
public virtual float Price
{
get { return _price; }
set
{
OnPriceChanging();
_price = value;
OnPriceChanged();
}
}
partial void OnPriceChanging();
partial void OnPriceChanged();
public virtual string Name
{
get { return _name; }
set
{
OnNameChanging();
_name = value;
OnNameChanged();
}
}
partial void OnNameChanging();
partial void OnNameChanged();
public virtual IList<Store> Stores
{
get { return _stores; }
set
{
OnStoresChanging();
_stores = value;
OnStoresChanged();
}
}
partial void OnStoresChanging();
partial void OnStoresChanged();
#endregion
}
The mapping for the Product class:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" namespace="CodeSmithSampel.Generated.BusinessObjects" assembly="CodeSmithSampel">
<class name="CodeSmithSampel.Generated.BusinessObjects.Product, CodeSmithSampel" table="product" lazy="true">
<id name="Id" column="Id">
<generator class="native" />
</id>
<property name="Price" column="Price" />
<property name="Name" column="Name" />
<bag name="Stores" table="storeproduct" lazy="true" cascade="all" inverse="true" >
<key column="Product_id"></key>
<many-to-many column="Store_id" class="Store" />
</bag>
</class>
</hibernate-mapping>
What is particularly weird is that when I add a Store object to one of the product, the database record is not updated; the add doesn't seem to take place, although the new store object exists in the database:
IManagerFactory managerFactory = new ManagerFactory();
var productManager = managerFactory.GetProductManager();
var myProduct= productManager.GetById(2);
var myStore = new Store();
myStore.Name = "new Store"; //a "new store" entry is created in the Store table
myProduct.Stores.Add(myStore); // but this "new store" is not linked to the myproduct, as it should.
productManager.Session.CommitChanges();
Is there anything I miss?
Note: I generate the above code using CodeSmith.
Edit: The accepted answer works. The reason I got in this problem is because
Only one entity class should have inverse = true, not two. So either Product or Store should set the inverse to false. The code generation tool didn't handle this properly.
The correct way to Add Many to Many relationship is explained below. You must add two times.
Can this have anything to do with the fact that you have a surrogate key in the storeproducts table ?
What happens if you remove this surrogate key column Id, and put the primary key on the combination of the product_id and store_id columns ?
I believe that, if you want to have a surrogate key on the storeproducts table, you'll have to create yet another entity.
If you want to use the surrogate key, you'll have to use the idbag mapping.
How does your Product class and mapping look like ?
I see that you specify the 'inverse' attribute in your mapping of the Products collection in the Store entity.
If you do this (and thus you have a bi-directional association), then you should add the Store to the Stores collection of the product as well.
Since -from the NH documentation- :
Changes made only to the inverse end
of the association are not persisted.
This means that NHibernate has two
representations in memory for every
bidirectional association, one link
from A to B and another link from B to
A. This is easier to understand if you
think about the .NET object model and
how we create a many-to-many
relationship in C#:
category.Items.Add(item); // The category now "knows" about the relationship
item.Categories.Add(category); // The item now "knows" about the relationship
session.Update(item); // No effect, nothing will be saved!
session.Update(category); // The relationship will be saved
The non-inverse side is used to save
the in-memory representation to the
database. We would get an unneccessary
INSERT/UPDATE and probably even a
foreign key violation if both would
trigger changes! The same is of course
also true for bidirectional
one-to-many associations.
You may map a bidirectional
one-to-many association by mapping a
one-to-many association to the same
table column(s) as a many-to-one
association and declaring the
many-valued end inverse="true".
This means, that only one of the ends should be inverse.
Adding a Product to a store, should be done like this:
public class Store
{
public void AddProduct( Product p )
{
if( _products.Contains (p) == false )
{
_products.Add (p);
p.AddStore(this);
}
}
}
public class Product
{
public void AddStore( Store s )
{
if( _stores.Contains (s) == false )
{
_stores.Add (s);
s.AddProduct(this);
}
}
}
(Very important to check whether the collection already contains the item to be added; otherwise you'll end up in an infinite loop.
Related
FluentNHibernate: 1.3.0.733
NHibernate: 3.3.1.4000
I'm trying to set the column name of the Id column, but it seems to be ignored.
Edit:
Found solution. Property redeclaration (new-modifier) was the problem (see answer).
I'm using AutoMapping with conventions and overrides.
Override:
public class OrderHeadMapping : IAutoMappingOverride<OrderHead>
{
public void Override(AutoMapping<OrderHead> mapping)
{
mapping.Schema("[database].[dbo]");
mapping.Table("OrderHeads");
mapping.Id(x => x.Id, "desiredColumnName")
.Column("desiredColumnName")
.GeneratedBy.UuidString();
...
}
}
This code gets executed, but the column name stays "Id".
I've already exported the mappings to an directory to see what's the outcome:
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
<class xmlns="urn:nhibernate-mapping-2.2" schema="[database].[dbo]" name="OrderHead, Core, Version=1.0.4666.19686, Culture=neutral, PublicKeyToken=null" table="OrderHeads">
<cache usage="read-write" />
<id name="Id" type="System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<column name="Id" />
<generator class="assigned" />
</id>
...
</class>
</hibernate-mapping>
I've searched my whole solution for ".Id(" and ".Column(" to ensure it is not accidently reset but none of the results deal with setting/overwriting the id column name. So now I'm a little bit lost.
I finally found the problem:
One thing I left out in my description is that I have two base entities:
public abstract class Entity
{
protected object _id;
public virtual object Id
{
get { return _id; }
protected internal set { _id = value; }
}
}
public abstract class Entity<TKey> : Entity
{
public Entity()
{
_id = default(TKey);
}
new public virtual TKey Id
{
get { return (TKey)_id; }
protected internal set { _id = (TKey)value; }
}
...
}
The problem was that FluentNHibernate handles redefined properties twice: Entity.Id and Entity<>.Id ultimately overwriting the desired mapping with the mapping of the base class version.
So I have to walk the inheritance tree up to check if this member is the top most rewrite (if any).
Now I handle the problem in the ShouldMap method in my implementation of DefaultAutomappingConfiguration:
public override bool ShouldMap(Member member)
{
var res = base.ShouldMap(member);
if (res == true)
{
var originalDeclaringType = GetOriginalDeclaringType(member.MemberInfo);
...
if(member.Name == "Id")
{
if (GetTopMostRedefinedMember(member.MemberInfo) != member.MemberInfo.DeclaringType)
return false;
}
}
...
return res;
}
with GetTopMostRedefinedMember being:
private Type GetTopMostRedefinedMember(MemberInfo member)
{
List<Type> types = new List<Type>();
Type type = member.ReflectedType;
while (type != null)
{
types.Add(type);
type = type.BaseType;
}
foreach (var t in types)
{
var tmp = t.GetMember(member.Name, BindingFlags.Public |
BindingFlags.NonPublic |
BindingFlags.Instance |
BindingFlags.DeclaredOnly);
if (tmp.Length != 0)
{
type = t;
break;
}
}
return type;
}
Disclaimer: This code is not thoroughly tested.
I hope someday someone will be safed from debugging hours!
Lg
warappa
In my project all of entities have composite primary keys [systemId as long, deviceId as long, id as long]. Values filled manualy before I save the entity.
I'm using "code first" approach and to provide simplicity references NHibernate.Mapping.Attributes extension to define schema with attributes just like in Java based Hibernate.
All entities have an abstract base type which provides shared properties and functionality:
[Serializable]
public abstract class EntityBase
{
[CompositeId(0, Name = "id", ClassType = typeof(EntityId))]
[KeyProperty(1, Name = "systemId", Column = "restId")]
[KeyProperty(2, Name = "deviceId", Column = "deviceId")]
[KeyProperty(3, Name = "id", Column = "id")]
private EntityId id = new EntityId(); // this is a component, see below
[Property(Column = "isDeleted", NotNull = true)]
private bool deleted = false;
public EntityId Id
{
get { return id; }
set { id = value; }
}
public bool Deleted
{
get { return deleted; }
set { deleted = value; }
}
}
Behind the composite id there is a component, which represent the complex primary key:
[Serializable]
[Component]
public class EntityId
{
[Property(Column = "restId", NotNull = true)]
private long systemId = 0;
[Property(NotNull = true)]
private long deviceId = 0;
[Property(NotNull = true)]
private long id = 0;
public long SystemId
{
get { return systemId; }
set { systemId = value; }
}
public long DeviceId
{
get { return deviceId; }
set { deviceId = value; }
}
public long Id
{
get { return id; }
set { id = value; }
}
}
I definied two entities called OTMList and OTMItem which have bi-directional OneToMany and ManyToOne associations to each other.
[Serializable]
[Class]
public class OTMList : EntityBase
{
[List(0, Cascade = "none", Generic = true, Lazy = CollectionLazy.True)]
[Key(1, Column = "id")]
[Index(2, Column = "id")]
[OneToMany(3, NotFound = NotFoundMode.Exception, ClassType = typeof(OTMItem))]
private IList<OTMItem> otmItems = new List<OTMItem>();
public IList<OTMItem> OTMItems
{
get { return otmItems; }
set { otmItems = value; }
}
}
[Serializable]
[Class]
public class OTMItem : EntityBase
{
[ManyToOne(0, Name = "otmList", ClassType = typeof(OTMList), Column = "OTMListId", Cascade = "none", Lazy = Laziness.Proxy)]
private OTMList otmList = null;
public OTMList OTMList
{
get { return otmList; }
set { otmList = value; }
}
}
The hibernate mapping xml file contains the following information:
<?xml version="1.0" encoding="utf-8"?>
<hibernate-mapping default-access="field" auto-import="true" assembly="NHibernateTest, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" xmlns="urn:nhibernate-mapping-2.2">
<class name="NHibernateTest.ListTest.OTM.OTMList, NHibernateTest">
<composite-id class="NHibernateTest.Domain.EntityId, NHibernateTest" name="id">
<key-property name="systemId" column="restId" />
<key-property name="deviceId" column="deviceId" />
<key-property name="id" column="id" />
</composite-id>
<property name="deleted" column="isDeleted" not-null="true" />
<list name="otmItems" lazy="true" cascade="none" generic="true">
<key column="id" />
<index column="id" />
<one-to-many class="NHibernateTest.ListTest.OTM.OTMItem, NHibernateTest" not-found="exception" />
</list>
</class>
<class name="NHibernateTest.ListTest.OTM.OTMItem, NHibernateTest">
<composite-id class="NHibernateTest.Domain.EntityId, NHibernateTest" name="id">
<key-property name="systemId" column="restId" />
<key-property name="deviceId" column="deviceId" />
<key-property name="id" column="id" />
</composite-id>
<property name="deleted" column="isDeleted" not-null="true" />
<many-to-one name="otmList" class="NHibernateTest.ListTest.OTM.OTMList, NHibernateTest" column="OTMListId" cascade="none" lazy="proxy" />
</class>
</hibernate-mapping>
When I validate the schema with SchemaValidator of the NHibernate I get the following exception:
Foreign key (FKF208BF0B9A2FCB3:OTMItem [OTMListId])) must have same number of columns as the referenced primary key (OTMList [restId, deviceId, id])
The problem are the same too when I try to create uni-directional ManyToOne or bi/uni-directional ManyToMany associations.
Somebody can help me please?
The full source code available here:
NHibernateTest source code
The problem is solved, check this out:
http://jzo001.wordpress.com/category/nhibernate/
I have an entity where a composite id is used. I changed to code to make use of wrapping the composite id in a seperate key class. I expected that with Linq I could do a comparison on key object and with the Criteria API to use Restrictions.IdEq but both fail. I need to explicitly compare the key values to make it work.
I cannot find any documentation if this should work so for the moment I am stuck with direct comparisons but this means that when I alter the key that I also need to update the query code which is obviously not what I would want.
As a side note, I tried this with NHibernate 3.0.0 Alpha 2 and 3.
Domain
Mapping
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="Cwc.Pulse.Dal"
namespace="Cwc.Pulse.Dal">
<class name="AddonStatus">
<composite-id name="Id">
<key-many-to-one name="Context" column="Context_Id" class="Context" />
<key-property name="AddonType" column="Addon_Id"/>
</composite-id>
<property name="Status" />
</class>
</hibernate-mapping>
Class
public class AddonStatus
{
public virtual string Status { get; set; }
public virtual Key Id { get; protected set; }
public AddonStatus()
{
Id = new Key();
}
public class Key
{
public virtual Context Context { get; set; }
public virtual AddonType AddonType { get; set; }
public override int GetHashCode()
{
return ContextId.GetHashCode() ^ AddonType.GetHashCode();
}
public override bool Equals(object obj)
{
if (this == obj) return true;
var o = obj as Key;
if (null == o) return false;
return Context == o.Context && AddonType == o.AddonType;
}
}
}
Working queries
The queries below work and as you can see I compare the key values explicitly. I do not compare the key object.
Linq
from status
in session.Query<AddonStatus>()
where status.Id.Context == context && status.Id.AddonType == addonType
select status
Criteria API
session.CreateCriteria<AddonStatus>()
.Add(Restrictions.Eq("Id.Context", context))
.Add(Restrictions.Eq("Id.AddonType", addonType))
Expected to work but dont
I expect the following queries to work. Either in efficiently for linq in memory instead of the database but I expect the criteria api to be smart enough to handle such composite id´s in queries.
Both linq and criteria api queries make use of a Key object comparison.
var key = new AddonStatus.Key
{
Context = context,
AddonType = addonType
};
Linq
from status
in session.Query<AddonStatus>()
where status.Id == key
select status
Criteria API
session.CreateCriteria<AddonStatus>()
.Add(Restrictions.IdEq(key))
So if anyone has such a scenario working then what am I doing wrong?
Not directly an answer to your question, but it may be useful to you anyway. You could avoid the (explicit) composite key by mapping the AddonStatus as composite-element on the owner (most probably the Context):
<class name="Context">
<map name="AddonStates" table="AddonStatus">
<key column="Context_Id" /> <!-- Foreign key to the Context -->
<index column="Addon_Id" /> <!-- Dictionary key -->
<composite-element>
<property name="Status" /> <!-- data -->
</composite-element>
</map>
</class>
In the class Context is looks like this:
class Context
{
IDictionary<AddonType, AddonStatus> AddonStates { get; private set; }
}
This results and pretty the same database structure, but it is different to work with. I can't say if this is what you actually want, but it just looks like it.
Interestingly, I'm getting almost the exact opposite of this behavior in 2.1.2.
My mapping (simplified):
<!-- Subscriber class -->
<class name="Subscriber" >
<composite-id name="SubscriberKey" class="SubscriberKey">
<key-property name="Request" column="RequestID" type="int"/>
<key-many-to-one name="User" column="UserID" class="User" not-found="ignore" />
</composite-id>
<!-- User class - note that this goes to a different schema,
and is not mutable. Who knows if that's important... -->
<class name="User" schema="AnotherDb.dbo" mutable="false">
<id name="Id" column="UserID" type="int">
<generator class="native" />
</id>
<property name="FirstName" column="FirstName" type="string" />
<property name="LastName" column="LastName" type="string" />
goes to:
public class User
{
public virtual int? Id {get; protected set;}
public virtual string FirstName { get; protected set; }
public virtual string LastName { get; protected set; }
public User() { }
}
public class Subscriber
{
public virtual SubscriberKey SubscriberKey { get; set; }
public virtual User User { get; set; }
public Subscriber() { }
}
public class SubscriberKey
{
public override bool Equals(object obj)
{
if (obj is SubscriberKey && obj != null)
return ((SubscriberKey)obj).Request == Request
&& ((SubscriberKey)obj).User.Id == User.Id;
return false;
}
public override int GetHashCode()
{
return (Request.ToString() + User.Id.ToString()).GetHashCode();
}
public virtual int Request { get; set; }
public virtual User User { get; set; }
public SubscriberKey() { }
}
Things which work:
CreateCriteria<Subscriber>()
.Add(Restrictions.IdEq(keyInstance))
.UniqueResult<Subscriber>();
CreateCriteria<Subscriber>()
.Add(Restrictions.Eq("SubscriberKey.User.Id", aUserID))
.Add(Restrictions.Eq("SubscriberKey.Request", aRequestID))
.UniqueResult<Subscriber>();
Things which don't work:
Get<Subscriber>(keyInstance);
I'm thinking this is an inconsistency between their various ID-equaling query forms. When I get time, I'll be building a minimal unit test to submit as a bug example. I'd be interested in any / all thoughts anyone might have on this...
edit: Heeey, I figured it out!
Things which do work, now that I've read this
Get<Subscriber>(new SubscriberKey() {
User = Load<User>(aUserID), // the important part!
Request = aRequestID
});
This will create a proxy object for the User key, without hitting the database (unless necessary). If you swap Load<User> for Get<User>, you'll immediately hit the database to populate the object, rather than respecting your lazy-loading properties. Use Load.
And things like this are precisely why people suggest the (type)Repository pattern - I can do this behind the scenes: Get<>(new SK(){User=Load<>(key.User.Id)}, and still Get(key) by a single key, identical to every other object.
Using Fluent NHibernate, I cannot seem to devise the necessary automapping conventions for the following (seemingly simple and common) use-case:
public class MyClass
{
private int _specialIdentityField
private string _firstname;
public Id { get { return _specialIdentityField; }; }
public virtual string Firstname
{
get
{
return _firstname;
}
set
{
_firstname = value;
}
}
}
public class OtherClass
{
private int _specialIdentityField
private string _lastname;
public Id { get { return _specialIdentityField; }; }
public virtual string Lastname
{
get
{
return _lastname;
}
set
{
_lastname = value;
}
}
}
The desired mappings are like so:
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" default-access="field.camelcase-underscore" auto-import="true" default-cascade="none" default-lazy="true">
<class xmlns="urn:nhibernate-mapping-2.2" mutable="true" name="MyClass" table="`MyClass`">
<id name="_specialIdentityField" type="System.Int32" access=field>
<column name="Id" />
<generator class="identity" />
</id>
<property name="Firstname" type="System.String">
<column name="Firstname" />
</property>
</class>
<class xmlns="urn:nhibernate-mapping-2.2" mutable="true" name="OtherClass" table="`OtherClass`">
<id name="_specialIdentityField" type="System.Int32" access=field>
<column name="Id" />
<generator class="identity" />
</id>
<property name="Lastname" type="System.String">
<column name="Lastname" />
</property>
</class>
</hibernate-mapping>
Basically the rules are:
everything is field-camelcase-underscore as an access type EXCEPT identity
identity is a fixed-name field in every class (name=_someSpecialIdentityField)
identity access is always field-only and bears no relation to the name of the RO property that surrounds it
The part of this that is completely tripping me up is the convention-mapping of the identity-related elements (clearly the convention-mapping of the properties is completely standard fare). The issue I am having is how to tell FNH conventions that my identity field is a fixed-name. All of the convention overrides that I can find seem to assume there will always be some relationship between the property that represents identity and the name of its underlying backing field (e.g. I can set a 'custom prefix' for the backing field, but cannot seem to see how I can just say "this is always the name of the backing field").
Its obvious to me how to accomplish this with explicit mapping (and for that matter, with XML mapping files) but not obvious at all to me how to accomplish this with convention-based (Automapping) mapping in FNH.
This can't be an atypical use-case so I must just be overlooking something terribly obvious. Thoughts from any FNH gurus appreciated!
EDIT: Take a look at the IAutomappingConfiguration interface. Create you own implementation or override the DefaultAutomappingConfiguration class.
public virtual bool IsId(Member member)
{
return member.Name.Equals("id", StringComparison.InvariantCultureIgnoreCase);
}
then you add it to the initialization:
Fluently.Configure( Configuration )
.Mappings( cfg =>
{ cfg.AutoMappings.Add( IAutomappingConfigurationInstance )}
===============================================
Hi Steve, I think I know what you are trying to do. I use Proteus and FNH automapping. To do the trick with the id I created a wrapper around Proteus which do two things:
1) Maps the ID
2) Hides the Setter for the id
public abstract class EntityObject<TEntity> : IdentityPersistenceBase<TEntity, Guid>, IEntity
where TEntity : class, IEntity
{
public virtual Guid Id
{
get { return _persistenceId; }
}
Guid IIdentifiedEntity<Guid>.Id
{
get { return _persistenceId; }
set { _persistenceId = value; }
}
public virtual int Version
{
get { return _persistenceVersion; }
set { _persistenceVersion = value; }
}
}
And to avoid the property IsTransient to be persisted + other stuff you can create MappingAlternation:
public class EntityAlteration : IAutoMappingAlteration
{
public void Alter( AutoPersistenceModel model )
{
model.OverrideAll( map =>
{
Type recordType = map.GetType().GetGenericArguments().Single();
if( recordType.BaseType.Name == "EntityObject`1" )
{
Type changeType = typeof( Change<> ).MakeGenericType( recordType );
var change = ( IChange )Activator.CreateInstance( changeType );
change.Go( map );
}
} );
}
}
interface IChange
{
void Go( object mapObject );
}
class Change<TRecord> : IChange where TRecord : EntityObject<TRecord>
{
void IChange.Go( object mapObject )
{
var map = ( AutoMapping<TRecord> )mapObject;
map.Id( x => x.Id ).GeneratedBy.Guid().Access.Property();
map.IgnoreProperty( x => x.IsTransient );
}
}
PS: I am really missing the times when you were active in the online space. It was exciting Summer and Authumn 2 years ago...
Is it possible to have a collection of entities that are mapped via an IUserType? For example:
class Foo
{
public int Id { get; set; }
public ISet<Special> Items { get; set; }
}
class Special
{
public Special(string columnValue)
{
_val = columnValue;
}
public override string ToString()
{
return _val.TranformInInterestingWays();
}
private string _val;
}
class SpecialUserTypeForMapping : IUserType
{
// assume that all the right stuff is here
// for using the non-default constructor on the way in
// and calling ToString() on the way out
}
If I'm reading the documentation correctly, neither the <set>, <one-to-many>, <many-to-many>, nor <class> element supports the "type" attribute as is used to apply IUserType mapping to <property>s. So how would I go about mapping this?
The most expedient solution seems to be to use the <element> element, like so:
<class name="Foo" table="foos">
<id name="Id" />
<set name="Items" table="foo_special">
<key column="fooId" />
<element column="special_value" type="SpecialUserTypeForMapping" />
</set>
</class>
Retrieving different Foo instances from the DB is no problem, but it's not clear whether it's possible to write queries against the special_value column, which is a requirement in my scenario. This question seems to indicate that it's not.