Detail is a value-object (component) not an entity.
On Fri, Dec 17, 2010 at 7:34 PM, gregmac <[email protected]> wrote:
> I'm trying to do a one-to-many mapping, from a Master object which
> contains a .Details collection with a set of Detail objects. In my
> domain model, this only needs to be "uni-directional": that is, Detail
> does NOT have a .Master property that refers back to its master. I
> never need to have a Detail object by itself, nor will I ever load a
> single Detail object -- it will always be accessed from Master first.
>
> For a concrete example, consider User and UserPreferences. I don't
> need to access a UserPreference by itself, I will always access it in
> the context of a User (via User.Preferences). Contrast this to a
> different type of one-to-many relationship, like between User and
> BlogPost. A BlogPost is interesting by itself, and will often be the
> entity the application is loading (the main page of a blog, for
> example) - and so having a bi-directional relationship (so I have both
> BlogPost.User and User.BlogPosts) makes sense.
>
> Here's what the classes look like for my example:
>
> public class Detail {
> public virtual Guid DetailId { get; set; }
> public virtual string Name { get; set; }
> }
> public class Master {
> public virtual Guid MasterId { get; set; }
> public virtual string Name { get; set; }
> public virtual IList<Detail> Details { get; set; }
> }
>
>
> The Master database table is:
>
> masterId uniqueidentifier NOT NULL
> name nvarchar(max) NULL
>
> and Detail is:
>
> DetailId uniqueidentifier NOT NULL
> MasterId uniqueidentifier NOT NULL
> name nvarchar(max) NULL
> foreign key (masterId) references [Master]
>
>
>
> So what I'd like to be able to do is this:
>
> Master mast = new Master {
> MasterId = new Guid(),
> Name = "test",
> Details = new List {
> new Detail {
> DetailId = new Guid(),
> Name = "Test1"
> },
> new Detail {
> DetailId = new Guid(),
> Name = "Test1"
> }
> }
> };
>
> using (transaction == Session.BeginTransaction) {
> Session.Save(mast);
> transaction.Commit();
> }
>
>
> The problem is, the Save will fail -- because NH tries to:
> * Insert the Master object
> * Insert the Detail object, but with MasterId = NULL
> * Update the Detail object with the correct MasterId.
>
> It fails on the second step because the database doesn't allow
> MasterId of NULL.
>
>
> For reference, here's the mapping (fluent) I'm using:
>
> public class MasterMap : ClassMap<Master> {
> public MasterMap() {
> Id(x => x.MasterId).GeneratedBy.Guid();
> Map(x => x.Name);
> }
> }
> public class DetailMap : ClassMap<Detail> {
> public DetailMap() {
> Id(x => x.Id).GeneratedBy.Guid();
> Map(x => x.Name);
> HasMany(x => x.Details).Not.KeyNullable.Cascade.All();
> }
> }
>
>
>
> I can fix this by adding a reference backwards:
>
> Add References(x => x.Master).Column("masterId") to the DetailMap;
> add a Detail.Master property; set the Master.Details as a .Inverse()
> relation; and set the Detail.Master property whenever adding a Detail
> to a Master.Details collection.
>
> Less than ideal, because now I need to do a bunch of "overhead work"
> in my business code (setting Detail.Master), not to mention it makes
> it prone to error if I don't do that, or somehow it gets set to a
> different ID than the collection it actually belongs to, etc.
>
> (also note, in case anyone with a similar problem is reading this:
> without .Inverse(), it will only do the INSERT/UDPATE
> behaviour. .Invese() is needed to make it set MasterId in the initial
> INSERT.
>
>
> One way to at least abstract this from my client code is to have my
> own DetailCollection type, and override the .Add method so it sets
> Detail.Master automatically.. but this won't work if I load an
> existing Master object, as NHibernate will set it to be a
> PersistentGenericBag<Detail> instead of my own DetailCollection type.
>
>
> Is it a bug that NH doesn't support calling Save() as I did in my
> first example? Is it simply a side-effect of supporting different
> types of ID generation on different databases, and if so, can this be
> fixed? Is it an intended design decision ,and if so, what's the
> rationale behind it? I know this is documented at
> http://www.nhforge.org/doc/nh/en/index.html#collections-onetomany (it
> says you need to use a bi-directional relationship), but it doesn't
> explain the "why" behind this.
>
> On the flip side, if there is a legitimate reason behind this and/or
> it can't be fixed, what is the proper pattern I should be using? Is
> there something nicer than using a bi-directional relation and
> requiring all my client code to have to do:
>
> detail.Master = mast;
> mast.Details.Add(detail);
>
>
> Thanks
>
> --
> You received this message because you are subscribed to the Google Groups
> "nhusers" group.
> To post to this group, send email to [email protected].
> To unsubscribe from this group, send email to
> [email protected]<nhusers%[email protected]>
> .
> For more options, visit this group at
> http://groups.google.com/group/nhusers?hl=en.
>
>
--
Fabio Maulo
--
You received this message because you are subscribed to the Google Groups
"nhusers" group.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to
[email protected].
For more options, visit this group at
http://groups.google.com/group/nhusers?hl=en.