Got there in the end. I have both methods working, i.e. 3 classes and
2 classes. In this post, it's the 3 class method - Table1 and Table 2
are indirectly related via a link class Table3 (which directly maps to
my DB schema).
So, taking my classes from previous post, below is a modified Table1
and Table3
public class Table1 // Table2 similar to Table1
{
public virtual decimal Id {get;set;}
public virtual string Name {get;set;}
public virtual Table2
{
get
{
result listOfTable3[0].Table2; // obviously with collection
validation!
}
}
protected virtual IList<Table3> listOfTable3 {get; set;} // hidden
away
public Table1()
{
listOfTable3 = new List<Table3>();
}
}
public class Table3 // the link class
{
public virtual Table1 Table1 {get;set;}
public virtual Table2 Table2 {get;set;}
public override bool Equals(object obj) {/* return combined equality
check */}
public override int GetHashCode() {/* return combined hashcode */}
}
You need the Equals and GetHashCode for the .KeyReference() maps
below.
And so the mappings required to get Table1 to reference Table2 via the
link class Table3:
public class Table3Map : ClassMap<Table3> // As per Hudson's reply
{
public Table3Map()
{
Table("Table3");
CompositeId()
.KeyReference(x => x.Table1, "Table1_Id")
.KeyReference(x => x.Table2, "Table2_Id")
}
}
public class Table1Map : ClassMap<Table1> // Table2Map similar
{
public Table1Map()
{
Table("Table1");
Id(x => x.Id, "Table1_Id");
Map(x => x.Name, "Name");
HasMany<Table3>(Reveal.Property<Table1>("listOfTable3"))
.Table("Table3")
.KeyColumn("Table1_Id")
.Inverse();
}
}
The use of Reveal.Property is so that I can hide away the listOfTable3
collection as a protected property. Much neater.
I think that's all correct, my implementation certainly works, but
please note that I have only implemented this for reading data so
far. I haven't attempted to insert new records into any of the
tables. That's where one of these methods may prove better than the
other.
My next post will show the 2 class method as Hudson suggested would be
better.
Boz
On Jan 25, 7:25 pm, Boz <[email protected]> wrote:
> Hi Hudson,
>
> My preference is not to have 3 classes, it just doesn't feel right,
> and it means I can't easily navigate from a Table1 to it's Table2
> without hanging on to Table3 references somewhere. So if it's
> possible to use two ManyToMany for this, that certainly sounds the
> right approach.
>
> Could you help me a bit further with this implementation? I assume
> you're talking about having non-virtual properties that hide the
> implementation like this (get/set implementation elided):
>
> public class Table1
> {
> public virtual decimal Id {get;set;}
> public virtual string Name {get;set;}
> public Table2 Table2 {get {...} set {...}
>
> }
>
> public class Table2
> {
> public virtual decimal Id {get;set;}
> public virtual string Name {get;set;}
> public Table1 Table1 {get {...} set {...}
>
> }
>
> But do you then have protected virtual properties for Table1 and
> Table2 aswell so that they can be mapped with Fluent mappings? I find
> this aspect hard to visualise, both in terms of the entities and the
> mappings. Sorry to be slow, but if you could help me further here it
> would be much appreciated.
>
> Boz
>
> On Jan 25, 7:13 pm, Hudson Akridge <[email protected]> wrote:
>
> > You can definitely do this by creating your join table as an entity, but as
> > it's only an entity for the sake of enforcing a database
> > normalization/uniqueness constraint via foreign keys, and contains no
> > additional behavior, that seems like it would impact your domain negatively,
> > un-necessarily.
>
> > Also, point of order, you're going to want to use a .KeyReference() instead
> > of .KeyProperty, and drop the explicit .References(). Otherwise you will end
> > up getting the error that the person posting was getting.
> > CompositeId()
> > .KeyReference(x => x.Table1, "Table1_Id")
> > .KeyReference(x => x.Table2, "Table2_Id")
>
> > So, is adding a third class to your domain less of an impact than having
> > both sides of table1 and table2 be a manytomany? That's a decision you need
> > to make for yourself. In my opinion, creating a class for a join table, with
> > no other relevant information associated to that class, would be a last
> > resort. You could use two HasManyToMany()'s and hide your implementation
> > behind a public property. This seems like much less of an impact to your
> > domain model in my opinion.
>
> > Plus, in NH terms, now your third table row is now an entity, and will
> > become a heavier object to manage, both in the NH caching/proxying and for
> > Sql statements when updating/inserting/deleting. But, do the testing
> > yourself and then see which one works better for you and fits your domain
> > preference and go with that.
>
> > On Mon, Jan 25, 2010 at 12:34 PM, Boz <[email protected]> wrote:
> > > Hi,
>
> > > I just found someone doing a similar thing:
>
> > >http://stackoverflow.com/questions/497548/nhibernate-2-0-mapping-a-co...
> > > which says you need 3 classes, with the link table class referencing
> > > the other two tables, and it seems a composite key mapping does the
> > > job. Below is what I've got so far. Note that Table1 and Table2 are
> > > identical for this simplified example, and their maps are
> > > straightforward and not shown below.
>
> > > public class Table1 // Table2 identical
> > > {
> > > public virtual decimal Id {get;set;}
> > > public virtual string Name {get;set;}
> > > }
>
> > > public class Table3
> > > {
> > > public virtual Table1 Table1 {get;set;}
> > > public virtual Table2 Table2 {get;set;}
> > > }
>
> > > public class Table3Map: ClassMap<Table3>
> > > {
> > > Table("Table3");
> > > CompositeId()
> > > .KeyProperty(x => x.Table1, "Table1_Id")
> > > .KeyProperty(x => x.Table2, "Table2_Id")
> > > Reference(x => x.Table1, "Table1_Id")
> > > Reference(x => x.Table2, "Table2_Id")
> > > }
>
> > > So, comments on the above approach would be appreciated.
>
> > > But also, I get the following error when running this:
> > > "Could not determine type for: MyNamespace.Table1"
>
> > > On Jan 25, 5:17 pm, Hudson Akridge <[email protected]> wrote:
> > > > The only way I can think of mapping that would be using a ManyToMany
> > > between
> > > > the two of them (even though you're only ever going to get a single row
> > > > back), and then projecting that backing field with a public property
> > > > that
> > > > just does something like a return backingCollectionField.First();
>
> > > > I don't know if there's a better way to do it. May jump onto:
> > >http://groups.google.com/group/nhusersandaskthem (using NH xml mappings
> > > > of course). If they can give you better xml to do it, we can probably
> > > show
> > > > you how to map it in FNH.
>
> > > > On Mon, Jan 25, 2010 at 11:12 AM, Boz <[email protected]> wrote:
> > > > > I'm using a legacy database schema which cannot be changed (although
> > > > > it can be added to, keys, columns etc).
>
> > > > > I have two tables that have no direct relationships between them.
> > > > > There is then a third table, a link table, which simply references the
> > > > > two tables with foreign keys. A record in this third table is meant
> > > > > to be unique (although it isn't enforced with a key yet!) and it
> > > > > indicates that a row in Table1 is related to only 1 row in Table2 and
> > > > > vice-versa.
>
> > > > > Questions:
> > > > > What classes should I have?
> > > > > It seems like I ought to have just two classes that each have a single
> > > > > reference to each other.
>
> > > > > How do I go about mapping this?
> > > > > I can't use HasOne because that requires that the keys in each table
> > > > > are unique, and besides I have the link table in the way.
>
> > > > > I could create 3 classes, one for each table, but I can't seem to
> > > > > avoid implementing one-to-many relationships (which I couldn't get to
> > > > > work either).
>
> > > > > I intend to add a composite key to the link table to enforce the
> > > > > unique relationship, in case that assists with the mapping?
>
> > > > > Simplified schema, if it helps:
>
> > > > > Table1 (Table1_Id (pk), Name)
> > > > > Table2 (Table2_Id (pk), Name)
> > > > > Table3 (Table1_Id (fk), Table2_Id (fk))
>
> > > > > Thanks in advance
>
> > > > > Boz
>
> > > > > --
> > > > > You received this message because you are subscribed to the Google
> > > Groups
> > > > > "Fluent NHibernate" group.
> > > > > To post to this group, send email to
> > > [email protected].
> > > > > To unsubscribe from this group, send email to
> > > > > [email protected]<fluent-nhibernate%[email protected]>
> > > <fluent-nhibernate%[email protected]<fluent-nhibernate%[email protected]>
>
> > > > > .
> > > > > For more options, visit this group at
> > > > >http://groups.google.com/group/fluent-nhibernate?hl=en.
>
> > > > --
> > > > - Hudsonhttp://www.bestguesstheory.comhttp://twitter.com/HudsonAkridge
>
> > > --
> > > You received this message because you are subscribed to the Google Groups
> > > "Fluent NHibernate" group.
> > > To post to this group, send email to [email protected].
> > > To unsubscribe from this group, send email to
> > > [email protected]<fluent-nhibernate%[email protected]>
> > > .
> > > For more options, visit this group at
> > >http://groups.google.com/group/fluent-nhibernate?hl=en.
>
> > --
> > - Hudsonhttp://www.bestguesstheory.comhttp://twitter.com/HudsonAkridge
--
You received this message because you are subscribed to the Google Groups
"Fluent NHibernate" 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/fluent-nhibernate?hl=en.