How to map this in Fluent.NHibernate - nhibernate

I'd like to get this output from fluent.nhibernate
<map name="Dict" table="TABLE">
<key column="ID_USER" />
<index-many-to-many column="ID_TABLE" class="TableClass" />
<element column="COL" type="Int32" />
</map>
where class has:
public class User
{
public virtual IDictionary<TableClass, int> Dict { get; protected set; }
}
Closest I've got to is this:
HasMany(x => x.Dict)
.Table("TABLE")
.KeyColumn("ID_USER")
.AsMap<TableClass>("ID_TABLE")
.Element("COL");
And the output for that is:
<map name="Dict" table="TABLE">
<key>
<column name="ID_USER" />
</key>
<index type="TableClass">
<column name="ID_TABLE" />
</index>
<element type="Int32">
<column name="COL" />
</element>
<one-to-many class="Int32" /> <!-- BUG -->
</map>
How can I remove the last line (marked with BUG)?
It's not always needed (like in my example it isn't)!

Related

NHibernate Could not determine type for [namespace,assembly] for columns: NHibernate.Mapping.Column(column)

I'm confused with this situation. Having these two classes:
public class Payment {
public Payment() { }
public string Trn { get; set; }
public TxType TxTypeId { get; set; }
public string TxCode { get; set; }
public System.Nullable<decimal> Amount { get; set; }
public System.Nullable<System.DateTime> DateStamp { get; set; } }
public PaymentAudit() { }
public System.DateTime DateStamp { get; set; }
public Payment Trn { get; set; }
public PaymentSaga PaymentStateId { get; set; }
public ProcessState ProcessState { get; set; }
public PublishState PublishState { get; set; }
public System.Nullable<short> ChgCount { get; set; }
public string UserName { get; set; }
And mappings are:
<class name="Payment" table="Payment" lazy="false" >
<id name="Trn">
<generator class="identity" />
</id>
<many-to-one insert="false" update="false" lazy="false" name="TxTypeId">
<column name="TxTypeId" sql-type="varchar" not-null="false" />
</many-to-one>
<property name="TxTypeId">
<column name="TxTypeId" sql-type="varchar" not-null="false" />
</property>
<many-to-one insert="false" update="false" lazy="false" name="TxCode">
<column name="TxCode" sql-type="varchar" not-null="false" />
</many-to-one>
<property name="TxCode">
<column name="TxCode" sql-type="varchar" not-null="false" />
</property>
<property name="Amount">
<column name="Amount" sql-type="decimal" not-null="false" />
</property>
<property name="DateStamp">
<column name="DateStamp" sql-type="datetime" not-null="false" />
</property>
<bag name="PaymentAudits" inverse="true" cascade="none">
<key column="Trn" />
<one-to-many class="PaymentAudit" not-found="ignore" />
</bag>
</class>
<class name="PaymentAudit" table="PaymentAudit" >
<composite-id>
<key-many-to-one name="PaymentStateId" column="PaymentStateId" />
<key-property name="DateStamp" column="DateStamp" />
<key-many-to-one name="ProcessState" column="ProcessState" />
</composite-id>
<property name="PublishState">
<column name="PublishState" sql-type="varchar" not-null="false" />
</property>
<many-to-one name="Trn">
<column name="Trn" sql-type="varchar" not-null="false" />
</many-to-one>
<property name="ChgCount">
<column name="ChgCount" sql-type="smallint" not-null="false" />
</property>
<property name="UserName">
<column name="UserName" sql-type="nvarchar" not-null="false" />
</property>
<many-to-one name="PublishState">
<column name="PublishState" sql-type="varchar" not-null="false" />
</many-to-one>
</class>
The tables,
Payment:
Trn PK
TxTypeId FK
TxCode FK
Amount
DateStamp
PaymentAudit:
PaymentStateId PK
DateStamp PK
ProcessState PK
PublishState FK
Trn FK
ChgCount
UserName
And I got this error:
Could not determine type for: MyProject.NHibernate.Payment.Model.Payment.Payment, MyProject.NHibernate.Payment.Model, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null, for columns: NHibernate.Mapping.Column(Trn)
Any ideas?
Thanks.
Trn is the primary key, is a string and is mapped as identity? Identity is an integer, so the property should be int.

Nhibernate: Foreign key must have same number of columns as the referenced primary key

I'm confused with this situation. Having these two classes:
public class TxType {
public TxType()
{
Payments = new List<Payment>();
}
public string TxTypeId { get; set; }
public string TxCode { get; set; }
public IList<Payment> Payments { get; set; }
public string Description { get; set; } }
public class Payment {
public Payment() { }
public string Trn { get; set; }
public TxType TxTypeId { get; set; }
public string TxCode { get; set; }
public System.Nullable<decimal> Amount { get; set; }
public System.Nullable<System.DateTime> DateStamp { get; set; } }
TxType table have two PK (TxTypeId, TxCode), and both are FK in Payment Table.
These are my mappings:
<class name="TxType" table="TxType" lazy="false" >
<composite-id>
<key-property name="TxTypeId" column="TxTypeId" />
<key-property name="TxCode" column="TxCode" />
</composite-id>
<property name="Description">
<column name="Description" sql-type="nvarchar" not-null="false" />
</property>
<bag name="Payments" inverse="true" cascade="none">
<key>
<column name="TxTypeId" />
<column name="TxCode" />
</key>
<one-to-many class="MyProject.Nhibernate.Repository.Payment" not-found="ignore" />
</bag>
</class>
<class name="Payment" table="Payment" lazy="false" >
<id name="Trn">
<generator class="identity" />
</id>
<many-to-one insert="false" update="false" lazy="false" name="TxTypeId">
<column name="TxTypeId" sql-type="varchar" not-null="false" />
</many-to-one>
<property name="TxTypeId">
<column name="TxTypeId" sql-type="varchar" not-null="false" />
</property>
<many-to-one insert="false" update="false" lazy="false" name="TxCode">
<column name="TxCode" sql-type="varchar" not-null="false" />
</many-to-one>
<property name="TxCode">
<column name="TxCode" sql-type="varchar" not-null="false" />
</property>
<property name="Amount">
<column name="Amount" sql-type="decimal" not-null="false" />
</property>
<property name="DateStamp">
<column name="DateStamp" sql-type="datetime" not-null="false" />
</property>
</class>
And I'm stuck with this error:
Foreign key (FKF4FA0208CDBA724F:Payment [TxTypeId])) must have same number of columns as the referenced primary key (TxType [TxTypeId, TxCode]).
Thanks!
Okay, i figured out by adding these lines in Payment mapping:
<many-to-one name="TxType" class="MyProject.Nhibernate.Repository.TxType" insert="false" update="false" lazy="false" >
<column name="TxTypeId" sql-type="varchar" not-null="false" />
<column name="TxCode" sql-type="varchar" not-null="false" />
</many-to-one>
Thanks!
Your id property must be only one. That is, if your table has a composite primary key, you should create a class for mapping to these columns. Remember, the Get/Load methods only take a single parameter for the id, not many.

Mapping dictionary

I have 3 tables Users, AccessLevels, Roles. My User class have public virtual Dictionary<Role,AccessLevel> Roles {get; set;}, and
Role class have public virtual Dictionary<User,AccessLevel> Users {get;set;} ? How such dictionaries should be represented in the mapping?
in FluentNhibernate it would look like
HasManyToMany(user => user.Roles)
.Table("UserRoleAccessLevel")
.AsEntityMap("role_id")
.KeyColumn("user_id");
HasManyToMany(role => role.Users)
.Table("UserRoleAccessLevel")
.AsEntityMap("user_id")
.KeyColumn("role_id");
can't test it right now though
Update: in hbm.xml
<map name="Roles" table="UserRoleAccessLevel">
<key column="user_id" />
<index-many-to-many class="Role" column="role_id" />
<many-to-many class="AccessLevel" column="accesslevel_id" />
</map>
<map name="Users" table="UserRoleAccessLevel">
<key column="role_id" />
<index-many-to-many class="User" column="user_id" />
<many-to-many class="AccessLevel" column="accesslevel_id" />
</map>
It would be very nice if you provided structure of your tables, especially AccessLevels table. As far as I could guess structure of your tables I can suggest you this mapping
<class name="User" table="Users">
<id name="Id" column="user_id">
<generator class="native" />
</id>
<property name="Name" column="user_name" not-null="true" />
<map name="Roles" table="AccessLevels" cascade="save-update">
<key column="user_id" />
<map-key-many-to-many column="role_id" class="Role" />
<one-to-many class="AccessLevel"/>
</map>
</class>
<class name="Role" table="Roles">
<id name="Id" column="role_id">
<generator class="native" />
</id>
<property name="Name" column="user_name" not-null="true" />
</class>
<class name="AccessLevel" table="AccessLevels">
<id name="Id" column="Id">
<generator class="native" />
</id>
<property name="Level" column="level" not-null="true" />
<many-to-one name="User" column="user_id" not-null="true" cascade="save-update" />
<many-to-one name="Role" column="role_id" not-null="true" cascade="save-update" />
</class>

Saving manually created objects using NHibernate

I am trying to use NHibernate to save an object that was completely manually created. My mappings are in place and I currently have no data in the database. Everytime I call Save() or SaveOrUpdate(), NHibernate does a select statement for what I am trying to save. Then it gives me the exception: "a different object with the same identifier value was already associated with the session". Does anyone know how I can tell NHibernate to save my manually instantiated object without thinking that a different object has already been loaded?
Additional Information:
I have a primary mapping with a one-to-many collection. The exception is telling me that "a different object with the same identifier has been loaded", on the collection, not the parent object. I don't know if this provides any useful information. The mappings are as follows:
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" namespace="Program.Application.Models" assembly="Company.Application.Models">
<class name="ProductVersion" table="ClientVersion" lazy="false">
<composite-id>
<key-property name="PracticeName">
<column name="practiceName" not-null="true" />
</key-property>
<key-property name="Address">
<column name="address" not-null="true" />
</key-property>
<key-property name="City">
<column name="city" not-null="true" />
</key-property>
<key-property name="State">
<column name="state" not-null="true" />
</key-property>
<key-property name="Zip">
<column name="zip" not-null="true" />
</key-property>
</composite-id>
<property name="LegalName" column="legalName" />
<property name="Version" column="version" />
<bag name="ProductsLicensesDetail" inverse="true" lazy="false" >
<key>
<column name="practiceName" />
<column name="address" />
<column name="city" />
<column name="state" />
<column name="zip" />
</key>
<one-to-many class="ProductLicenseDetail" />
</bag>
</class>
</hibernate-mapping>
and
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" namespace="Program.Application.Models" assembly="Program.Application.Models">
<class name="ProductLicenseDetail" table="ClientProductLicense">
<id name="ProductCode" column="productCode">
<generator class="assigned" />
</id>
<property name="TotalEnterpriseLicenses" column="totalEnterpriseLicenses" />
<property name="EnterpriseLicensesUsed" column="enterpriseLicensesUsed" />
<property name="TotalPracticeLicenses" column="totalPracticeLicenses" />
<property name="PracticeLicensesUsed" column="practiceLicensesUsed" />
<property name="TotalProviderLicenses" column="totalProviderLicenses" />
<property name="ProviderLicensesUsed" column="providerLicensesUsed" />
<property name="TotalUserLicenses" column="totalUserLicenses" />
<property name="UserLicensesUsed" column="userLicensesUsed" />
<property name="LicenseKey" column="licenseKey" />
<property name="LicenseActivationDate" column="licenseActivationDate" />
<property name="LicenseExpirationDate" column="licenseExpirationDate" />
<many-to-one name="ProductVersion" class="ProductVersion" cascade="none">
<column name="practiceName" />
<column name="address" />
<column name="city" />
<column name="state" />
<column name="zip" />
</many-to-one>
</class>
</hibernate-mapping>
NHibernate is telling me that "a different object with the same identifier value was already associated with the session" for the ProductCode key of the second mapping. Any insight would greatly be appreciated. Thank you.
I believe you will need to add a version field to your composite key class and mapping; see this article for further details.
Have you tried
session.SaveOrUpdateCopy(entity);
session.Flush();
?

With NHibernate, how can I add a child object when updating a parent object?

I have a simple Parent/Child relationship between a Person object and an Address object. The Person object exists in the DB. After doing a Get on the Person, I add a new Address object to the Address sub-object list of the parent, and do some other updates to the Person object. Finally, I do an Update on the Person object. With a SQL trace window, I can see the update to the Person object to the Person table and the Insert of the Address record to the Address table.
The issue is that, after the update is performed, the AddressId (primary key on the Address object) is still set to 0, which is what it defaults to when you first initialize the Address object. I have verified that when I do an Add, this value is set correctly. Is this a known issue when trying to add sub-objects as part of an NHibernate UPDATE? Sample code and mapping files are below
Thanks
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
<class name="BusinessEntities.Wellness.Person,BusinessEntities.Wellness" table="Person" lazy="true" dynamic-insert="true" dynamic-update="false">
<id name="Personid" column="PersonID" type="int">
<generator class="native" />
</id>
<version type="binary" generated="always" name="RecordVersion" column="`RecordVersion`"/>
<property type="int" not-null="true" name="Customerid" column="`CustomerID`" />
<property type="AnsiString" not-null="true" length="9" name="Ssn" column="`SSN`" />
<property type="AnsiString" not-null="true" length="30" name="FirstName" column="`FirstName`" />
<property type="AnsiString" not-null="true" length="35" name="LastName" column="`LastName`" />
<property type="AnsiString" length="1" name="MiddleInitial" column="`MiddleInitial`" />
<property type="DateTime" name="DateOfBirth" column="`DateOfBirth`" />
<bag name="PersonAddresses" inverse="true" lazy="true" cascade="all">
<key column="PersonID" />
<one-to-many class="BusinessEntities.Wellness.PersonAddress,BusinessEntities.Wellness" /
</bag>
</class>
</hibernate-mapping>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
<class name="BusinessEntities.Wellness.PersonAddress,BusinessEntities.Wellness" table="PersonAddress" lazy="true" dynamic-insert="true" dynamic-update="false">
<id name="PersonAddressId" column="PersonAddressID" type="int">
<generator class="native" />
</id>
<version type="binary" generated="always" name="RecordVersion" column="`RecordVersion`" />
<property type="AnsiString" not-null="true" length="1" name="AddressTypeid" column="`AddressTypeID`" />
<property type="AnsiString" not-null="true" length="60" name="AddressLine1" column="`AddressLine1`" />
<property type="AnsiString" length="60" name="AddressLine2" column="`AddressLine2`" />
<property type="AnsiString" length="60" name="City" column="`City`" />
<property type="AnsiString" length="2" name="UsStateId" column="`USStateID`" />
<property type="AnsiString" length="5" name="UsPostalCodeId" column="`USPostalCodeID`" />
<many-to-one name="Person" cascade="none" column="PersonID" />
</class>
</hibernate-mapping>
Person newPerson = new Person();
newPerson.PersonName = "John Doe";
newPerson.SSN = "111111111";
newPerson.CreatedBy = "RJC";
newPerson.CreatedDate = DateTime.Today;
personDao.AddPerson(newPerson);
Person updatePerson = personDao.GetPerson(newPerson.PersonId);
updatePerson.PersonAddresses = new List<PersonAddress>();
PersonAddress addr = new PersonAddress();
addr.AddressLine1 = "1 Main St";
addr.City = "Boston";
addr.State = "MA";
addr.Zip = "12345";
updatePerson.PersonAddresses.Add(addr);
personDao.UpdatePerson(updatePerson);
int addressID = updatePerson.PersonAddresses[0].AddressId;
Try:
updatePerson.PersonAddresses = new List<PersonAddress>();
PersonAddress addr = new PersonAddress();
addr.AddressLine1 = "1 Main St";
addr.City = "Boston";
addr.State = "MA";
addr.Zip = "12345";
updatePerson.PersonAddresses.Add(addr);
addr.Person = updatePerson
A common practice is to expose Add and Remove methods for managing collections, e.g.
public void AddAddress(Address addr)
{
PersonAddresses.Add(addr);
addr.Person = this;
}