I'm trying to switch out .hbm mappings to fluent mappings and have a problem with the mapping of composite-ids and the usage of Interfaces
the Class looks as follows:
public class ClassWithCompositeId {
public virtual IKeyOne KeyOne { get; set; }
public virtual IKeyTwo KeyTwo { get; set; }
}
our hbm mapping looks like this:
<hibernate-mapping ...>
<class name="ClassWithCompositeId" table="t_classwithcompositeid">
<composite-id>
<key-many-to-one name="KeyOne" column="colkeyone" class="company.namespace.boSkillBase, BL_Stammdaten" />
<key-many-to-one name="KeyTwo" column="colkeytwo" class="boQualifikation" />
</composite-id>
</hibernate-mapping>
Please note, that we got interfaces in the Class! No I'm trying to map this with Fluent nhibernate.
Map {
public ClassWithCompositeIdMap() {
CompositeId()
.KeyReference(x => x.KeyOne, "colkeyone")
.KeyReference(x => x.KeyTwo, "colkeytwo");
...
}
}
But now Fluent generates the Mapping as follows:
...
<composite-id mapped="false" unsaved-value="undefined">
<key-many-to-one name="KeyOne" class="company.namespace.IKeyOne, Interfaces, Version=0.1.4.3379, Culture=neutral, PublicKeyToken=null">
<column name="colkeyone" />
</key-many-to-one>
<key-many-to-one name="KeyTwo" class="company.namespace.IKeyTwo, Interfaces, Version=0.1.4.3379, Culture=neutral, PublicKeyToken=null">
<column name="colkeytwo" />
</key-many-to-one>
</composite-id>
...
The "Class" Attribute points now to the Interface not to the implementation of this interface which results in an error.
How can I tell Fluent nHibernate to use another class as the attribute value?
Try downloading NhGen from SourceForge. It reads database schemas and generates Fluent mappings and classes etc. While all the code might not be what you need, it should start you off in the right direction as it supports composite keys and represents them as separate classes off the main entity.
I beleive it uses a syntax similar to
CompositeId()
.ComponentCompositeIdentifier(x => x.Key, "Namespace.Key, Assembly")
.KeyProperty(x => x.Key.Id1, "Id1")
.KeyProperty(x => x.Key.Id2, "Id2")
.KeyProperty(x => x.Key.Id3, "Id3");
Tanks! But I've found the answer on my on. In fact i found a missing feature in fluent nHibernate. The feature has already been added to the dev branch by Paul Batum.
You would use it like so:
Map {
public ClassWithCompositeIdMap() {
CompositeId()
.KeyReference(x => x.KeyOne, k =>
k.Type<KeyOneImplementation>(), "colkeyone")
.KeyReference(x => x.KeyTwo, k =>
k.Type<KeyTwoImplementation>(), "colkeytwo");
...
}
}
http://github.com/paulbatum/fluent-nhibernate/tree/dev
You can see the original Conversation here: http://support.fluentnhibernate.org/discussions/help/349-how-to-map-a-composite-id-when-using-interfaces-or-how-to-change-the-class-attribute-in-the-key-many-to-one-tag
Related
I have a requirement to write the mapping using Nhibernate fluent.
I have the following in hbm
<class name="XYZ" table="Some_Table">
<composite-id>
<key-many-to-one name="A" column="A_ID"/>
<key-property name="Term" type="Some_Assembly">
<column name="YEAR"/>
<column name="MONTH"/>
</key-property>
</composite-id>
<property name="P" column="P"/>
</class>
and I would need to rewrite this in fluent. the main reason is that we are moving away from hbm files to fluent.
so far I have the following
public class XYZMap: ClassMap<XYZ>
{
public XYZMap()
{
Table("Some_Table");
CompositeId()
.KeyProperty(x=> x.Term, set =>
{
set.ColumnName("Year");
set.ColumnName("Month");
set.Type(typeof(Some_Assembly));
})
.KeyProperty(x=> x.A, set =>
{
set.ColumnName("A");
set.Type(typeof (Other_Assembly));
});
Map(x=> x.P, "P");
}
}
But I am getting the following error
X.Y.TestZ.PostCreate:
SetUp : Autofac.Core.DependencyResolutionException : An exception was thrown while executing a resolve operation. See the InnerException for details.
----> FluentNHibernate.Cfg.FluentConfigurationException : An invalid or incomplete configuration was used while creating a SessionFactory. Check PotentialReasons collection, and InnerException for more detail.
----> NHibernate.MappingException : Could not compile the mapping document: (XmlDocument)
----> NHibernate.MappingException : Could not determine type for: Other_Assembly, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null, for columns: NHibernate.Mapping.Column(A_ID)
I think I am unable to map many-to-one when I am trying to configure using fluent.
So can someone please help.
You should be using KeyReference instead for column A.
.KeyReference(x => x.A, "A");
i've got one more question. I upgraded to FluentNHibernate and got now a
problem with my dicitionary mappings.
The class i'am trying to map has the following Property
IDictionary LohnParameter
The mapping is as follows
HasMany(x => x.LohnParameter)
.ForeignKey("cat_condition_version__id")
.DictionaryKey("wrd_cntry__id")
.OneToMany<boLohnartEigenschaften>()
.Not.Lazy()
.Inverse()
.Cascade.AllDeleteOrphan();
The resulting hbm.xml looks like this:
<map cascade="all-delete-orphan" inverse="true" lazy="false" name="LohnParameter" table="boLohnartVersionLohnParameter" mutable="true">
<key>
<column name="cat_condition_version__id" />
</key>
<index-many-to-many class="proSoft.Office.Model.Business.Welt.boLand, proSoft.Office.Model.Business, Version=0.1.19.20243, Culture=neutral, PublicKeyToken=b0e4f89242e69335">
<column name="wrd_cntry__id" />
</index-many-to-many>
<one-to-many class="proSoft.Office.Model.Business.Konditionen.boLohnartEigenschaften, proSoft.Office.Model.Business, Version=0.1.19.20243, Culture=neutral, PublicKeyToken=b0e4f89242e69335" />
</map>
With the new version, the compiler complains, that the Property
"ForeignKey" is missing. I tried now everything, but i can't get it to
work properly. My last try was:
HasMany(x => x.LohnParameter)
.AsMap<boCountry>(
index => index.Column("wrd_cntry__id").Type<boCountry>(),
element => element.Type<boLohnartEigenschaften>()
)
.KeyColumn("cat_condition_version__id")
.Not.LazyLoad()
.Inverse()
.Cascade.AllDeleteOrphan();
But the error i always get is:
{"Could not determine type for:
proSoft.Office.Model.Business.Welt.boCountry,
proSoft.Office.Model.Business, Version=0.1.14.556, Culture=neutral,
PublicKeyToken=b0e4f89242e69335, for columns:
NHibernate.Mapping.Column(wrd_cntry__id)"}
I don't have clue what do do.
Regards
Christian Erhardt
i think you searching for this
HasMany(x => x.LohnParameter)
.AsEntityMap("wrd_cntry__id")
Thank you for the hint, it was the right way. The correct mapping is this:
HasMany(x => x.LohnParameter)
.KeyColumn("cat_condition_version__id")
.AsEntityMap("wrd_cntry__id")
.Not.LazyLoad()
.Inverse()
.Cascade.AllDeleteOrphan();
This results in exact the same hbm.xml file.
Thank you!
I am trying to user NHibernate / FluentNHibernate to create a table in my database. I seem to have figured it out for the most part but when I run the test the table isn't created. I see in the Configuration object that the ClassMappings is a big fat zero even thought I have user FluentNHibernate to configure them from an assembly. I somewhat understand this but I am missing some connection somewhere... Here is the code snippets, maybe someone can see what I forogt?
Here is my dataconfig class.
public static FluentConfiguration GetFluentConfiguration()
{
string hibernateCfgFile = #"C:\Users\kenn\Documents\Visual Studio 2008\Projects\NHibernateTestTwo\Infrastructure\hibernate.cfg.xml";
return Fluently.Configure(new Configuration().Configure(#hibernateCfgFile))
.Mappings(cfg => cfg.FluentMappings.AddFromAssembly(typeof(AddressMap).Assembly));
}
Here is the test class.
[Test, Explicit]
public void SetupDatabase()
{
FluentConfiguration conf = DataConfig.GetFluentConfiguration();
conf.ExposeConfiguration(BuildSchema).BuildSessionFactory();
}
private static void BuildSchema(Configuration conf)
{
new SchemaExport(conf).SetOutputFile("drop.sql").Drop(false, true);
new SchemaExport(conf).SetOutputFile("create.sql").Create(false, true);
}
Here is the mappings
public AddressMap()
{
Table("Address");
DynamicUpdate();
Id(a => a.Id).GeneratedBy.GuidComb();
Map(a => a.AddressOne).Not.Nullable().Length(100);
Map(a => a.AddressTwo).Length(100);
Map(a => a.City).Not.Nullable().Length(100);
Map(a => a.state).Not.Nullable().Length(100);
Map(a => a.zip).Not.Nullable().Length(50);
Map(a => a.Primary).Not.Nullable();
}
The hibernate.cfg.xml file
<hibernate-configuration xmlns="urn:nhibernate-configuration-2.2">
<session-factory>
<property name="connection.driver_class">
NHibernate.Driver.SqlClientDriver
</property>
<property name="connection.connection_string">
Data Source=MYPC;Initial Catalog=NHibernateSample;Integrated Security=True;
</property>
<property name="show_sql">true</property>
<property name="dialect">
NHibernate.Dialect.MsSql2005Dialect
</property>
<property name="adonet.batch_size">100</property>
<!--<property name="proxyfactory.factory_class">
NHibernate.ByteCode.LinFu.ProxyFactoryFactory,
NHibernate.ByteCode.LinFu
</property>-->
<property name="proxyfactory.factory_class">
NHibernate.ByteCode.Castle.ProxyFactoryFactory,
NHibernate.ByteCode.Castle
</property>
</session-factory>
I am just not sure what is missing there... It clearly is talking to the DB cause if I change the name of the database to something that doesn't exist it trows an exception, I am stuck - I have gone round and round on this and just haven't figured it out yet so any help would be greatly appreciated.
Thanks!
See my comment... Don't forget to make your map classes public or FluentNHibernate won't see them.
I have been doing nhibernate since many days. But Today I stuck at a frustrating issue i.e. mapping exception.
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" namespace="IPC.Base.Domains" assembly="IPC">
<class name="MenuItem" table="dbo.COR_MenuItem" default-access="property" default-cascade="save-update" default-lazy="true">
<cache usage="read-only" region="completelyStatic"/>
<id name="Id" type="System.Int32">
<generator class="identity" />
</id>
<property name="Name" type="System.String" />
<property name="Order" column="DisplayOrder" />
<property name="Key" column="KeyChain" />
<property name="Route" />
<property name="ActionMethod" />
<property name="IsHotlink" />
<many-to-one name="ParentMenuItem" column="ParentMenuItemId" class="MenuItem" cascade="none"/>
<bag name="MenuItems" table="dbo.COR_MenuItem" cascade="none">
<cache usage="read-only" region="completelyStatic"/>
<key column="ParentMenuItemId" />
<one-to-many class="MenuItem" />
</bag>
</class>
</hibernate-mapping>
And I do have a mapping class as following:
using System;
using System.Collections.Generic;
namespace IPC.Base.Domain
{
public partial class MenuItem : PersistentObject
{
public MenuItem()
{
MenuItems = new List<MenuItem>();
}
public virtual string Name { get; set; }
public virtual int? Order { get; set; }
public virtual string Key { get; set; }
public virtual string Route { get; set; }
public virtual string ActionMethod { get; set; }
public virtual bool IsHotlink { get; set; }
public virtual IList<MenuItem> MenuItems { get; set; }
public virtual MenuItem ParentMenuItem { get; set; }
/// <summary>
/// only to be used from the menu builder app
/// </summary>
/// <param name="id"></param>
public virtual void SetId(int id)
{
Id = id;
}
}
}
I have the following nHibernate Config running with the application. But as per my experience there is absolutely no issue with the nHibernate Config.
<?xml version="1.0"?>
<hibernate-configuration xmlns="urn:nhibernate-configuration-2.2">
<session-factory name="IPC">
<property name="dialect">NHibernate.Dialect.MsSql2005Dialect</property>
<property name="connection.driver_class">NHibernate.Driver.SqlClientDriver</property>
<property name="connection.connection_string">**************SQL CONNECTION****************/property>
<property name="proxyfactory.factory_class">NHibernate.ByteCode.LinFu.ProxyFactoryFactory, NHibernate.ByteCode.LinFu</property>
<property name="cache.provider_class">NHibernate.Caches.SysCache.SysCacheProvider, NHibernate.Caches.SysCache</property>
<property name="cache.use_query_cache">true</property>
<property name="show_sql">true</property>
<mapping assembly="IPC"/>
</session-factory>
</hibernate-configuration>
I am using the following line for code for invoking nHibernate session object to system.
static ISessionFactory CurrentFactory
{
get
{
if (factory == null)
{
Configuration cfg = new Configuration();
if (cfgFile == null)
cfg = cfg.Configure();
else
cfg = cfg.Configure(cfgFile);
factory = cfg.BuildSessionFactory();
}
return factory;
}
}
public static ISession Create()
{
var session = CurrentFactory.OpenSession();
return session;
}
Now! When I call the following line of code I am getting:
public virtual T Get(int id)
{
return Session.Get<T>(id);
}
No persister for: IPC.Base.Domain.MenuItem
Exception Details: NHibernate.MappingException: No persister for: IPC.Base.Domain.MenuItem
I have already enabled by nHibernate log. Following are the basic details from log
00:54:40.011 [4] INFO NHibernate.Cfg.Environment - NHibernate 2.1.0.4000 (2.1.0.4000)
00:54:40.055 [4] INFO NHibernate.Cfg.Environment - Bytecode provider name : lcg
00:54:40.057 [4] INFO NHibernate.Cfg.Environment - Using reflection optimizer
00:54:40.682 [4] DEBUG NHibernate.Cfg.Configuration - dialect=NHibernate.Dialect.MsSql2005Dialect
00:54:40.683 [4] DEBUG NHibernate.Cfg.Configuration - connection.driver_class=NHibernate.Driver.SqlClientDriver
00:54:40.683 [4] DEBUG NHibernate.Cfg.Configuration - connection.connection_string=*********************SQL Connection*****************
00:54:40.683 [4] DEBUG NHibernate.Cfg.Configuration - proxyfactory.factory_class=NHibernate.ByteCode.LinFu.ProxyFactoryFactory, NHibernate.ByteCode.LinFu
00:54:40.683 [4] DEBUG NHibernate.Cfg.Configuration - cache.provider_class=NHibernate.Caches.SysCache.SysCacheProvider, NHibernate.Caches.SysCache
00:54:40.683 [4] DEBUG NHibernate.Cfg.Configuration - cache.use_query_cache=true
00:54:40.683 [4] DEBUG NHibernate.Cfg.Configuration - show_sql=true
00:54:40.684 [4] DEBUG NHibernate.Cfg.Configuration - IPC<-IPC
00:54:40.685 [4] INFO NHibernate.Cfg.Configuration - Searching for mapped documents in assembly: IPC
00:54:40.689 [4] WARN NHibernate.Cfg.Configuration - No mapped documents found in assembly: IPC, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
00:54:40.691 [4] INFO NHibernate.Cfg.Configuration - Configured SessionFactory: IPC
00:54:40.691 [4] DEBUG NHibernate.Cfg.Configuration - properties: System.Collections.Generic.Dictionary`2[System.String,System.String]
00:54:45.716 [4] INFO NHibernate.Cfg.Configuration - checking mappings queue
00:54:45.716 [4] INFO NHibernate.Cfg.Configuration - processing one-to-many association mappings
00:54:45.717 [4] INFO NHibernate.Cfg.Configuration - processing one-to-one association property references
00:54:45.717 [4] INFO NHibernate.Cfg.Configuration - processing foreign key constraints
00:54:45.747 [4] INFO NHibernate.Dialect.Dialect - Using dialect: NHibernate.Dialect.MsSql2005Dialect
I have read almost all the post with stack! :) and even doing google for the same. The final words I am getting there is an issue with my assembly naming! I sure that there is a issue with the steps which I have followed. But still searching for that trigger!!!
Thanks!
In advance!!
Probably you forgot to set your hbm mappings as embedded resource to the DLL.
Finally I resolved the issue! Its basically a stupid stuff which normally I tend to do and then forget to get it back on running stage.
Yes, I was related with the assembly issue. Nothing but when I was trying to build then data layer application It was not enbading the xml files inside assembly.
A Fix for solve the problem go to your
*.hbm.xml files and do a right click and change build option of that file
to embedded resource.
Alrite!
Many thanks!
I am then using Fluent NHibernate and its automapping feature to map the the following simplified POCO classes:
public class Webpage
{
public virtual int Id { get; set; }
public virtual string UrlIdentifier { get; set; }
public virtual WebpageType WebpageType { get; set; }
}
public class WebpageType
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
}
I am then overriding the following mapping to explicitly set no cascading from Webpage to WebpageType:
public class WebpageMap : IAutoMappingOverride<Webpage>
{
public void Override(AutoMapping<Webpage> mapping)
{
mapping.References(w => w.WebpageType).Cascade.None();
}
}
For any pur NHibernate readers, here are the xml mappings produced by fluent:
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" default-access="property" auto-import="true" default-cascade="none" default-lazy="true">
<class xmlns="urn:nhibernate-mapping-2.2" name="EveryPage.Core.Domain.Webpage, EveryPage.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" table="`Webpage`">
<id name="Id" type="System.Int32, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" unsaved-value="0">
<column name="Id" />
<generator class="identity" />
</id>
<property name="UrlIdentifier" type="System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<column name="UrlIdentifier" />
</property>
<many-to-one cascade="none" class="EveryPage.Core.Domain.WebpageType, EveryPage.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" name="WebpageType">
<column name="WebpageType_id" />
</many-to-one>
</class>
</hibernate-mapping>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" default-access="property" auto-import="true" default-cascade="none" default-lazy="true">
<class xmlns="urn:nhibernate-mapping-2.2" name="EveryPage.Core.Domain.WebpageType, EveryPage.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" table="`WebpageType`">
<id name="Id" type="System.Int32, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" unsaved-value="0">
<column name="Id" />
<generator class="identity" />
</id>
<property name="Name" type="System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<column name="Name" />
</property>
</class>
</hibernate-mapping>
The problem comes when I test that updates do not cascade to WebpageType via webpage, basically they do!!
I have the following test:
[Test]
public void Assert_SaveOrUpdate_On_Webpage_Does_Not_Cascade_Update_To_WebpageType()
{
// Get the existing webpage.
webpage = _webpageRepository.Get("~/testwebpage1.aspx");
// Update the WebpageType.
const string webpageTypeName = "qwerty test";
webpage.WebpageType.Name = webpageTypeName;
// Save the changes.
Assert.DoesNotThrow(() => _webpageRepository.SaveOrUpdate(webpage));
// We need to flush the changes to the store for it to execute the changes.
Assert.DoesNotThrow(() => NHibernateSession.Current.Flush());
// Remove the webpage and tag from the level 1 cache so we force a trip to the store on our next check.
NHibernateSession.Current.Evict(webpage);
// Check that the webpageType has not been updated.
webpageType = _webpageTypeRepository.Get(webpageType.Id);
Assert.AreNotEqual(webpageTypeName, webpageType.Name);
}
The above test is wrapped in a global transaction.
The test fails and NHibernate does execute an update to the Name of the related WebpageType. The delete and save(create new) cascades work correctly and do not cascade.
Have I missunderstood cascade and/or is there a problem with my logic/test.
Any help/advice is appreciated. Thanks.
If you are trying to stop your app from accidentally changing properties on WebPageType, I think it would be easier and safer to achieve this by marking WebPageType as ReadOnly in the mapping. Then you won't need to protect it via handling cascading in all its associations.
I think this is a misunderstanding of what cascading means.
In your example, NHibernate will update the Name property of you WebPageType no matter what you set cascading to. If you think about it, how would the NHibernate library tell if you're manipulating the property's value using the association from the WebPage instance, or if it's done "directly"?
The settings for cascading in NHibernate tells how associations between entities should be handled, not how the actual value inside each entity is handled. For example, you can set delete cascading, which will automatically delete associated entities when the entity itself is deleted.
Things blog post might make things a bit clearer, or at least work as some kind of reference: http://ayende.com/Blog/archive/2006/12/02/NHibernateCascadesTheDifferentBetweenAllAlldeleteorphansAndSaveupdate.aspx
What does your repository do? Make sure it doesn't run a saveorupdate on the webpagetype. If it isn't then I don't see any obvious explanation for this behaviour.