NHibernate configuration, foreign keys - c#

I must admit I'm still pretty new to NHibernate, so please go easy on me.
I have 2 POCOs, a Log and a UserProfile. If it is not inherently clear, the log entry can reference a user profile, and a user profile can have many log entries associated with it. The mapping files look like this:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="C3.DataModel.Generated"
namespace="C3.DataModel">
<class name="Log" table="Logs">
<id name="LogId">
<generator class="guid" />
</id>
<property name="TimeStamp" column="TimeStamp" index="ixLogTimeStamp" not-null="false" />
<property name="Thread" length="255" column="Thread" not-null="false" />
<property name="Severity" column="Severity" />
<property name="Source" length="255" not-null="false" column="Source" />
<property name="Message" length="4000" not-null="false" column="Message" />
<property name="Exception" length="4000" column="Exception" not-null="true" />
<many-to-one name="UserProfile" not-null="false" class="UserProfile" foreign-key="UserID" column="UserID" />
</class>
</hibernate-mapping>
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="C3.DataModel.Generated"
namespace="C3.DataModel">
<class name="UserProfile" table="UserProfiles">
<id name="UserID">
<generator class="assigned" />
</id>
<property name="GivenName" length="40" column="GivenName" />
<property name="MiddleName" length="40" column="MiddleName" not-null="false" />
<property name="FamilyName" length="40" column="FamilyName" />
<property name="UserName" length="250" index="ixUserName" unique="true" />
<property name="NickName" length="40" column="NickName" />
<property name="Address1" length="50" column="Address1" />
<property name="Address2" length="50" column="Address2" />
<property name="City" length="50" column="City" />
<property name="State" length="2" column="State" />
<property name="ZipCode" length="10" column="ZipCode" />
<property name="Bio" length="2000" column="Bio" not-null="false" />
<property name="BirthDate" not-null="false" column="BirthDate" />
<property name="LinkToAvatar" length="250" column="LinkToAvatar" not-null="false" />
<property name="LinkToWebpage" length="250" column="LinkToWebPage" not-null="false" />
<bag name="ActivityLogs" cascade="none" table="Logs" >
<key column="LogId" />
<one-to-many class="Log"/>
</bag>
</class>
</hibernate-mapping>
When I'm trying to build my schema against a PostgreSQL database, I receive the following error:
NHibernate.HibernateException: ERROR: 42804: foreign key constraint "fkf9c1212b275a3569" cannot be implemented ---> Npgsql.NpgsqlException: ERROR: 42804: foreign key constraint "fkf9c1212b275a3569" cannot be implemented
at Npgsql.NpgsqlState.<ProcessBackendResponses_Ver_3>d__a.MoveNext()
at Npgsql.ForwardsOnlyDataReader.GetNextResponseObject()
at Npgsql.ForwardsOnlyDataReader.GetNextRowDescription()
at Npgsql.ForwardsOnlyDataReader.NextResult()
at Npgsql.ForwardsOnlyDataReader..ctor(IEnumerable`1 dataEnumeration, CommandBehavior behavior, NpgsqlCommand command, NotificationThreadBlock threadBlock, Boolean synchOnReadError)
at Npgsql.NpgsqlCommand.GetReader(CommandBehavior cb)
at Npgsql.NpgsqlCommand.ExecuteNonQuery()
at NHibernate.Tool.hbm2ddl.SchemaExport.ExecuteSql(IDbCommand cmd, String sql)
at NHibernate.Tool.hbm2ddl.SchemaExport.Execute(Action`1 scriptAction, Boolean export, Boolean throwOnError, TextWriter exportOutput, IDbCommand statement, String sql)
at NHibernate.Tool.hbm2ddl.SchemaExport.Execute(Action`1 scriptAction, Boolean export, Boolean justDrop, IDbConnection connection, TextWriter exportOutput)
at NHibernate.Tool.hbm2ddl.SchemaExport.Execute(Action`1 scriptAction, Boolean export, Boolean justDrop)
--- End of inner exception stack trace ---
at NHibernate.Tool.hbm2ddl.SchemaExport.Execute(Action`1 scriptAction, Boolean export, Boolean justDrop)
at NHibernate.Tool.hbm2ddl.SchemaExport.Execute(Boolean script, Boolean export, Boolean justDrop)
at NHibernate.Tool.hbm2ddl.SchemaExport.Create(Boolean script, Boolean export)
at C3.DataModel.Repositories.NHibernateHelper.CreateSchema() in C:\projects\C3\C3.DataModel.Generated\Repositories\NHibernateHelper.cs:line 41
The SQL it's trying to run at the time of death is:
alter table Logs add constraint FKF9C1212B275A3569 foreign key (LogId) references UserProfiles
...which is clearly not what I was expecting it to do, but I don't know where to fix it. Tell me what I'm doing wrong first, and get some quick rep. If you can explain it so I can understand where I went wrong, I'd be most appreciative.

if you do not want the foreign key then get rid of the following line from your mapping:
<key column="LogId" />
In your question you have not specified what you expect it do.

The answer was simple; I wish the error reflected what was truly wrong.
I was trying to set up a bidirectional relationship by defining it in the mappings for both; this was both unnecessary and incorrect. I removed the
<bag name="ActivityLogs" cascade="none" table="Logs" >
<key column="LogId" />
<one-to-many class="Log"/>
</bag>
element from my UserProfile configuration, and the error went away.

Related

Nhibernate One To Many With Criteria

I've got a class which contains a list of associated objects.
I've map it as follows:
<class name="MyDto" table="`MyView`" mutable="false" lazy="false">
<id name="Id" column="ID" type="int">
<generator class="assigned" />
</id>
<set name="Days" cascade="all-delete-orphan" lazy="false" >
<key column="Id" />
<one-to-many class="MyAssociatedObj"/>
</set>
</class>
<class name="MyAssociatedObj" table="V_SC_NEED" mutable="false" lazy="false" >
<id name="Id" column="`ID2`" type="int">
<generator class="assigned" />
</id>
<property type="DateTime" not-null="true" name="DayDate" column="`Date`" />
<component name="Audit">
<property type="string" not-null="true" length="255" name="Username" column="`USERNAME`" />
<property type="decimal" not-null="true" name="PreviousValue" column="`PrevVal`" />
</component>
</class>
What I wanted to do is to get a list of MyDto Object, filtering by my associated object, MyAssociatedObject.
Which means, for example, if i do:
UnitOfWork.Session.CreateCriteria<MyDto>().List<MyDto>()
I get a list of 42 lines.
Now, I want to filter them by my associated object, by a restriction of between.
the problem is that when i do so:
UnitOfWork.Session.CreateCriteria<MyDto>().CreateAlias("Days", "days").Add(Restrictions.Between("days.DayDate", query.FromDate, query.ToDate));
I'm getting, somehow 684 rows ! (some cartesial product or something).
Any advise?
Many thanks !
You are getting more rows in the second query because you are JOINing with MyAssociatedObj, which seems to be in a many-to-one relationship with your MyDTO class.
If you want to filter out duplicates of MyDTO, you can use the DistinctRootResultTransformer:
UnitOfWork.Session.CreateCriteria<MyDto>().CreateAlias("Days", "days")
.Add(Restrictions.Between("days.DayDate", query.FromDate, query.ToDate))
.SetResultTransformer(
new NHibernate.Transform.DistinctRootEntityResultTransformer());
Hope it helps.

Mapping a non existing column in Nhibernate

I am working with an Oracle legacy database and I am trying to map a relationship with two tables. Unfortunately the company who designed the database used composite keys.
I've got this table (ORDERS) which has a primary key composed on 3 fields: ordernumber (number), version and company.
<?xml version="1.0" encoding="utf-8"?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="BpSalesOrders.Domain"
namespace="BpSalesOrders.Domain">
<class name="Order" table="OCSAORH" mutable="false" where="OCHAMND = 0">
<cache usage="read-only"/>
<composite-id>
<key-property name="Number" column="OCHORDN" type="String" length="10"></key-property>
<key-property name="Ver" column="OCHAMND" type="Int32"></key-property>
<key-property name="Company" column="OCHCOSC" type="String" length="5"></key-property>
</composite-id>
<property name="Reference" column="OCHOCNO" type="String" length="25"></property>
<property name="Date" column="OCHOCDT" type="Int32"></property>
<property name="Status" column="OCHSTA1" type="Char"></property>
<property name="LineOfCreditStatus" column="OCHSTA4" type="Char"></property>
<property name="WareHouse" column="OCHRESP" type="String"></property>
<set name="OrderLines" access="field.pascalcase-underscore" inverse="true" lazy="true" mutable="false">
<key>
<column name="OCLORDN" not-null="true"/>
<column name="OCLAMND" not-null="true" default="0"/>
<column name="OCLCOSC" not-null="true"/>
</key>
<one-to-many class="OrderLine" not-found ="ignore"/>
</set>
<set name="OrderReleases" access="field.pascalcase-underscore" inverse="true" lazy="true" mutable="false">
<key>
<column name="ORLORDINE" not-null="true" />
<column name="ORLSOCIETA" not-null="true"/>
</key>
<one-to-many class="OrderRelease" not-found ="ignore"/>
</set>
</class>
</hibernate-mapping>
There's a relation with OrderLines which works fine cause that table has the same exact key structure.
I am having problem with the OrderReleases relation. That table has got a primary key composed on a OrderNumber, Company and date. There's no version here:
<?xml version="1.0" encoding="utf-8"?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="BpSalesOrders.Domain"
namespace="BpSalesOrders.Domain">
<class name="OrderRelease" table="OCBPORDRIL" mutable="true">
<composite-id>
<key-property name="Company" column="ORLSOCIETA" type ="String" length="5"></key-property>
<key-property name="OrderNumber" column="ORLORDINE" type ="String" length="10"></key-property>
<key-property name="RequestDate" column="ORLDATARICHIESTA" type ="Int32"></key-property>
</composite-id>
<property name="Status" column="ORLSTATUS" type="Char" length="1"></property>
<property name="StatusMessage" column="ORLSEGNALAZIONE" type="String" length="90"></property>
<property name="ProcessingDate" column="ORLDATAELABORA" type ="Int32"></property>
</class>
</hibernate-mapping>
Considering that the relationship is one-to-many I would like to join the table Orders and OrderRelease on OrderNumber and Company. If I try to do that (as in the example) nHibernate throws an exception telling me that the key it's trying to map is composed with 3 fields.
Is there a way to create an non-existing column which wouldn't be persisted so that I can setup my mapping and relations?
Any help will be apreciated.
Ended up creating a view with a fake column.

NHibernate column uniqueness and inheritance

Consider the mapping file below. Both classes have a different notion of what the businessId is, which the object model handles, but it is acceptable to store it as a plain string in one column. I also want them each to have the same relationship with the Allocations set, which is the main reason I want them mapped using inheritance.
While the odds of a business id for a Project being the same as an Account are remote today, it would be ideal if there were a way to make the combination of the businessId and discriminator both part of the natural-id. This is not allowed, maybe for some good reason that I am not seeing.
Can I improve this mapping in some obvious way?
Cheers,
Berryl
<class name="ActivitySubject" table="ActivitySubjects" discriminator-value="BASE_CLASS">
<id name="Id" unsaved-value="0">
<column name="ActivitySubjectId" />
<generator class="hilo" />
</id>
<discriminator column="ActivitySubjectType" type="System.String" />
<natural-id mutable="true">
<property name="BusinessId" length="25" not-null="true" />
</natural-id>
<property name="Description" length="75" not-null="true" />
<set access="field.camelcase-underscore" cascade="all-delete-orphan" inverse="true" name="Allocations">
<key foreign-key="Allocations_Resource_FK">
<column name="ActivityId" />
</key>
<one-to-many class="Allocation />
</set>
<subclass name="Account" discriminator-value="ACCOUNT" />
<subclass name="SProject" discriminator-value="PROJECT" />
Here is the way I finally got both columns to be part of the same unique index:
<discriminator type="System.String" >
<column name="ActivitySubjectType" unique-key="ActivitySubjectTypeBusinessId" />
</discriminator>
<property name="BusinessId" length="25" not-null="true" node="1" unique-key="ActivitySubjectTypeBusinessId"/>
Don't use natural ids for primary keys. I would use a synthetic key, a database generated identity or a GUID for the primary key and add a unique constraint on the combination of BusinessId and ActivitySubjectType.

NHibernate Many-to-Many Mapping not working

I have a Nhibernate mapping file for a simple user/role mapping.
Here are the mapping files:
Users.hbm.xml
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="Sample.Persistence" namespace="Sample.Persistence.Model">
<class name="User" table="Users">
<id name="UserKey">
<generator class="identity"/>
</id>
<property name="UserName" column="UserName" type="String" />
<property name="Password" column="Password" type="Byte[]" />
<property name="FirstName" column="FirstName" type="String" />
<property name="LastName" column="LastName" type="String" />
<property name="Email" column="Email" type="String" />
<property name="Active" column="Active" type="Boolean" />
<property name="Locked" column="Locked" type="Boolean" />
<property name="LoginFailures" column="LoginFailures" type="int" />
<property name="LockoutDate" column="LockoutDate" type="DateTime" generated="insert" />
<property name="Expired" column="Expired" type="Boolean" generated="insert"/>
<set name="Roles" table="UsersRolesBridge" lazy="false">
<key column="UserKey" />
<many-to-many class="Role"
not-found="exception"
column="RoleKey" />
</set>
</class>
</hibernate-mapping>
Role.hbm.xml
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="Sample.Persistence" namespace="Sample.Persistence.Model">
<class name="Role" table="Roles">
<id name="RoleKey">
<generator class="identity"/>
</id>
<property name="Name" column="Name" type="String" />
<set name="Users" inverse="true" table="UsersRolesBridge" lazy="false" >
<key column="RoleKey" />
<many-to-many class="User" column="UserKey" />
</set>
</class>
</hibernate-mapping>
I am able to retrieve roles for each user via NHibernate but when I go to save a new object, the roles are not saved in the Bridge table.
The user is created and insert with no issues. I've checked that the Role collection, a field on the user, is being populated with the proper rolekey before the Session.Save() is called.
There is no exception thrown as well.
UPDATE:
After adding a cascade, there was still no actual insert taking place to the M-2-M table.
The logs from Nhibernate showed the following:
12/22/2010 23:18:11.684 [11] INFO NHibernate.Engine.Cascade
Message: processing cascade NHibernate.Engine.CascadingAction+SaveUpdateCascadingAction for: Sample.Persistence.Model.User
Exception:
12/22/2010 23:18:11.686 [11] INFO NHibernate.Engine.Cascade
Message: done processing cascade NHibernate.Engine.CascadingAction+SaveUpdateCascadingAction for: Sample.Persistence.Model.User
Exception:
12/22/2010 23:18:11.789 [11] INFO NHibernate.Engine.Cascade
Message: processing cascade NHibernate.Engine.CascadingAction+SaveUpdateCascadingAction for: Sample.Persistence.Model.User
Exception:
12/22/2010 23:18:11.792 [11] INFO NHibernate.Engine.Cascade
Message: cascade NHibernate.Engine.CascadingAction+SaveUpdateCascadingAction for collection: Sample.Persistence.Model.User.Roles
Exception:
12/22/2010 23:18:11.814 [11] INFO NHibernate.Engine.Cascade
Message: done cascade NHibernate.Engine.CascadingAction+SaveUpdateCascadingAction for collection: Sample.Persistence.Model.User.Roles
Exception:
12/22/2010 23:18:11.814 [11] INFO NHibernate.Engine.Cascade
Message: done processing cascade NHibernate.Engine.CascadingAction+SaveUpdateCascadingAction for: Sample.Persistence.Model.User
You haven't specified a cascade on the relationship. By default, inserts, updates, and deletes are not cascaded. More information here:
http://ayende.com/Blog/archive/2006/12/02/nhibernatecascadesthedifferentbetweenallalldeleteorphansandsaveupdate.aspx
I generally find that many to many issues are caused by not setting the cascade properties properly.
Try setting the cascade = "save-update" attribute on the Roles set in the Users.hbm.xml file
<set name="Roles" table="UsersRolesBridge" lazy="false" cascade="save-update">

NHibernate save object, a column is null

the database has two table,one is mb_user table and other is mb_comment table。A user have some comments.When I insert a comment into mb_comment ,the comment_user_id of mb_comment is null.I can't save value of comment_user_id.Please help me!
Map file
mb_user Map file
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="MiserBlogData" namespace="MiserBlogData.Entities" default-lazy="false">
<class name="A_Person" table="mb_user" discriminator-value="0">
<id name="Id" column ="user_id" >
<generator class ="native"/>
</id>
<discriminator column="user_role" type="int" />
<property name="State" column="user_state" />
<property name ="Name" column="user_name" />
<property name ="Pwd" column="user_pwd" />
<property name ="CDate" column="user_cdate" />
<property name ="UDate" column="user_udate" />
<property name ="Role" column="user_role" />
<property name ="Face" column="user_face" />
<bag name="CommentList" >
<key column="comment_user_id" foreign-key="FK_PersonComment" />
<one-to-many class="Comment" />
</bag>
</class>
</hibernate-mapping>
mb_comment Map file
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="MiserBlogData" namespace="MiserBlogData.Entities">
<class name ="Comment" table="mb_comment">
<id name="Id" column ="comment_id">
<generator class ="native"/>
</id>
<property name ="ArticleId" column="comment_article_id" />
<property name ="Content" column="comment_content" />
<property name ="State" column="comment_state" />
<property name ="CDate" column="comment_cdate" />
<many-to-one name="Person" column="comment_user_id" class="A_Person" not-null="true" foreign-key="FK_PersonComment" />
</class>
</hibernate-mapping>
C# code
protected ISession _session = NHibernateHelper.GetCurrentSession();
public virtual object Save<T>(T obj) where T : class
{
return _session.Save(obj);
}
error :not-null property references a null or transient valueMiserBlogData.Entities.Comment.Person
I know,if I remove not-null="true" in mb_comment file ,system is good.But comment_user_id column is null.How to solve?
You should assign the property Person on your Comment to a valid A_Person instance. Then NHibernate will know which user id to write in the table.

Categories

Resources