Mapping oneToMany object to smooth nhibernate
The problem seems simple, but I'm having so many problems trying to match these objects. I just can't see what I am doing wrong. Can you guys help me?
I have a class Cliente
:
public class Cliente
{
public Cliente () { }
public virtual int ClienteId { get; set; }
public IList<Medidor> ListaMedidores { get; set; }
public virtual string NumeroMedidor { get; set; }
}
And class Medidor
public class Medidor
{
public Medidor() { }
public virtual string NumeroMedidor { get; set; }
public virtual string MarcaMedidor { get; set; }
public virtual Cliente Cliente { get; set; }
}
I tried to display like this
public ClienteMap()
{
Map(x => x.NumeroMedidor).Column("CORE_NUMERO_MEDIDOR");
HasMany(x => x.ListaMedidores)
.KeyColumn("NUMERO_MEDIDOR").Inverse().Cascade.All();
}
public MedidorMap()
{
Table("medidor");
LazyLoad();
Id(x => x.NumeroMedidor).Column("NUMERO_MEDIDOR");
Map(x => x.TipoMedidor).Column("TIPO_MEDIDOR");
References(x => x.Cliente).Column("CORE_NUMERO_MEDIDOR");
}
The target is my list Medidor
according to the database. So I did:
Session.Query<Cliente>().Fetch(x => x.ListaMedidores).ToList();
And I get the list always empty. Even with the data on these tables ... I would appreciate any help or suggestion.
Hello
EDITMy database is like this:
CREATE TABLE CLIENTE ( CORE_ID NUMBER NOT NULL, CORE_NUMERO_MEDIDOR VARCHAR2 (50 BYTE) ) CREATE TABLE MEDIDOR ( NUMERO_MEDIDOR VARCHAR2 (50 BYTE), MARCA_MEDIDOR VARCHAR2 (50 BYTE) )
Given sql select * from cliente where core_numero_medidor = '3569371'
:
CORE_ID CORE_NUMERO_MEDIDOR
123 3569371
and sql select * from MEDIDOR where numero_medidor = '3569371'
:
NUMERO_MEDIDOR MARCA_MEDIDOR
3569371 general_motors
3569371 kia
3569371 FIAT
So, I have to get 3 elements in class IList<Medidor> Lista Medidores
on Cliente
..
EDIT
I changed to this:
public class Cliente
{
public Cliente () { }
public virtual int ClienteId { get; set; }
public IList<Medidor> ListaMedidores { get; set; }
public virtual string NumeroMedidor { get; set; }
}
public class Medidor
{
public Medidor() { }
public virtual string NumeroMedidor { get; set; }
public virtual string MarcaMedidor { get; set; }
}
And changed the map ClienteMap
to:
Map(x => x.NumeroMedidor).Column("NUMERO_MEDIDOR");
HasMany(x => x.ListaMedid)
.KeyColumns.Add("NUMERO_MEDIDOR")
.Table("MEDID")
.PropertyRef("CoreNumeroCliente")
.Cascade.All();
and now the list gets the expected number of entries, but they are all the same as the first ones. i.e:
Expected
NUMERO_MEDIDOR MARCA_MEDIDOR
3569371 general_motors
3569371 kia
3569371 FIAT
My result
NUMERO_MEDIDOR MARCA_MEDIDOR
3569371 general_motors
3569371 general_motors
3569371 general_motors
Any suggestions? I'd like to thank @Radim Köhler for the help.
OTHER IMAGE
I found a solution!
I was trying to map a non-unique column as the primary key ... I just changed the column to a real primary key and it works!
So now the solution
public class Cliente
{
public Cliente () { }
public virtual int ClienteId { get; set; }
public IList<Medidor> ListaMedidores { get; set; }
public virtual string NumeroMedidor { get; set; }
}
public class Medidor
{
public Medidor() { }
public virtual string NumeroMedidor { get; set; }
public virtual string MarcaMedidor { get; set; }
}
public class ClienteMap : ClassMap<Cliente>
{
public ClienteMap()
{
Map(x => x.NumeroMedidor).Column("NUMERO_MEDIDOR");
HasMany(x => x.ListaMedid)
.KeyColumns.Add("NUMERO_MEDIDOR")
.Table("MEDID")
.PropertyRef("CoreNumeroCliente")
.Cascade.All();
}
}
public class MedidorMap : ClassMap<Medidor>
{
public MedidorMap()
{
LazyLoad();
Id(x => x.NumeroMedidor).Column("NUMERO_MEDIDOR");
Map(x => x.MarcaMedidor).Column("MARCA_MEDIDOR");
[...] //Other properties
}
}
And here is my request:
Session.Query<CorteReligacao>()
.Fetch(x => x.ListaMedid)
I would really like to thank Radim Köhler for his help. His patience, attention and willingness to help solve a problem leave me with a lack of gratitude. I can only wish him all the best in life.
And I really hope this thread can help people with the same problem.
Sincerely.
source to share
In the end, with these SQL scripts (adjust for SQL Server in my case)
CREATE TABLE CLIENTE
(
CORE_ID int NOT NULL,
CORE_NUMERO_MEDIDOR VARCHAR(50)
)
CREATE TABLE MEDIDOR
(
NUMERO_MEDIDOR VARCHAR(50),
MARCA_MEDIDOR VARCHAR(50)
)
With these entities (all properties are virtual)
public class Cliente
{
public virtual int ClienteId { get; set; }
public virtual IList<Medidor> ListaMedidores { get; set; }
public virtual string NumeroMedidor { get; set; }
}
public class Medidor
{
public virtual string NumeroMedidor { get; set; }
public virtual string MarcaMedidor { get; set; }
public virtual Cliente Cliente { get; set; }
}
and only this :
public class ClienteMap: ClassMap<Cliente>
{
public ClienteMap()
{
Table("CLIENTE");
Id(x => x.ClienteId, "CORE_ID");
Map(x => x.NumeroMedidor).Column("CORE_NUMERO_MEDIDOR");
HasMany(x => x.ListaMedidores)
.KeyColumn("NUMERO_MEDIDOR")
.Component(com =>
{
com.ParentReference(y => y.Cliente);
com.Map(y => y.MarcaMedidor, "MARCA_MEDIDOR");
})
.PropertyRef("NumeroMedidor")
.Table("MEDIDOR")
// .Inverse() // NO INVERSE, won't work
.Cascade.All();
}
}
I can confirm that this query will work:
var list = session.Query<Cliente>().Fetch(x => x.ListaMedidores).ToList();
var firt = list.First().ListaMedidores.First();
var last = list.First().ListaMedidores.Last();
Assert.IsTrue(firt.MarcaMedidor != last.MarcaMedidor);
By the way, this would be the (my preferred) generated xml
mapping:
<class xmlns="urn:nhibernate-mapping-2.2" name="Cliente" table="CLIENTE">
<id name="ClienteId" type="System.Int32">
<column name="CORE_ID" />
<generator class="identity" />
</id>
<bag cascade="all" name="ListaMedidores" table="MEDIDOR">
<key property-ref="NumeroMedidor">
<column name="NUMERO_MEDIDOR" />
</key>
<composite-element class="Medidor">
<parent name="Cliente" />
<property name="MarcaMedidor" type="System.String">
<column name="MARCA_MEDIDOR" />
</property>
</composite-element>
</bag>
<property name="NumeroMedidor" type="System.String">
<column name="CORE_NUMERO_MEDIDOR" />
</property>
</class>
For documentation see:
source to share
one-to-many
and are many-to-one
always linked by one column. It is such a column that contains the reference identifier (foreign key) for another table / object.
In our case, this is a column in the table , and its name will be . The display should look like this: Medidor
"CORE_NUMERO_MEDIDOR"
public ClienteMap()
{
...
HasMany(x => x.ListaMedidores)
//.KeyColumn("NUMERO_MEDIDOR")
.KeyColumn("CORE_NUMERO_MEDIDOR") // column in other table
.Inverse().Cascade.All();
}
public MedidorMap()
{
...
References(x => x.Cliente)
.Column("CORE_NUMERO_MEDIDOR"); // column in this table
}
OUTGOING
Based on the extended question, when can we see this table structure
CREATE TABLE CLIENTE
(
CORE_ID NUMBER NOT NULL,
CORE_NUMERO_MEDIDOR VARCHAR2(50 BYTE)
)
CREATE TABLE MEDIDOR
(
NUMERO_MEDIDOR VARCHAR2(50 BYTE),
MARCA_MEDIDOR VARCHAR2(50 BYTE)
)
That the DB link is different from C #. It seems as if
the CLIENTE table only refers to one MEDIDOR, while the MEDIDOR has many CLIENTEs.
It seems that the objects should look like this:
public class Cliente
{
...
//public IList<Medidor> ListaMedidores { get; set; }
//public Medidor Medidor { get; set; }
}
public class Medidor
{
...
//public virtual Cliente Cliente { get; set; }
public virtual IList<Cliente> Clientes { get; set; }
}
and the display should be
public ClienteMap()
{
...
References(x => x.Medidor, "CORE_NUMERO_MEDIDOR");
}
public MedidorMap()
{
...
Id(x => x.NumeroMedidor).Column("NUMERO_MEDIDOR")
// column in this table to be compared
HasMany(x => x.Clientes)
.KeyColumn("CORE_NUMERO_MEDIDOR") // with column in other table
.Inverse().Cascade.All();
}
ANOTHER EXTEND
Since the second MEDIDOR table does not have its own primary key (NUMERO_MEDIDOR column), but can contain many of the same values ... based on CLIENT TABLE ... we have to use component mapping
public ClienteMap()
{
...
Map(x => x.NumeroMedidor).Column("CORE_NUMERO_MEDIDOR");
HasMany(x => x.ListaMedidores)
.Component(com =>
{
com.Parent(y => y.Cliente, "NUMERO_MEDIDOR")
.PropertyRef("NumeroMedidor")
;
com.Map(y => y.MarcaMedidor, "MARCA_MEDIDOR");
})
.PropertyRef("NumeroMedidor")
.Table("MEDIDOR")
// .Inverse() // NO INVERSE, won't work
.Cascade.All();
}
source to share