References vs HasOne

697 views
Skip to first unread message

Fregas

unread,
Aug 4, 2009, 12:40:17 PM8/4/09
to Fluent NHibernate
Hi,

I'm not sure how to do this in fluent and whether I need HasOne or
References. Basically in my tables, I want a one to many relationship
between the UserAccounts table and the CreditCard table, so that as
far as the database is concerned, a user could have multiple credit
cards:

Create table UserAccount
(
ID uniqueIdentifier not null,
Username nvarchar(255)
...
)

create table CreditCard
(
ID uniqueidentifier not null,
NameOnCard nvarchar(255),
...
UserID uniqueidentifier not null
)

However, in my object model for now, I would like my user account to
have a reference to a SINGLE credit card, and then have nhibernate
look up that credit card by the userID in the credit card table. I
don't want a CreditCard collection or IList.

Is this possible, or is NH just not setup to do it this way? If it
is, how would i map this using Fluent NH?

Thanks,
Craig

Mikael Henriksson

unread,
Aug 4, 2009, 12:49:27 PM8/4/09
to fluent-n...@googlegroups.com
The whole point of NHibernate as I see it is to stop thinking in tables. You can create a decent model from pretty much any table structure you want. If you do it fluently you would have to solve it with HasOne(x => x.CreditCard); inside the ClassMap

2009/8/4 Fregas <fre...@gmail.com>

Fregas

unread,
Aug 4, 2009, 1:06:45 PM8/4/09
to Fluent NHibernate
Hi Mikael,

I tried that HasOne but then when I loaded up the User it couldn't
load the CreditCard. It was always null.

I think there ARE times you should think about the table structure.
Your object model should come first, but you might want your table
structure in a certain way as I do so that reporting is easier, the db
structure makes sense to non-object people like DBA's etc. I just
don't want my tables dictating my object model or vice-versa.

Craig

Mikael Henriksson

unread,
Aug 4, 2009, 1:11:16 PM8/4/09
to fluent-n...@googlegroups.com
Got two questions for you:
1. What does your two classes look like?
2. What does your mapping look like?

2009/8/4 Fregas <fre...@gmail.com>

James Gregory

unread,
Aug 4, 2009, 3:48:43 PM8/4/09
to fluent-n...@googlegroups.com
HasOne == one-to-one
References == many-to-one

Fregas

unread,
Aug 4, 2009, 4:24:36 PM8/4/09
to Fluent NHibernate
Ok, this is a bit complicated because I have a base class for all
domain objects and a base class for all mappings that maps the PK as a
Guid ID column:


public class DomainObject : IDomainObject
{
public virtual Guid ID { get; protected set; }

private bool isNew = true;
public virtual bool IsNew { get { return this.isNew; } protected set
{ this.isNew = value; } }
public virtual DateTime DateCreated { get; protected set; }
public virtual DateTime DateUpdated { get; protected set; }

public override bool Equals(object obj)
{
var persistentObject = obj as DomainObject;
return (persistentObject != null) && (ID == persistentObject.ID);
}

protected virtual void OnSave()
{
this.isNew = false;
}
}

public abstract class MapBase<T> : ClassMap<T> where T:IDomainObject
{
public MapBase()
{
Id(x => x.ID).GeneratedBy.GuidComb();
Map(x => x.DateCreated);
Map(x => x.DateUpdated);
this.CreateMappings();
}

public abstract void CreateMappings();
}


public class UserAccount : DomainObject
{

public virtual string FirstName { get; set; }
public virtual string LastName { get; set; }
public virtual string CompanyName { get; set; }
public virtual string Email { get; set; }
public virtual string Phone { get; set; }
public virtual string AltPhone { get; set; }
public virtual string Username { get; set; }
public virtual string Password { get; set; }
public virtual IDictionary<AddressType, Address> Addresses { get;
protected set; }

public virtual CreditCard CreditCard { get; set;}

public virtual Address PrimaryAddress
{
get
{
return this.Addresses[AddressType.Primary];
}
set
{
this.Addresses[AddressType.Primary] = value;
}
}

public virtual Address BillingAddress
{
get
{
return this.Addresses[AddressType.Billing];
}
set
{
this.Addresses[AddressType.Billing] = value;
}
}

public override string ToString()
{
return FirstName + " " + LastName;
}

public virtual string FullName { get { return ToString(); } }

public UserAccount()
{

this.CreditCard = new CreditCard();

this.Addresses = new Dictionary<AddressType, Address>();

if (this.Addresses != null) //here for nhibernate proxy
{
var primary = new Address(AddressType.Primary);
var billing = new Address(AddressType.Billing);

this.Addresses.Add(AddressType.Primary, primary);
this.Addresses.Add(AddressType.Billing, billing);
}

}


}


public class CreditCard : DomainObject
{
public virtual string NameOnCard { get; set; }
public virtual string CardNumber { get; set; }
public virtual string SecurityCode { get; set; }
public virtual DateTime ExpirationDate { get; set; }
public virtual CardType CardType { get; set; }
}

public class CardType : DomainObject
{
public virtual string Name { get; set; }
}



public class UserAccountMap : MapBase<UserAccount>
{

public override void CreateMappings()
{
Map(x => x.FirstName);
Map(x => x.LastName);
Map(x => x.CompanyName);
Map(x => x.AltPhone);
Map(x => x.Phone);
Map(x => x.Email);
Map(x => x.Username);
Map(x => x.Password);

HasOne(x => x.CreditCard).Cascade.All();
HasMany(x => x.Addresses).AsMap(x=>x.Type).Cascade.All();
}
}

public class CreditCardMap : MapBase<CreditCard>
{
public override void CreateMappings()
{
Map(x => x.CardNumber);
Map(x => x.ExpirationDate);
Map(x => x.NameOnCard);
Map(x => x.SecurityCode);
References(x => x.CardType).Cascade.SaveUpdate();
}
}



Fregas

unread,
Aug 4, 2009, 6:12:45 PM8/4/09
to Fluent NHibernate
I got it to work. I needed to have a PropertyRef in my UserAccount
mapping and an Owner property and References in the CreditCard and its
mapping. In addition, when I set the credit card on the UserAccount,
i have to set the owner property of the CreditCard thats getting
passed in. this is similar when you have a bi-directional
relationship between a one-to-many and a many-to-one. You usually
need an AddChild() type method that sets the parent when the child
gets added to the collection: Not sure why NH can't just figure this
stuff out based on the mappings instead of me having to do it in the
object model, but it works.

Heres what worked:

public class UserAccount : DomainObject
{

public virtual string FirstName { get; set; }
public virtual string LastName { get; set; }
public virtual string CompanyName { get; set; }
public virtual string Email { get; set; }
public virtual string Phone { get; set; }
public virtual string AltPhone { get; set; }
public virtual string Username { get; set; }
public virtual string Password { get; set; }
public virtual IDictionary<AddressType, Address> Addresses
{ get; protected set; }

private CreditCard creditCard;

public virtual CreditCard CreditCard
{
get { return this.creditCard; }
set
{
this.creditCard = value;
this.creditCard.Owner = this; //have to set the owner in my
object model for some reason
}
}
. . .
}

public class CreditCard : DomainObject
{
public virtual string NameOnCard { get; set; }
public virtual string CardNumber { get; set; }
public virtual string SecurityCode { get; set; }
public virtual DateTime ExpirationDate { get; set; }
public virtual CardType CardType { get; set; }

public virtual UserAccount Owner { get; set; } //the owner in the
credit card, which gets stored as "OwnerID" in the table
}


public class UserAccountMap : MapBase<UserAccount>
{

public override void CreateMappings()
{
Map(x => x.FirstName);
Map(x => x.LastName);
Map(x => x.CompanyName);
Map(x => x.AltPhone);
Map(x => x.Phone);
Map(x => x.Email);
Map(x => x.Username);
Map(x => x.Password);

HasOne(x => x.CreditCard)
.PropertyRef(c=>c.Owner) //again the owner- i guess this sets up
the FK OwnerID on the CreditCard table?
.FetchType.Join()
.Cascade.All();

HasMany(x => x.Addresses).AsMap(x=>x.Type).Cascade.All();
}
}

public class CreditCardMap : MapBase<CreditCard>
{
public override void CreateMappings()
{
LazyLoad();
Map(x => x.CardNumber);
Map(x => x.ExpirationDate);
Map(x => x.NameOnCard);
Map(x => x.SecurityCode);
References(x => x.Owner).Unique().Cascade.SaveUpdate
().LazyLoad(); //not sure if need this references...
References(x => x.CardType).Cascade.SaveUpdate();
}
}

Reply all
Reply to author
Forward
0 new messages