I am a beginner at using Fluent NHibernate.
I am developing a C# application that has to interact with an existing database.Let say I have 2 tables: Items and ItemsList.
Items: ID INT ItemName VARCHAR(100)
ItemsList: ID INT ChildItemID INT
I've built 2 classes and their mapping:
public class Items
{
public virtual int id {get; set;}
public virtual string itemName {get; set;}
}
public class ItemsMap : ClassMap<Items>
{
public ItemsMap()
{
Id(x => x.id).GeneratedBy.Increment();
Map(x => x.itemsName);
}
}
public class ItemsList()
{
public virtual int id {get; set;}
public virtual IList<Items> childItems {get; set;}
public ItemsList()
{
childItems = new List<Items>();
}
}
public class ItemsListMap : ClassMap<ItemsList>
{
public ItemsListMap()
{
Id(x => x.id).GeneratedBy.Increment();
HasMany(x => x.childItems).KeyColumn("childID").Cascade.All();
}
}
And finally, I insert an item in the itemsList and save it all:
try
{
using( ISession session = NH.OpenSession())
{
using(ITransaction transaction = session.BeginTransaction())
{
Items i = New Items()
i = session.get<Items>(1);
ItemsList il = new ItemsList();
il.childID.Add(i);
session.SaveOrUpdate(il);
transaction.Commit();
}
}
}
So when I commit, I have a new entry in ItemsList table, but the childID is blank.
Question:
All the examples I see has a reference to ItemsListID in Items table. But I don't want to have this reference since I want the item to be unique in the items table. How can I acheve that?
The NHibernate native way for expressing the unique reference, is:
5.1.12. one-to-one
There are two varieties of one-to-one association:
primary key associations
unique foreign key associations
Primary key associations don't need an extra table column; if two rows are related by the association then the two table rows share the same primary key value. So if you want two objects to be related by a primary key association, you must make sure that they are assigned the same identifier value!...
Other words, Tables would look like this (Table Items generates the value of ItemID, table ItemsList takes that value and stores it in the ItemID ) :
Items: ItemID INT ItemName VARCHAR(100)
ItemsList: ItemID INT
The C# would be (I changed Items into Item and ItemList into ItemMoreDetails, because it is not a list anymore)
public class Item
{
public virtual int ItemId { get; set; }
...
public virtual ItemMoreDetails ItemMoreDetails {get; set; }
public class ItemMoreDetails
{
public virtual int ItemId { get; set; }
...
public virtual Item Item {get; set;}
The mapping would be (in fluent):
// Parent side
public class ItemMap : ClassMap<Item>
{
public ItemMap()
{
Id(x => x.id).GeneratedBy.Increment();
...
HasOne(x => x.ItemMoreDetails).Cascade.All();
// child side
public class ItemMoreDetailsMap: ClassMap<ItemMoreDetails>
{
public ItemMoreDetailsMap()
{
...
References(x => x.parent).Unique();
See the doc:
HasOne / one-to-one
Related
I have a problem with mapping in NHibernate.
The Order table has the Invoice_Id column which is the nullable FK to the Invoice table.
The problem is, when I load an Invoice which Id exists in the Order table, I see that ConnectedOrder property is null, why?
public class Invoice
{
public virtual Order ConnectedOrder { get; set; }
}
public class Order
{
public virtual Invoice ConnectedInvoice { get; set; }
}
public class InvoiceMap : ClassMap<Invoice>
{
public InvoiceMap()
{
this.References(x => x.ConnectedOrder).Nullable();
}
}
public class OrderMap : ClassMap<Order>
{
public OrderMap()
{
this.References(x => x.ConnectedInvoice).Nullable();
}
}
edit
I've changed my classes and mappings like Radim Köhler said, then I found that topic
Fluent NHibernate One-To-Many Mapping
and there was the need to also add:
this.HasMany(x => x.Orders)
.KeyColumn("Invoice_id")
.Inverse()
.Cascade
.AllDeleteOrphan();
and now it works
You may not like it, but the table structure described above, is not representing Entity relations you've created (so called one-to-one).
In case, that one table contains column referencing the another table (FK), we have scenario:
Each Order has exactly one (or null) Invoice. (many-to-one)
Invoice can be referenced by none or one or many Orders. (one-to-many)
That means, that we should express Entities like this:
public class Invoice
{ // many orders could reference us
public virtual IList<Order> Orders { get; set; }
...
public class Order
{ // unchanged
public virtual Invoice ConnectedInvoice { get; set; }
...
And the mapping should be:
public InvoiceMap()
{ // HasMany is one-to-many
this.HasMany(x => x.Orders)
...
}
public OrderMap()
{ // References is many-to-one
this.References(x => x.ConnectedInvoice).Nullable();
...
I have a hierarchy category table like this
Id int,
Description varchar(100),
ParentId int,
Ordinal int,
IsActive bit
I want to fetch all of the Categories from parent to child, so when I called session.get<Category>(id), it already fetched all of their children. Here is my map and class:
class Category
{
public virtual int Id {get; set;}
public virtual string Description {get; set;}
public virtual int ParentId {get; set;}
public virtual int Ordinal {get; set;}
public virtual bool IsActive {get; set;}
}
class CategoryMap : ClassMap<Category>
{
public CategoryMap()
{
Table("TB_CATEGORY");
Id(f => f.Id).GeneratedBy.Native();
Map(f => f.Description);
Map(f => f.ParentId);
Map(f => f.Ordinal);
Map(f => f.IsActive);
}
}
I've searched so many articles, and am still confused when using their solutions because they don't tell me about the table structure and the mappings. Like this one from ayende blog, I think its a good solution, but I can't follow it well enough to apply this in my project.
Could somebody give me a step by step tutorial to achieve this? Are my mapping and class correct?
using the following classes
class Category
{
public virtual int Id {get; private set;}
public virtual string Description {get; set;}
public virtual Category Parent {get; set;}
public virtual bool IsActive {get; set;}
public virtual IList<Category> Children {get; private set;}
public override bool Euqals(object obj)
{
var other = obj as Category;
return other != null && (Id == 0) ? ReferenceEquals(other, this) : other.Id == Id;
}
public override int GetHashCode()
{
return Id;
}
}
class CategoryMap : ClassMap<Category>
{
public CategoryMap()
{
Table("TB_CATEGORY");
Id(f => f.Id).GeneratedBy.Native();
Map(f => f.Description);
References(f => f.Parent).Column("ParentId");
HasMany(f => f.Children)
.AsList(i => i.Column("Ordinal")) // let the list have the correct order of child items
.KeyColumn("ParentId")
.Inverse(); // let the reference maintain the association
Map(f => f.IsActive);
}
}
then you can query
var categoriesWithChildrenInitialised = session.QueryOver<Category>()
.Fetch(c => c.Children).Eager
.List()
This is based on a legacy system.
I have the following tables:
CREATE TABLE a
id int
CREATE TABLE b
a_id int,
c_id int
relationshipid int -- must be IN (1, 2, 3)
CREATE TABLE c
id int
I want the following domain models
public class A
{
public int Id { get; set; }
public C entityc { get ; set; }
}
public class C
{
public int Id { get; set; }
}
Table b is set up so that for a particular defined relationshipid there is (well, should only be) one pair of ids. For other relationships, that one to one mapping through B doesn't hold true. Relationshipid can be one of a small number of values.
How do I get entity C into class A from the relationship where the relationshipid is 1 using fluent NHIbernate?
As a side question, is there a name for what I am trying to do here? The original approach was trying use a HasOne with a Join table and Filter the results, but obviously that failed miserably.
EDIT: Clarified RelationshipID and purpose.
I think the easiest way to map this would be to make your table b an entity and have references to both A and C within that entity and RelationshipId as the id. So your mappings would look something like this:
public class A
{
public int Id { get; set; }
public IList<B> bEntities { get; set; }
}
public class ClassAMap : ClassMap<A>
{
public AMap()
{
Table("A");
Id(x => x.Id);
HasMany(x => x.bEntities)
.KeyColumns.Add("a_id");
}
}
public class B
{
public virtual int RelationshipId { get; set; }
public virtual A InstanceA { get; set; }
public virtual C InstanceC { get; set; }
}
public class ClassBMap : ClassMap<B>
{
public BMap()
{
Table("B");
Id(x => x.RelationshipId , "relationshipid");
References(x => x.InstanceA);
References(x => x.InstanceC);
}
}
Edit:
If your wanting to filter these results for the collection of B entities in your A entity to only ones matching RelationshipId = 1 then you should take a look at this post:
Fluent NHibernate and filtering one-to-many relationship on query requiring multiple joins?
You could also do something like this in your class A:
public class A
{
public int Id { get; set; }
public IList<B> bEntities { get; set; }
public C InstanceC
{
get { return bEntities.First<B>(x => x.RelationshipId == 1).InstanceC; }
}
}
I have two classes: a Site class and a Phase class. The Site class defines a collection of Phases. Each class corresponds to a database table. The database (SQL Server 2000) has a one to many reference between the two tables such that a given Site can be associated with many Phases but a given Phase can only be associated with a single Site.
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
public class Site
{
public virtual int Id {get; set;}
[Required]
[Editable(true)]
[StringLength(64)]
public virtual string Name { get; set; }
public virtual ICollection<Phase> Phases {get; set;}
}
public class Phase
{
public virtual int Id {get; set;}
[Required]
[Editable(true)]
[StringLength(64)]
public virtual string Name { get; set; }
[Editable(true)]
[StringLength(16)]
public virtual string Code { get; set; }
public virtual int SiteId {get; set;}
public virtual Site Site {get; set;}
}
I'm using FluentNHibernate to do my mapping. I want to map this in such away that I can create a new Site instance, assign a few Phase instances and make one call to get all instances into the database:
Site site = new Site() { Name = "SiteName" };
Phase phase = new Phase() { Name = "PhaseName", Code = "Code" };
Phase otherPhase = new Phase() { Name = "OtherPhaseName" };
site.Phases.Add(phase);
site.Phases.Add(otherPhase);
Session.SaveOrUpdate(site);
I have the following mappings in place, but they aren't doing the trick:
public class SiteMap : ClassMap<Site>
{
public SiteMap()
{
Id(p => p.Id).Column("ST_ID").GeneratedBy.Native();
Map(p => p.Name).Column("ST_Name");
HasMany<Phase>(x => x.Phases).KeyColumn("ST_ID").LazyLoad().Inverse().AsSet();
}
}
public class PhaseMap : ClassMap<Phase>
{
public PhaseMap()
{
Id(p => p.Id).Column("PH_ID").GeneratedBy.Native();
Map(p => p.Name).Column("PH_Name");
Map(p => p.Code).Column("PH_Code").Nullable();
Map(p => p.SiteId).Column("ST_ID");
References<Site>(x => x.Site).Column("ST_ID").LazyLoad(Laziness.Proxy).Not.Insert().Not.Update();
}
}
I'm new to NHibernate generally, so I recognize there may be other issues with the mappings shown here that I'm not aware of. Any assistance on how best to map these two classes would be appreciated. TIA.
References<Site>(x => x.Site).Column("ST_ID").LazyLoad(Laziness.Proxy).Not.Insert().Not.Update();
Should just be
References<Site>(x => x.Site).Column("ST_ID").LazyLoad(Laziness.Proxy);
Your Not Insert and Update specifications do not update this column when you're insert or update the table.
You also need to specify Cascade on your one to many end
HasMany<Phase>(x => x.Phases).KeyColumn("ST_ID").Cascade.AllDeleteOrphan().LazyLoad().Inverse().AsSet();
Basic question: How to I create a bidirectional one-to-many map in Fluent NHibernate?
Details:
I have a parent object with many children. In my case, it is meaningless for the child to not have a parent, so in the database, I would like the foreign key to the parent to have NOT NULL constraint. I am auto-generating my database from the Fluent NHibernate mapping.
I have a parent with many child objects like so:
public class Summary
{
public int id {get; protected set;}
public IList<Detail> Details {get; protected set;}
}
public class Detail
{
public int id {get; protected set;}
public string ItemName {get; set;}
/* public Summary Owner {get; protected set;} */ //I think this might be needed for bidirectional mapping?
}
Here is the mapping I started with:
public class SummaryMap : ClassMap<Summary>
{
public SummaryMap()
{
Id(x => x.ID);
HasMany<Detail>(x => x.Details);
}
}
public class DetailMap : ClassMap<Detail>
{
public DetailMap()
{
Id(x => x.ID);
Map(x => x.ItemName).CanNotBeNull();
}
}
In the Detail table, the Summary_id should be Not Null, because in my
case it is meaningless to have a Detail object not attached to the
summary object. However, just using the HasMany() map leaves the Summary_id foreign key nullable.
I found in the NHibernate docs (http://www.hibernate.org/hib_docs/nhibernate/html/collections.html) that "If the parent is required, use a bidirectional one-to-many association".
So how do I create the bidirectional one-to-many map in Fluent NHibernate?
To get a bidirectional association with a not-null foreign key column in the Details table you can add the suggested Owner property, a References(...).CanNotBeNull() mapping in the DetailsMap class, and make the Summary end inverse.
To avoid having two different foreign key columns for the two association directions, you can either specify the column names manually or name the properties in a way that gives the same column name for both directions. In this case you I suggest renaming the Details.Owner property to Details.Summary.
I made the Summary id generated by increment to avoid problems when inserting into the table since Summary currenty has no columns besides id.
Domain:
public class Detail
{
public int id { get; protected set; }
public string ItemName { get; set; }
// Renamed to use same column name as specified in the mapping of Summary.Details
public Summary Summary {get; set;}
}
public class Summary
{
public Summary()
{
Details = new List<Detail>();
}
public int id { get; protected set; }
public IList<Detail> Details { get; protected set; }
}
Mapping:
public class DetailMap : ClassMap<Detail>
{
public DetailMap()
{
Id(x => x.id)
.GeneratedBy.Native();
Map(x => x.ItemName)
.CanNotBeNull();
References<Summary>(x => x.Summary)
// If you don't want to rename the property in Summary,
// you can do this instead:
// .TheColumnNameIs("Summary_id")
.CanNotBeNull();
}
}
public class SummaryMap : ClassMap<Summary>
{
public SummaryMap()
{
Id(x => x.id)
.GeneratedBy.Increment();
HasMany<Detail>(x => x.Details)
.IsInverse()
.AsBag(); // Use bag instead of list to avoid index updating issues
}
}