How do I map <properties> by code with NHibernate? - nhibernate

I need to define a unique constraint on multiple properties. In XML this would look like:
<properties name="Name" unique="true">
<property name="FirstName" />
<property name="LastName" />
</properties>
What is the mapping-by-code equivalent in NHibernate (3.3+)? Are there any alternatives to create a multi-column unique index?

An alternative way to create a unique index is the following:
this.Property(x => x.FirstName, m => m.UniqueKey("name"));
this.Property(x => x.LastName, m => m.UniqueKey("name"));
I still have not found out how to map <properties>, though.

Related

NHibernate component loading as null when many-to-one NULL, despite several non-null properties

I understand that when all the properties of a mapped component are null in the database, NHibernate will set the component to null. However, in the following case the component is being set to null if the many-to-one item LevelOfInvolvement is null, despite the fact that all the other properties of the component are not null.
Changing the value of the LevelOfInvolvement column in the database will cause the component to be null or not, regardless of the value of the other fields.
Here is the maping in full:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
<class name="NHS.WebTeam.LPTInvolvementHub.Activity, NHS.WebTeam.LPTInvolvementHub" table="[Activity]" lazy="false" optimistic-lock="version" where="Deleted=0" >
<id name="ID" type="Int32" column="ID" unsaved-value="0">
<generator class="hilo">
<param name="table">NHibernateHiLo</param>
<param name="column">NextValue</param>
<param name="max_lo">100</param>
</generator>
</id>
<version name="Version"/>
<property name="Deleted" />
<property name="LastUpdateBy" />
<property name="LastUpdateDate" />
<many-to-one name="Service" column="ServiceID"></many-to-one>
<property name="Title"></property>
<property name="Abstract"></property>
<property name="ProposedDate"></property>
<property name="DueDate"></property>
<property name="ActualDate"></property>
<property name="Rationale"></property>
<many-to-one name="PreAssessment" column="PreAssessmentID"></many-to-one>
<many-to-one name="PostAssessment" column="PostAssessmentID"></many-to-one>
<component name="InvolvementChecklist">
<property name="Impact" column="InvolvementChecklist_Impact"></property>
<property name="RewardsAndRecognition" column="InvolvementChecklist_RewardsAndRecognition"></property>
<property name="Training" column="InvolvementChecklist_Training"></property>
<property name="LogisticalIssues" column="InvolvementChecklist_LogisticalIssues"></property>
<property name="Feedback" column="InvolvementChecklist_Feedback"></property>
<property name="DueRegard" column="InvolvementChecklist_DueRegard"></property>
<property name="SupportRequiredFromTheTeam" column="InvolvementChecklist_SupportRequiredFromTheTeam"></property>
<property name="QualityAssurance" column="InvolvementChecklist_QualityAssurance"></property>
<property name="NonComplianceReason" column="InvolvementChecklist_NonComplianceReason"></property>
<many-to-one name="LevelOfInvolvement" column="InvolvementChecklist_LevelOfInvolvementID"></many-to-one>
</component>
</class>
</hibernate-mapping>
The code to load the entity is simply:
entity = NHibernateSession.Load(persitentType, id);
I am using NHibernate version 3.3.1
This seems a relatively simple mapping, but I cannot find any reference to others having similar problems.
In addition to returning null if all of the component members are null, I believe NHibernate will also treat a component as null if any non-nullable members are null. By default a <many-to-one> mapping creates a non-nullable member, which I think would cause the behaviour you're seeing. Try changing the LevelOfInvolement mapping to allow nulls:
<many-to-one
name="LevelOfInvolvement"
column="InvolvementChecklist_LevelOfInvolvementID"
not-null="false" />

How to map this Dictionary with the newest fluentNHibernate version?

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!

Configuring Fluent NHibernate from NHibernate config section

I'm trying to use Fluent NHibernate in my solution by configuring it with the following NHibernate xml configuration section
<hibernate-configuration xmlns="urn:nhibernate-configuration-2.2" >
<session-factory name="mitre">
<property name="dialect">NHibernate.Dialect.Oracle9iDialect</property>
<property name="connection.provider">NHibernate.Connection.DriverConnectionProvider</property>
<property name="connection.driver_class">NHibernate.Driver.OracleDataClientDriver</property>
<property name="connection.connection_string">Data Source=YOUR_DB_SERVER;Database=Northwind;User ID=YOUR_USERNAME;Password=YOUR_PASSWORD;</property>
<property name="connection.isolation">ReadCommitted</property>
<property name="default_schema">TRATE</property>
<!-- HBM Mapping Files -->
<mapping assembly="Markel.Mint.Mitre.Data" />
</session-factory>
</hibernate-configuration>
In my code file, to instantiate ISession:
NH_Cfg.Configuration cfg = new NH_Cfg.Configuration();
cfg.Configure();
Fluently.Configure(cfg).Mappings(m => m.FluentMappings = ????)
My question is that if I have already specified the assembly in the NHibernate config section, do I need to explicitly set FluentMappings? If so, then is it possible to retrieve this data from NHibernate config programmatically?
Thanks
Oz
The mapping assembly in hibernate.cfg.xml is searched for embedded *.hbm.xml files. NHibernate does not know anything about fluent mappings (e.g. ClassMap) as those are introduced by Fluent NHibernate. So you need:
Fluently.Configure(cfg).Mappings(m => m.FluentMappings.AddFromAssemblyOf<SomeDomainType>();
in order to configure NHibernate using your ClassMap mappings.
Thanks for the quick response, James.
Could I do the following then?
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-configuration xmlns="urn:nhibernate-configuration-2.2" >
<session-factory name="mitre">
<property name="dialect">NHibernate.Dialect.Oracle9iDialect</property>
<property name="connection.provider">NHibernate.Connection.DriverConnectionProvider</property>
<property name="connection.driver_class">NHibernate.Driver.OracleDataClientDriver</property>
<property name="connection.connection_string">Data Source=YOUR_DB_SERVER;Database=Northwind;User ID=YOUR_USERNAME;Password=YOUR_PASSWORD;</property>
<property name="connection.isolation">ReadCommitted</property>
<property name="default_schema">TRATE</property>
<property name="fluent.nhibernate.fluentmapping">Markel.Mint.Mitre.Core.Domain</property>
</session-factory>
</hibernate-configuration>
Then my code could refer to the property thus:
NH_Cfg.Configuration cfg = new NH_Cfg.Configuration(); cfg.Configure();
Fluently.Configure(cfg).Mappings(m => m.FluentMappings.AddFromAssembly(Assembly.Load(cfg.Properties["fluent.nhibernate.fluentmapping"])));

NHibernate ClassMappings = 0

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.

NHibernate, one-to-one mapping, cascade insert

I have a one-to-one relationship between a Company class and a CompanySettings class. When I create a new Company object, (a CompanySettings object is created in Company's constructor for its Settings property), and then
SaveOrUpdate(session, companyObject)
I expect the INSERT to cascade from the Company to the CompanySettings. However, this does not happen unless I explicitly call SaveOrUpdate on the CompanySettings object as well.
Mapping files shown below:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"`>
<class name="AST.Domain.Company, AST.Domain" table="Companies">
<id name="EntityID" column="CompanyId">
<generator class="guid.comb" />
</id>
<property name="CompanyName" />
. . .
<one-to-one name="Settings" class="AST.Domain.CompanySettings, AST.Domain"
constrained="true" lazy="false" />
</class>
</hibernate-mapping>
My mapping file for the Company Settings class:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
<class name="AST.Domain.CompanySettings, AST.Domain" table="CompanySettings">
<id name="EntityID" column="CompanyId">
<generator class="foreign">
<param name="property">Company</param>
</generator>
</id>
<property name="MaxUsers" />
<one-to-one name="Company" class="AST.Domain.Company, AST.Domain" />
</class>
</hibernate-mapping>
Have you tried specifying cascade="all" on your one-to-one mapping?
Set the cascade attribute of the one-to-one in the Company mapping.
But, on another note:
Have you thought of mapping the CompanySettings as a 'component' of Company instead of a separate entity ?
Isn't it so that 'CompanySettings' is a 'value object', and should be mapped better as a component ?
By doing this, you can put the CompanySettings values in the same table as 'Company', but it will be treated as a separate class.
Since this is a one-to-one mapping, I think it is a better option for your data model as well.
Then, your mapping would look something like this:
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"`>
<class name="AST.Domain.Company, AST.Domain" table="Companies">
<id name="EntityID" column="CompanyId">
<generator class="guid.comb" />
</id>
<property name="CompanyName" />
. . .
<component name="Settings" class="AST.Domain.CompanySettings, AST.Domain">
<property name="MaxUsers" />
</component>
</class>
</hibernate-mapping>
You will have indeed 2 separate objects (Company & CompanySettings, and Company will have a 'Companysettings' object, but the settings will be saved in the Company table).
Or, is there any special reason on why you've put the CompanySettings in a separate table ? I mean, it is a one-to-one relation so this is completely not necessary (and imho even a bad practice :) ).