NHibernate and multiple databases - c#

I'm trying to solve pretty easy problem. I want to establish connection to 2 totally different databases ( but both mysql ). Now I tried to solve this by creating multiple config files and then creating multiple sessions. Everything works until I reached relations.
I have 2 tables in 2 databases:
db1
- News
db2
- News_Authors
I added News to db1 config and News_Authors to db2 config. When I try to build simple one-to-one relation error, I receive:
An association from the table songs refers to an unmapped class: db1.News_Authors
News.hbm.xml
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
<class name="project.News, project" table="news" lazy="false">
<id name="id" column="id" type="integer" length="11">
<generator class="native" />
</id>
<property name="title" type="String" length="255" />
<property name="authorid" type="integer" length="5" />
<one-to-one name="NewsAuthor" cascade="all-delete-orphan" lazy="proxy" column="authorid" unique="true" />
</class>
</hibernate-mapping>
News_Authors.hbm.xml
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
<class name="project.News_Authors, project" table="news_authors" lazy="false">
<id name="id" column="id" type="integer" length="11">
<generator class="native" />
</id>
<property name="name" type="String" length="255" />
</class>
</hibernate-mapping>
config
I added this to enable mappings. Now If I set both in one config files, everything works...
<mapping resource="project.News.hbm.xml" assembly="project" />
So how could I during creating of session also "notify" nhibernate that I have multiple sessions? Or should I pick totally another approach?

One other possible solution would be to create views in one of the sql server databases to reference the tables in the other. Views map just like tables and its easy to do a view that returns something like:
select * from db2.News_Authors
in the db1 database.
this way you would only need a single .hbm.xml file that maps to one of the two databases.
Hope this helps,
-Max

What you are after is not multiple sessions but multiple session factories. See this for more details.
The key here is that you don't have to initialize your session factory through config file - you can do it programatically. And it's just a step to have two session factories.

Related

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

Need access to table with string based primary key in nHibernate

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>

NHibernate Duplicate class/entity mapping problem

I've have started my foray into C#.NET and NHibernate and I'm finally stuck on an exception I can't seem to figure out, and Google isn't helping.
I'm getting a "NHibernate.DuplicateMappingException : Duplicate class/entity mapping" on my Parent class. Below is my mapping file for the Parent class, and the Youth class that uses the Parent class:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="Surrix.Cerberus.YouthData"
namespace="Surrix.Cerberus.YouthData.Domain">
<class name="Parent">
<id name="parentId">
<generator class="guid" />
</id>
<property name="firstName" not-null="true" />
<property name="lastName" not-null="true" />
<property name="homePhone" />
<property name="parentEmail" />
<property name="relationshipToYouth" />
<!-- Address component that should map to the Address class -->
<component name="parentAddress">
<property name="street" />
<property name="state" />
<property name="zipCode" />
<property name="city" />
</component>
</class>
</hibernate-mapping>
And here is the relevant part of the Youth class (it is considerably bigger)
<set name="YouthParents" table="YouthParents" cascade="none">
<key column="youthId" />
<many-to-many column="parentId" class="Parent"/>
</set>
Only other thing is the Youth class also has the firstName and lastName properties, but I can't see that being a problem.
Make sure you are not doing both of these 2 things.
(1) adding the assembly in code:
// Code Configuration
var cfg = new Configuration();
cfg.Configure();
cfg.AddAssembly(typeof(Employee).Assembly);
// Presuming Employee resides in "MyAssembly" as seen below.
(2) And then also adding the assembly in the config file:
<!-- .config configuration -->
<session-factory>
<!-- bunch of other stuff -->
<mapping assembly="MyAssembly"/> <!-- as in MyAssembly.dll -->
</session-factory>
You are adding the file or assembly containing the mapping twice to your Configuration object.
I had this problem, and solved it by putting this statement in hibernate.cfg.xml file:
<property name="cache.provider_class">org.hibernate.cache.NoCacheProvider</property>
Another possible cause to generate this error is multiple hbm files referencing the same Assembly during a Configuration.AddAssembly.
All hbm files in the same assembly are processed with one AddAssembly call.
As it gives a duplicate class entity mapping I can only imagen that you have two or more *.xml.hbm files referring to the same .net class.
Make sure that the xml class element for your Youth class does not have the same value for the name attribute.

Select latest group by in nhibernate

I have Canine and CanineHandler objects in my application. The CanineHandler object has a PersonID (which references a completely different database), an EffectiveDate (which specifies when a handler started with the canine), and a FK reference to the Canine (CanineID).
Given a specific PersonID, I want to find all canines they're currently responsible for. The (simplified) query I'd use in SQL would be:
Select Canine.*
from Canine
inner join CanineHandler on(CanineHandler.CanineID=Canine.CanineID)
inner join
(select CanineID,Max(EffectiveDate) MaxEffectiveDate
from caninehandler
group by CanineID) as CurrentHandler
on(CurrentHandler.CanineID=CanineHandler.CanineID
and CurrentHandler.MaxEffectiveDate=CanineHandler.EffectiveDate)
where CanineHandler.HandlerPersonID=#PersonID
Edit: Added mapping files below:
<class name="CanineHandler" table="CanineHandler" schema="dbo">
<id name="CanineHandlerID" type="Int32">
<generator class="identity" />
</id>
<property name="EffectiveDate" type="DateTime" precision="16" not-null="true" />
<property name="HandlerPersonID" type="Int64" precision="19" not-null="true" />
<many-to-one name="Canine" class="Canine" column="CanineID" not-null="true" access="field.camelcase-underscore" />
</class>
<class name="Canine" table="Canine">
<id name="CanineID" type="Int32">
<generator class="identity" />
</id>
<property name="Name" type="String" length="64" not-null="true" />
...
<set name="CanineHandlers" table="CanineHandler" inverse="true" order-by="EffectiveDate desc" cascade="save-update" access="field.camelcase-underscore">
<key column="CanineID" />
<one-to-many class="CanineHandler" />
</set>
<property name="IsDeleted" type="Boolean" not-null="true" />
</class>
I haven't tried yet, but I'm guessing I could do this in HQL. I haven't had to write anything in HQL yet, so I'll have to tackle that eventually anyway, but my question is whether/how I can do this sub-query with the criterion/subqueries objects.
I got as far as creating the following detached criteria:
DetachedCriteria effectiveHandlers = DetachedCriteria.For<Canine>()
.SetProjection(Projections.ProjectionList()
.Add(Projections.Max("EffectiveDate"),"MaxEffectiveDate")
.Add(Projections.GroupProperty("CanineID"),"handledCanineID")
);
but I can't figure out how to do the inner join. If I do this:
Session.CreateCriteria<Canine>()
.CreateCriteria("CanineHandler", "handler", NHibernate.SqlCommand.JoinType.InnerJoin)
.List<Canine>();
I get an error "could not resolve property: CanineHandler of: OPS.CanineApp.Model.Canine". Obviously I'm missing something(s) but from the documentation I got the impression that should return a list of Canines that have handlers (possibly with duplicates). Until I can make this work, adding the subquery isn't going to work...
I've found similar questions, such as Only get latest results using nHibernate but none of the answers really seem to apply with the kind of direct result I'm looking for.
Any help or suggestion is greatly appreciated.
Joining to a derived table, CurrentHandler in your example, won't work in HQL the last time I checked. Try mapping a stored procedure that lets you write whatever SQL you like. Here's what a mapped stored procedure looks like:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="S2.BP.NHSupport" namespace="S2.BP.Model">
<sql-query name="spGoGetMyDogs" callable="true">
<return-scalar column="PersonID" type="int" />
exec spGoGetMyDogs #PersonID=:personID
</sql-query>
</hibernate-mapping>
Then you can pass your PersonID parameter in and have NH map the results back to your objects with a transformer like so:
public IEnumerable<Canine> LetTheDogsOut(int personID) {
return nhSession.GetNamedQuery("spGoGetMyDogs")
.SetInt32("personID", personID)
.SetResultTransformer(Transformers.AliasToBean(typeof(Canine)))
.List<Canine>();
}

NHibernate Mapping for User Roles and Privileges

The Scenario
I've been banging my head against the wall trying to figure out the correct mapping for 3 entities: User, Role, and Privilege. In my application, Users can have Privileges, which just give a user additional permissions. Users can also have Roles which are essentially privileges that require additional properties.
For instance, a user might have a role of "Application Administrator" in which case the ApplicationAdministratorRole.cs would need a property to contain the list of applications that a user can manage. A user could also have a privilege of "Event Administrator" in which case Privilege.cs would NOT contain any additional properties for events because in our application an event administrator can manage all events. I hope this example makes sense. If not, I can elaborate a little more.
Table Structure
[Table Name]
TBL_USERS
[Columns]
UserId (PK),
FirstName,
LastName,
CompanyId,
etc...
[Table Name]
TBL_ROLEREF (just defines the roles within the system)
[Columns]
RoleId (PK),
RoleName
[Table Name] TBL_USERROLES (table to cross reference users to roles)
[Columns]
UserRoleId (PK),
UserId,
RoleId,
ActiveDate,
DeactiveDate
[Table Name] TBL_APPLICATIONADMINISTRATORS
[Columns]
ApplicationAdministratorId (PK),
ApplicationId,
UserId,
RoleId,
ActiveDate,
DeactiveDate
[Table Name] TBL_PRIVILEGEREF
[Columns]
PrivilegeId (PK),
PrivilegeName
[Table Name] TBL_USERPRIVILEGES
[Columns]
UserPrivilegeId (PK),
UserId,
PrivilegeId,
ActiveDate,
DeactiveDate
The table structure is pretty straight forward, all privileges and roles have an ActiveDate and DeactiveDate so that we can maintain a history of a users previous roles and privileges. One thing to note is that any role requires an additional table to store any additional information that goes along with this role, in this case, TBL_APPLICATIONADMINISTRATORS will tie a user's Application Administrator role to different applications from TBL_APPLICATIONREF. Again, please let me know if I need to reword this to make better sense.
Mapping Files
[User.hbm.xml]
User objects should have a collection of Privileges and Roles. These bags should probably be sets, but for the sake of this example I don't think it should matter.
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="Core" namespace="Core">
<class name="Core.Entities.User, Core" table="TBL_USERS">
<id name="UserId" column="USERID" type="Int32" unsaved-value="0">
<generator class="sequence">
<param name="sequence">SEQ_TBL_USERS</param>
</generator>
</id>
<property name="Title" column="USERTITLE" type="string" length="50" not-null="false" />
<property name="FirstName" column="USERFIRSTNAME" type="string" length="50" not-null="true" />
<property name="LastName" column="USERLASTNAME" type="string" length="50" not-null="true" />
<bag name="Privileges" generic="true" table="TBL_USERPRIVILEGES">
<key column="USERID" />
<many-to-many column="PRIVILEGEID" class="Core.Entities.Privilege, Core" />
</bag>
<bag name="Roles" generic="true" table="TBL_USERROLES" >
<key column="USERID" />
<many-to-many column="ROLEID" class="Core.Entities.Role, Core" />
</bag>
</class>
</hibernate-mapping>
[Privilege.hbm.xml]
Privilege objects should have a PrivilegeId, PrivilegeName, a collection of users associated with the privilege, and ActiveDate/DeactiveDate. I have commented out my failed attempts to map this.
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="Core" namespace="Core">
<class name="Core.Entities.Privilege, Core" table="TBL_PRIVILEGEREF">
<id name="PrivilegeId" column="PRIVILEGEID" type="Int32" unsaved-value="0">
<generator class="sequence">
<param name="sequence">SEQ_TBL_USERPRIVILEGES</param>
</generator>
</id>
<property name="Name" column="PRIVILEGENAME" type="string" length="128" not-null="false" />
<!--
This does not work. NHibernate is complaining about the repeated Column "USERID"
I have made several attempts to get this to work with no luck... where am I going wrong?
<bag name="Users" generic="true" table="TBL_USERPRIVILEGES" inverse="true">
<key column="USERID" />
<many-to-many column="USERID" class="Core.Entities.User, Core" />
</bag>
-->
<!--
This also does not work. This was my attempt to join the ActiveDate and DeactiveDate into Privilege.cs
The join NHibernate creates with this setup is completely wrong...
<join table="TBL_USERPRIVILEGES">
<key column="USERPRIVILEGEID" />
<property name="ActiveDate" column="ACTIVEDATE" type="DateTime" not-null="false" />
<property name="DeactiveDate" column="DEACTIVEDATE" type="DateTime" not-null="false" />
</join>
-->
</class>
</hibernate-mapping>
[Role.hbm.xml]
I would like to have a Role base class that has properties for ActiveDate and DeactiveDate that each role (like ApplicationAdministratorRole) can inherit from so that every role is forced to have these properties. I suppose I could use an interface to enforce this as well, but this is my first shot at mapping something semi-complicated in NHibernate, so please give me some direction on this.
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="Core" namespace="Core">
<class name="Core.Entities.Role, Core" table="TBL_ROLEREF">
<id name="RoleId" column="ROLEID" type="Int32" unsaved-value="0">
<generator class="sequence">
<param name="sequence">SEQ_TBL_USERROLES</param>
</generator>
</id>
<property name="Name" column="ROLENAME" type="string" length="128" not-null="false" />
<bag name="Users" generic="true" table="TBL_USERROLES">
<key column="ROLEID" />
<many-to-many column="USERID" class="Core.Entities.User, Core" />
</bag>
<joined-subclass name="Core.Entities.ApplicationAdministratorRole, Core" table="TBL_APPLICATIONADMINISTRATORS" extends="Core.Entities.Role, Core">
<key column="ROLEID" />
<property name="ApplicationAdministratorId" column="APPLICATIONADMINISTRATORID" type="Int32" />
<bag name="Applications" generic="true" table="TBL_APPLICATIONREF">
<key column="APPLICATIONID" />
<one-to-many class="Core.Entities.Application, Core" />
</bag>
</joined-subclass>
<!-- Do I need to use <join> here to set the ActiveDate and DeactiveDate in the Role base class? -->
</class>
</hibernate-mapping>
I've done a lot of reading on this stuff, but I'm obviously missing something. As you may have gathered, I am implementing a table-per-subclass strategy for Roles.
These mapping files work as they are, but they do not return the correct results. Any and all help is greatly appreciated.
Thanks guys,
Josh
You are trying to map the Users when using USERID twice, both as the key and as the reference.
You probably mean:
I don't understand this, what is the relation between TBL_PRIVILEGEREF and TBL_USERPRIVILEGES, there doesn't seems to be any:
You join-subclass mapping seems fine

Categories