Need access to table with string based primary key in nHibernate - c#

I am trying to map a SQL Server database with nHibernate that is full of tables with varchar primary keys that are generated by external software and I need update/read (no insert) access.
I cannot find a way to get past the following error:
XXXX.Tests.GMCRepository_Fixture.Can_get_existing_GMC_by_parameter'
failed: NHibernate.MappingException :
XXXX.Domain.Mappings.GMC2.hbm.xml(4,6): XML validation error: The element 'class' in namespace 'urn:nhibernate-mapping-2.2' has invalid child element 'property' in namespace 'urn:nhibernate-mapping-2.2'. List of possible elements expected: 'meta, subselect, cache, synchronize, comment, tuplizer, id, composite-id' in namespace 'urn:nhibernate- mapping-2.2'.
Research has suggested this error is relating to there not being a valid primary key (id) defined.
Mapping XML looks like:
<?xml version="1.0" encoding="utf-8"?>
<hibernate-mapping assembly="XXXX.Domain" namespace="XXXX.Domain" xmlns="urn:nhibernate-mapping-2.2" schema="GM.dbo">
<class name="GMC2" table="C2" lazy="true" >
<property name="PARAMETER">
<column name="PARAMETER" sql-type="varchar" not-null="true" />
</property>
...
<id name="Recid">
<column name="recid" sql-type="varchar" not-null="true" />
</id>
</class>
</hibernate-mapping>
Thanks for your help!

I believe that the convention is to have the ID mapping as the first thing under the class declaration.
Also as part of the Id mapping you need to specify the Generator of the ID. In your case I think you will need the assigned generator added to your ID mapping. Your class mapping will look something like this.
<?xml version="1.0" encoding="utf-8"?>
<hibernate-mapping assembly="XXXXCRMAPI.Domain" namespace="XXXXCRMAPI.Domain" xmlns="urn:nhibernate-mapping-2.2" schema="GoldMine.dbo">
<class name="GMContact2" table="CONTACT2" lazy="true" >
<id name="Recid">
<generator type="assigned" />
<column name="recid" sql-type="varchar" not-null="true" />
</id>
<property name="Accountno">
<column name="ACCOUNTNO" sql-type="varchar" not-null="true" />
</property>
...
</class>
</hibernate-mapping>

Related

How to create mapping for additional class inside some class?

I have two classes: Order and User
Order has User class inside of it:
[DataMember]
public virtual User User { get; set; }
I have NHibernate mapping for both of them:
For User:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="Purchasing" namespace="Purchasing.Other">
<class name="User" table="tUser">
<id name="RID">
<column name="RID" sql-type="bigint"/>
<generator class="native"/>
</id>
<property name="Created"/>
<property name="Modified"/>
<property name="UserName"/>
<property name="Email"/>
<property name="ExternalUserId"/>
</class>
</hibernate-mapping>
And I tried to add User class to Order mapping, BUT looks like something is wrong:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="Purchasing" namespace="Purchasing.Other">
<class name="Order" table="tOrder">
<id name="Id">
<column name="OrderId" sql-type="bigint"/>
<generator class="native" />
</id>
<property name="DataOwnerId" />
<property name="UserId" />
<property name="OrderNo"/>
<property name="FirstTaken"/>
<property name="DateRequired" />
<property name="ExternalOrderId" />
<many-to-one name="User" class="User" property-ref="ExternalUserId">
<column name="UserId" not-null="false"/>
</many-to-one>
</class>
</hibernate-mapping>
I have a situation, when, writing Order in tOrder database, I also need to write its User to tUser database.
But looks like it didn't work. (Order mapping without User map works fine).
How in this situation mapping should look correctly, and what am I missing?
P.S Sorry for my bad English.
If we want to persist User with its Order, we can use cascading setting:
5.1.10. many-to-one
An ordinary association to another persistent class is declared using a many-to-one element. The relational model is a many-to-one association. (It's really just an object reference.)
<many-to-one
name="PropertyName" (1)
...
cascade="all|none|save-update|delete" (4)
...
/>
(4) cascade (optional): Specifies which operations should be cascaded from the parent object to the associated object.
So the mapping could be like this:
<many-to-one name="User" class="User"
property-ref="ExternalUserId"
cascade="save-update"
column="UserId" not-null="false"
/>
Now, we only have to be sure, that we try to find a user (it could be existing)
var user = session.Get<User>(userId);
if(user == null)
{
user = new User { ... };
}
And then we can assing that user to the Order
var order = new Order { ... };
order.User = user;
and calling session to persist Order - user will be as well:
session.SaveOrUpdae(order);
NOTE: if you can reference user by its native ID ... it would be better. Mapping then would look like:
<many-to-one name="User" class="User"
cascade="save-update"
column="RID" not-null="false"
/>
Mapping with property-ref should be used exceptionally

Modifying how nHibernate saves Saga data in nServiceBus

I'm having an issue where I'm sending a message to nServiceBus with a message data field of variable length. nServiceBus is using nHibernate to create a table called
[NServiceBus].[PendingMentorEmailSagaData]
from a class called PendingMentorEmailSagaData . The message field is however getting set to nvarchar(255), I am looking for a way to have it set to nvarchar(MAX).
I've tried using an embedded hbm file, but am getting a "persistent class PendingMentorEmailSagaData not found" error.
This probably means I can't figure out what class to set the file too.
The hbm file:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
<class name="PendingMentorEmailSagaData">
<id name="Id" />
<property name="OriginalMessageId" />
<property name="Originator" />
<property name="PendingMentorEmailCommandId" />
<property name="JobBoardCode" />
</class>
</hibernate-mapping>
PendingMentorEmailSagaData is the name of the saga's data class.
Create a PendingMentorEmailSagaData.hbm.xml file in the same project that the saga exists on, eg:
<?xml version="1.0" encoding="utf-8"?>
<hibernate-mapping xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="urn:nhibernate-mapping-2.2">
<class name="MY_NAMESPACE.PendingMentorEmailSagaData, MY_ASSEMBLY_NAME" table="PendingMentorEmailSagaData" dynamic-update="true" optimistic-lock="all">
<id name="Id" type="Guid">
<generator class="assigned" />
</id>
<property name="Originator" />
<property name="OriginalMessageId" />
<property name="LargeText" type="StringClob" />
</class>
</hibernate-mapping>
2. Mark that file as an Embedded Resource

nhibernate subclass - zero guid in not used column instead of null

I have an issue with a two subclasses using the same table and same fields. Even though the generated table has this fields as nullable uniqueidentifiers, when I save the one record that does not use the third column, then the created field has a value of "000000000-0000-0000-0000-000000000000" instead of NULL. Other parts of application require this place to be null. Here's the code. What's wrong?
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly=".."
namespace="...">
<class name="TEST" table="..." discriminator-value="not null">
<id name="Id" column="tID">
<generator class="assigned" />
</id>
<discriminator column="iTestTypeID" type="Int32" not-null="true"/>
<property name="TotalScore" column="fTotalScore"/>
<property name="IsTimedOut" column="bTimedOut"/>
</hibernate-mapping>
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly=".."
namespace="...">
<subclass name="TestA" extends="TEST" discriminator-value="3">
<many-to-one name="Department" column="uTestCriteria01" cascade="save-update" not-null="false" />
<many-to-one name="Building" column="uTestCriteria02" cascade="save-update" not-null="false" />
</subclass>
</hibernate-mapping>
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly=".."
namespace="...">
<subclass name="TestB" extends="TEST" discriminator-value="3">
<many-to-one name="Building" column="uTestCriteria01" cascade="save-update" not-null="false" />
<many-to-one name="Floor" column="uTestCriteria02" cascade="save-update" not-null="false" />
<many-to-one name="Room" column="uTestCriteria03" cascade="save-update" not-null="false" />
</subclass>
</hibernate-mapping>
My guess would be that your objects aren't using nullable types.
Do you have the types defined like this:
public class A
{
public Guid? field { get; set; }
}
Take a look at this StackOverflow article for an explanation:
How can I default a parameter to Guid.Empty in C#?
Also in your question I would list the specific field(s) you are talking about so there is no confusion and post the code for your objects that are tied to this mapping.

NHibernate - getting one-to-many

Wondering if anyone could point me in the right direction..
I currently have the following 3 tables in my sqlserver database
Parameter
ParameterId
ParameterName
ParameterValue
ParameterValueId
ParameterValue
ParameterParameterValue
ParameterId
ParameterValueId
I'm trying to get it where the Parameter domain object will also fetch all the ParameterValue objects as well (I'm guessing Parameter has a one-to-many relationship with ParameterValue, since a parameter can have more than one value) but I'm getting no where - the msot I've achieved is fetching the first value, rather than all :(
If anyone is willing to help or anything I can post some code and/or the mappings I'm using - as always, any help is much appreciated :)
Mappings for Parameter
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
namespace="StockMarketAdvisorDatabaseAccess.Domain"
assembly="StockMarketAdvisorDatabaseAccess">
<class name="Parameter" table="Parameter">
<id name="ParameterId">
<column name="ParameterId" sql-type="int" not-null="true" />
<generator class="identity" />
</id>
<property name="ParameterName" />
<bag name="ParameterValues" table="ParameterParameterValue" cascade="none">
<key column="ParameterValueId" />
<one-to-many class="ParameterValue" />
</bag>
</class>
</hibernate-mapping>
Mappings for ParameterValue
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
namespace="StockMarketAdvisorDatabaseAccess.Domain"
assembly="StockMarketAdvisorDatabaseAccess">
<class name="ParameterValue" table="ParameterValue">
<id name="ParameterValueId">
<column name="ParameterValueId" sql-type="int" not-null="true" />
<generator class="identity" />
</id>
<property name="Value" column="ParameterValue"/>
</class>
</hibernate-mapping>
Thanks again, just started using nHibernate so still trying to figure most it out! :)
Your issue is you are describing one thing (1-many) but your table structure is another (many-many)
If you need many values per parameter then you should simplify your table structure to:
Parameter
-------------
* ParameterId
ParameterName
ParameterValue
--------------------
* ParameterValueId
ParameterId
ParameterValue
Then your mapping can use a 1-many mapping:
<class name="Parameter" table="Parameter">
<id name="ParameterId">
<column name="ParameterId" sql-type="int" not-null="true" />
<generator class="identity" />
</id>
<property name="ParameterName" />
<bag name="ParameterValues" table="ParameterValue" cascade="none">
<key column="ParameterId" />
<one-to-many class="ParameterValue" />
</bag>
</class>

NHibernate Criteria: Add a constraint for property type

I have a program with a base class (Message) from which many other classes derivate (notably Article and Comment). I would like to create a criteria that will select all the Comments for which the RootMessage property is of type Article (I have some other type of potential RootMessages). But I can't seem to find how to add that kind of constraint in a criteria.
For now my code is this:
public virtual ICollection<Comment> GetCommentsByArticle(ISession session)
{
var c = session.CreateCriteria(typeof(Comment))
.Add(Expression.Eq("Author", this))
.AddOrder(Order.Desc("CreationDate"))
.SetMaxResults(20);
var commentList = c.List<Comment>();
return commentList;
}
But of course this returns every single comment of this Author. What I want is every single comment of this Author that have a RootMessage of the Article type.
These are the mappings.
Message:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="FolkeLib"
namespace="Folke.Code.Domain">
<class name="Message" discriminator-value="0">
<id name="Id">
<generator class="native"></generator>
</id>
<discriminator column="MessageType" type="Int32"/>
<timestamp column="ModificationDate" name="ModificationDate"/>
<property name="CreationDate" index="ArticleCreationDate"/>
<many-to-one name="Author"/>
<property name="Text" type="StringClob">
<column name="Text" sql-type="text"/>
</property>
<property name="Locked"/>
<many-to-one name="Locker" not-null="false"/>
<many-to-one name="RootMessage" not-null="false"/>
<many-to-one name="ParentMessage" not-null="false"/>
<property name="Score"/>
<many-to-one name="Site" not-null="true"/>
Article:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="FolkeLib"
namespace="Folke.Code.Domain">
<subclass name="Article" extends="Message" discriminator-value="1">
<property name="Title"/>
<set name="TagSet" lazy="true" table="MessageTag">
<key column="MessageId"/>
<many-to-many class="Tag" column="TagId"/>
</set>
<set name="ImageSet" lazy="true" table="MessageImage">
<key column="MessageId"/>
<many-to-many class="Image" column="ImageId"/>
</set>
<many-to-one name="Forum"/>
</subclass>
</hibernate-mapping>
Comment (class is pretty much empty for now):
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="FolkeLib"
namespace="Folke.Code.Domain">
<subclass name="Comment" extends="Message" discriminator-value="2">
</subclass>
</hibernate-mapping>
Any help appreciated!
This would be easier to answer if you posted your complete mappings, but I believe something along the lines of:
.CreateAlias("RootMessage", "rm")
.Add(Expression.Eq("rm.class",typeof(Article)))
Is what you're looking for.

Categories