NHibernate: How to Correctly Model This Schema - c#

I have these related tables in my database:
table [Files]:
FileID FileName
------ --------
1 /data/foo.jpeg
2 /data/bar.gif
table [Attachments]:
FileID DocumentID Caption
------ ---------- -------
1 10 Foo is awesome.
1 20 Foo is horrible.
2 10 Bars are my favorite.
table [Documents]:
DocumentID Title
---------- -----
10 Things Jack loves.
20 Stuff Mary hates.
This is how they are currently mapped in NHibernate:
<class name="File" table="Files">
<id name="Id" type="System.Int32" column="FileID">
<generator class="identity" />
</id>
<property name="FileName" column="FileName" type="System.String" />
</class>
<joined-subclass name="Attachment" table="Attachments" extends="File">
<key column="FileID" />
<property name="DocumentID" column="DocumentID" type="System.Int32" />
<property name="Caption" column="Caption" type="System.String" />
</joined-subclass>
<class name="Document" table="Documents">
<id name="Id" type="System.Int32" column="DocumentID">
<generator class="identity" />
</id>
<property name="Title" column="Title" type="System.String" />
</class>
I know that this mapping does not quite fit the schema for the Attachments table.
Is there a better way to map these tables?
(This is related to my previous question.)

To elaborate on the above answer, you can use the many-to-one element to set up the relationship and use collection mapping to make the relationship bi-directional, which seems like it could be useful to your schema.
You'd use the many-to-one element in the mapping for the attachments. For example,
<many-to-one name="File" class="File" column="FileID"/>
And you can specify the inverse on the mapping for files:
<set name="Attachments" inverse="true" lazy="true">
<key column="FileID" />
<one-to-many class="Attachment" />
</set>
You can do the same thing for the documents. Names above are just taken from the schema, but you'd still need to make sure they match their classes and etc. But that's the overall idea.

You are asking how to enforce relationship between Attachment and Document? If true just refer to "many-to-one" element.

Related

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 - cascade delete is not working

I use NHibernate 3.2 with MS SQL Server 2008 R2
I have the fallowing mappings
<class name="LocalizedProperty" table="LocalizedProperty">
<cache usage="read-write"/>
<id name="Id" column="Id">
<generator class="guid.comb"/>
</id>
<property name="CultureName" not-null="true"/>
<property name="PropertyName" not-null="true"/>
<property name="PropertyValue" not-null="true"/>
<any id-type="Guid" name="Entity">
<column name="LocalizedEntityClass" not-null="true"/>
<column name="EntityId" not-null="true"/>
</any>
</class>
And this one has a Reference to LocalizedProperty:
<class name="CommunicationType" table="CommunicationType" lazy="false" >
...
<set name="LocalizedProperties" where="LocalizedEntityClass = 'Prayon.Entities.CommunicationType'" cascade="delete">
<key column="EntityId" foreign-key="none" />
<one-to-many class="LocalizedProperty" />
</set>
</class>
My Problem is, when I delete an entity of CommunicationType, NHibernate is executing the fallowing update-statement for LocalizedProperty
UPDATE LocalizedProperty SET EntityId = null WHERE EntityId = #p0 AND (LocalizedEntityClass = 'Prayon.Entities.CommunicationType')
Instead of a Delete-Statement.
Does someone see, what is wrong?
To delete child elements of a table you need to specify cascade="all-delete-orphan".
<bag name="TableClassName" table="TableClassName" cascade="all-delete-orphan" >
<key column="PrimaryKey"/>
<one-to-many class="NameSpace.TableClassName" />
If you put a all-delete-orphan the problem is the all part. It will cascade all the actions, not just the delete.

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

retrieve PK of mapped table with NHibernate

just started out with NHIbernate and have one question, probably a bit of a stupi one! ;-)
I have 2 tables, Email and Attachment, an email can have zero or more attachments. so I created a hbm file like this:
<set name="Attachments" table="Attachments">
<key column="EmailId" foreign-key="fk_Attachments_Emails"/>
<composite-element class="Foo.Emails.Attachment, Foo.Emails">
<!-- PROBLEM HERE!!! -->
<property name="Id" column="Id" type="long" />
<!-- END PROBLEM -->
<property name="Name" column="Name" type="String" length="50"/>
<property name="Mime" column="MimeType" type="String" length="50"/>
<property name="Size" column="Size" type="long" />
<property name="FilePath" column="FilePath" type="String" length="256"/>
<property name="Parsed" column="Parsed" type="Boolean" />
</composite-element>
</set>
As I want to be able to search for the attachments by PK (the Id column in the set) I included it, but now everytime I try to save an email with attachments I get an error from the db as Nhibernate tries to insert a value into the PK, which my db naturally wont allow.
So, my question is, can I extract the pk for the Attqachment table but stop Nhiberntate from writing it when inserting a an email/attachment? Should I swap to another container like ?? if so wold you be abler to provide an example as I struggling to find a one that I understand!
THanks for your help!
Perhaps a more practical example? Where you have an object structure like this:
Email
--EmailId
--EmailProperty1
--AttachmentCollection
Attachment
--AttachmentId
--ParentEmail
--AttachmentProperty1
mapped to a table structure like this (not how i'd name it, but it's for example):
email
--emailId int PK, identity
--emailProp1 varchar(50)
emailattachment
--attachmentId int PK, identity
--emailId int, FK to email table
--attachmentProp1 varchar(50)
<hibernate-mapping>
<class name="Email" table="email">
<id name="EmailId">
<!-- this tells NHibernate to use DB to generate id -->
<generator class="native"/>
</id>
<property name="EmailProperty1" column="emailProp1"/>
<bag name="AttachmentCollection" lazy="true" inverse="true">
<key column="emailId"/>
<one-to-many class="Foo.Emails.Attachment, Foo.Emails"/>
</bag>
</class>
<class name="Attachment" table="emailattachment">
<id name="AttachmentId">
<generator class="native"/>
</id>
<property="AttachmentProperty1" column="attachmentProp1"/>
<many-to-one name="ParentEmail" class="Foo.Emails.Email, Foo.Emails" lazy="proxy" column="emailId">
</class>
</hibernate-mapping>
In this map, you'd get the bi-directional relationship, and that generator tag is telling nhibernate that objects with a null Id property (you can also specify another "unsaved-value"), then you're inserting the object, else updating. Should fix your current problem.
Couple other things: examine closely what kind of containers you need to use when mapping (bag vs. set vs. list). There's an excellent writeup in hibernatingrhino's NHibernateFAQ.
Also, since you're new to NHibernate, I very, very greatly recommend the summer of nhibernate screencasts. The best tool I've found so far for learning.
I think you want an bi-directional relationship. This way you can navigate the association both ways. This includes generated keys... Here is an example:
<class name="Order" table="ORDERHEADER" lazy="false" >
<id name="OrderId" column="ORDERID" type="int">
<generator class="sequence">
<param name="sequence">"ORDERID_SEQ"</param>
</generator>
</id>
<property name="OrderType" column="ORDERTYPE" type="Int16"/>
<bag name="OrderDetail" table="ORDERDETAIL" lazy="false" inverse="true">
<key column="OrderId"/>
<one-to-many class="OrderDetail" />
</bag>
<class name="OrderDetail" table="ORDERDETAIL" lazy="false">
<id name="OrderDetailId" column="ORDERDETAILID" type="int">
<generator class="sequence">
<param name="sequence">"ORDERDTLID_SEQ"</param>
</generator>
</id>
<property name="OrderId" column="ORDERID" type="Int32"/>
<property name="ItemNumber" column="ITEMNUMBER" type="Int32"/>
<property name="OrderQuantity" column="ORDERQUANTITY" type="Int32"/>
<many-to-one name="Order" class="Order" column="OrderId" />

Categories