I'm afraid to even ask this question really, as I find the whole thing pretty disgusting myself. But, what are you going to do with a legacy database.
I have the following three tables
Generator Alarm AlarmDescription
--------- ----- ----------------
Id Id
Id <- GensetId DescriptionText
EventTypeId -> AlarmCode
PanelId ----------------> PanelId
If it's not clear from the above rendering, I have an Alarm, which has both a Genset and an AlarmDescription. The Genset is directly mappable via the GensetId property. The AlarmDescription should also be easily mappable off the Id property right? But it wasn't designed that way, and instead is mapped off a composite of (AlarmCode, PanelId) (note, they don't even share the same field name, found this out after struggling to find any relation until inspecting the data).
So, how would you map this using Fluent NHibernate? I've tried a couple variations, but have failed. Something like the following would be ... ideal, but I don't think anything like this is necessarily directly available.
References(x => x.AlarmDescription)
.Column("AlarmCode", m => m.EventTypeId)
.Column("PanelId", m => m.Genset.PanelId)
Have you tried formulas? (Sorry, I am not using fluent.)
<many-to-one name="AlarmDescription">
<column name="EventTypeId" />
<formula>(select g.PanelId from Generator g where g.Id = GensetId)</formula>
</many-to-one>
This requires declaring AlarmDescription primary key as being the composite id (AlarmCode, PanelId).
If you need to preserve AlarmDescription id, then add in it a natural-id as a component:
<class name="AlarmDescription">
<id name="Id">
<generator .../>
</id>
<natural-id>
<component name="AlarmDescriptionNaturalId">
<property name="AlarmCode" />
<property name="PanelId" />
</component>
</natural-id>
...
And reference it as the key for your relation in Alarm thanks to property-ref:
<many-to-one name="AlarmDescription" property-ref="AlarmDescriptionNaturalId">
<column name="EventTypeId" />
<formula>(select g.PanelId from Generator g where g.Id = GensetId)</formula>
</many-to-one>
Related
I've got a legacy schema with a main table and secondary table, where the secondary table connects to the main table by having the same primary key (a Secondary doesn't not necessarily exist for a given Main). I've been searching up-and-down for XML mappings that will make this work but haven't found anything that works for me.
<class name="Secondary" table="Secondary" lazy="true" dynamic-insert="true" dynamic-update="true">
<id name="mainId" type="Int32">
<column name="MAIN_ID" not-null="true" />
<generator class="foreign">
<param name="property">Main</param>
</generator>
</id>
<one-to-one class="Main" name="Main" constrained="true" />
</class>
<class name="Main" table="Main" lazy="true" dynamic-insert="true" dynamic-update="true">
<one-to-one name="Secondary" cascade="all-delete-orphan" class="Secondary" />
Also tried on the Main side, still no go. It doesn't necessarily break, but it certainly doesn't do what I expect. For example:
session.Query<Main>().Count(m => m.Secondary != null) generates
select
cast(count(*) as INT) as col_0_0_
from
MAIN main0_
where
main0_.MAIN_ID is not null
Note that it's using the MAIN_ID from the MAIN table and is ignoring Secondary altogether.
This issue is fixed in NHibernate 5.3
It's a known issue (PR with suggested fix is here).
For now as a workaround in LINQ you can call Count on some non-ID and not-nullable property:
session.Query<Main>().Count(m => m.Secondary.NotNullableProperty != null)
If such property doesn't exist (or you just want to easily find all such hacky usages in future) you can additionally map your Id column as read-only property and use it instead:
<property name="ForceJoinId" not-null="true" column="MAIN_ID" insert="false" update="false" />
session.Query<Main>().Count(m => m.Secondary.ForceJoinId != null)
I know it has been asked for many times, i also have found a lot of answers on this website, but i just cannot get out this problem.
Can anyone help me with this piece of code?
Many thanks.
Here is my parent mapping file
<set name="ProductPictureList" table="[ProductPicture]" lazy="true" order-by="DateCreated" inverse="true" cascade="all-delete-orphan" >
<key column="ProductID"/>
<one-to-many class="ProductPicture"/>
</set>
Here is my child mapping file
<class name="ProductPicture" table="[ProductPicture]" lazy="true">
<id name="ProductPictureID">
<generator class="identity" />
</id>
<property name="ProductID" type="Int32"></property>
<property name="PictureName" type="String"></property>
<property name="DateCreated" type="DateTime"></property>
</class>
Here is my c# code
var item = _productRepository.Get(productID);
var productPictrue = item.ProductPictureList
.OfType<ProductPicture>()
.Where(x => x.ProductPictureID == productPictureID);
// reomve the finding item
var ok = item.ProductPictureList.Remove(productPictrue);
_productRepository.SaveOrUpdate(item);
ok is false value and this child object is still in my database.
Not 100% sure, but could be because you have defined ProductID as a property of ProductPicture, I assume this is the PK from the Product class. You don't need to add this again, it will be created by the relationship.
I'm not sure that your use of table="[ProductPicture]" in the set tag is right.
The one-to-many tag already establishes the link between ProductPictureList and ProductPicture.
I think the table attribute is generally for using a separate relationship table when modelling many-to-may relationships.
From nhibernate.info Doc:
table (optional - defaults to property name) the name of the
collection table (not used for one-to-many associations)
And:
A collection table is required for any collection of values and any
collection of references to other entities mapped as a many-to-many
association
Let's start with this mapping:
<component name="Location">
...
<property name="Settings" type="JsonUserType,...">
<column name="LocationSettingsType" />
<column name="LocationSettingsData" />
</property>
</component>
This maps to
TABLE Primary (
...
LocationSettingsType,
LocationSettingsData
...
)
and
class Location {
...
object Settings { get; set; }
}
Now, I want to extract settings into a separate table (because they are seldom here).
So I get
TABLE Primary (
...
LocationSettingsId,
...
)
TABLE Settings (
Id,
Type,
Data
)
Can I keep my C# classes the same?
Update: This is not a many-to-one relationship. As before, each location has zero or one settings, and each settings belong to at most one location.
I believe the closest thing that exists to this is the <map> mapping element; details are explained in this article.
If you want a one to many relationship on the Primary and Settings tables, you'll have to set a foreign key constraint first. Then you'll use the bag property in XML to map your tables. You will have an entity for each table.
See also this question on NHibernate/FluentNHibernate Property Bag.
I also recommend you purchase the NHibernate 2 for Beginners book. It helped me alot.
This is an old question but i had the same issue and looking to a solution I came here.
the component element can map several columns to several object models.
the join element can map several tables to an object model.
The main problem is that while the component cannot map columns from a different table where the model belong, the join cannot map the different table columns to a different object model.
The solution I found is to use both to achieve the map column of a different table to several object:
<class name="Primary" table="Primary">
<id name="Id">
<generator class="identity"/>
</id>
<property name="Name" />
...
<join table="Settings">
<key column="PrimaryId"/>
<component name="Location">
...
<property name="Settings" type="JsonUserType,...">
<column name="LocationSettingsType" />
<column name="LocationSettingsData" />
</property>
</component>
</join>
</class>
Reference:
NHibernate Join mapping element
NHibernate Component mapping element
I am having a problem when trying create a many to many mapping. Consider the following tables:
CREATE TABLE [dbo].[student]
(
[Id] INT IDENTITY (1, 1) NOT NULL,
[Name] NVARCHAR(255) NOT NULL,
-- Some other stuff...
)
CREATE TABLE [dbo].[Subject]
(
[Id] INT IDENTITY (1, 1) NOT NULL,
[Name] NVARCHAR (50) NOT NULL,
-- Some other stuff...
)
CREATE TABLE [dbo].[studentToSubject]
(
[studentId] INT NOT NULL,
[subjectId] INT NOT NULL,
)
The interesting part of my student mapping file looks like this:
<id name="Id" type="Int32">
<column name="Id" sql-type="int" not-null="true" unique="true"/>
<generator class="native" />
</id>
<property name="Name" not-null="true" />
<bag name="subjects" table="studentToSubject">
<key column="studentId"></key>
<many-to-many column="subjectId" class="subject" />
</bag>
I want to end up with a student with a collection of their subjects. However, I get an error:
NHibernate.MappingException: Could not determine type for: MyApp.Domain.Subject, MyApp.Domain, Version=1.0.0.0, Culture=neutral, PublicKeyToken=865c2d2b185d0c4b, for columns: NHibernate.Mapping.Column(studentId).
I have seen some examples of this type of mapping, but they differ in the fact that their Id columns have name that match the mapping table name, for example their Id column in the student table is called 'studentId'. I cannot do this (it has to be Id) but I think this is the cause of the problem.
Thanks
you should write this relation for subject class again and sure your field for relation is correct.
i do it with attribute models
It seems like you are either missing the mapping file for Subject (did you remember to include it properly?) or you need to provide the full path if it is in a different namespace.
Please correct me if I'm wrong, but I guess there's something messy about your Id mapping. Perhaps it can be done the way you do and I have never seen it, this is possible.
Though I would write my mapping like so:
<class name="Student"> <!-- I omit the table attribute as both your class and table seems to have the same name. -->
<id name="Id">
<generator class="native"/> <!-- Though I would recommend using "identity" if SQL Server's used. -->
</id>
<property name="Name" length="255" not-null="true"/>
<list name="Subjects" not-null="true" table="StudentToSubject">
<key column="studentId" />
<many-to-many column="studentId" class="Subject" />
</list>
</class>
Within the element, it is optional to specify the not-null, unique, type and sql-type attributes as NHibernate will determine them during runtime using reflection, though I understand that for pedagogy purposes, it is better to write those. Plus, if you want your Id property name within your object class be the same as your table field, you may just omit the column attribute. NH will then consider using the same name as the property for the data table field Id field.
As for your collection of subjects, if you intend to use a Dictionary in your Sudent class, you'd better the element instead of . However, if you want a List, you'd better opt for the element as I did. This all depends on your needs and your objective through this exercise.
Please, consider that I took this NH XML mapping right from the top of my head, and I didn't test it, so it might contain errors on fly.
In addition to this, you can take an eye out on this: Chapter 6. Collection Mapping
Hope this helps! =)
Did you remember to set the .hbm.xml mapping files to an Embedded Resource?
Also this line is not correct.
<many-to-many column="subjectId" class="subject" />
Subject should be capital S and it is good practice to give the namespace and assembly. such as
<many-to-many column="subjectId" class="MyApp.Domain.Subject, MyApp.Domain" />
I might be mistaken but the error hints at it:
NHibernate.MappingException: Could not determine type for: MyApp.Domain.Subject, MyApp.Domain, Version=1.0.0.0, Culture=neutral, PublicKeyToken=865c2d2b185d0c4b, for columns: NHibernate.Mapping.Column(studentId).
Basically the way i read the error, NHibernate can't figure out what type the column studentId is, The fault is most likely in your Subject mapping. There is a property there which apperently references a student (i'm guesing the other side of the many-to-many).
I'm having trouble with something that (I think) should be simple, but can't find any clear info.
In the scenario where I have three tables, describing a domain where a person can have more than one job:
Person - has PersonId, Name
Job - has JobId, JobName
PersonJob - has PersonId, JobId, YearsOfEmployment
Note: In my object model, I have entities representing each table. I have that third entity to represent the Person/Job relationship since there is useful metadata there (YearsOfEmployment) and is not just a simple join table.
So, if I knew the PersonId and the JobId, is there a simple way for me to use the session and return an object matching those Ids?
Or, put a different way, since I already know the primary keys is there a brain-dead, simple way I can turn the SQL "SELECT YearsOfEmployment FROM PersonJob WHERE PersonId=1 AND JobId=1" into something like:
var keys = new {PersonId=1, JobId=2};
PersonJob obj = Session.Get<PersonJob>(keys);
BTW: maps would look something like this:
<class name="Person" table="dbo.Person" lazy="true">
<id name="PersonId">
<generator class="native"/>
</id>
<property name="Name"/>
</class>
<class name="Job" table="dbo.Job" lazy="true">
<id name="JobId">
<generator class="native"/>
</id>
<property name="JobName"/>
</class>
<class name="PersonJob" table="dbo.PersonJob" lazy="true">
<composite-id>
<key-property name="PersonId"></key-property>
<key-property name="JobId"></key-property>
</composite-id>
<property name="YearsOfEmployment"/>
</class>
Well, I answered my own question. I think posting your problem is almost as cathartic as talking it out with someone. If I were to make the composite-id of PersonJob a component or class, i.e.
<class name="PersonJob" table="dbo.PersonJob" lazy="true">
<composite-id name="PersonJobKey" class="PersonJobKey">
<key-property name="PersonId"></key-property>
<key-property name="JobId"></key-property>
</composite-id>
</class>
Then I can simply do this:
PersonJobKey key = new PersonJobKey() { PersonId = 1, JobId = 1 };
PersonJob obj = Session.Get<PersonJob>(key);
int yearsOfEmployment = obj.YearsOfEmployment;
cool. hope this helps anyone else figuring this out ...
Thanks for posting the answer above, I was looking at it against an object I have mapped where the composite key doesn't have a name or class. When I tried making a class to represent the composite key, it changed the way that the object behaved when used by other code. Also I wanted to write something like;
Session.Get<SalesRepArea>(new { AreaCode = "ACode", RegionCode = "RCode"});
I found that nHibernate couldn't make much sense of the anonymous object, but I did realise that I don't need a name for my composite key, or a class type. What the nHibernate Get method is after, in fact, is a transient object so that it can get it's equivalent object from the database (must be why you have to override the equals method in your C# class to get the composite key to work). So for the following map
<class name="SalesRepArea">
<composite-id>
<key-property
name="AreaCode" column="AreaCode" type="String" length="12" />
<key-property
name="RegionCode" column="RegionCode" type="String" length="12" />
</composite-id>
I write a bit less code, and dispense with the object representing the key to get
SalesRepArea myArea = Session.Get<SalesRepArea>(
new SalesRepArea()
{
AreaCode = "ACode",
RegionCode = "RCode"
}
);
I'm not saying that the named key method is bad, less code is not always better, it's just to show that Hibernate is looking for the object that the key is in to get the specific object from the database.
If I've got it wrong please let me know, but I hope this helps, as I was having a bit of trouble with this.
Thanks,
Mark