使用流畅的nhibernate映射实体oneToMany

时间:2015-07-08 12:10:27

标签: c# nhibernate fluent-nhibernate nhibernate-mapping fluent-nhibernate-mapping

问题似乎很简单但是我在尝试映射这些实体时遇到了很多麻烦。我只是看不出我做错了什么。你能帮助我吗?

我有班级Cliente

public class Cliente
{    
    public Cliente () { }
    public virtual int ClienteId { get; set; }  
    public IList<Medidor> ListaMedidores { get; set; }   
    public virtual string NumeroMedidor { get; set; }       
}

班级Medidor

public class Medidor
{
    public Medidor() { }
    public virtual string NumeroMedidor { get; set; }
    public virtual string MarcaMedidor { get; set; }
    public virtual Cliente Cliente { get; set; }
}

我试图像这样映射

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");
}

目标是根据数据库提供我的Medidor列表。 所以我做了:

Session.Query<Cliente>().Fetch(x => x.ListaMedidores).ToList();

我的列表总是空着。即使有这些表格的数据......我也会感激任何帮助或建议。

此致

编辑

我的数据库是这样的:


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)
)

给出sql select * from cliente where core_numero_medidor = '3569371'

CORE_ID CORE_NUMERO_MEDIDOR
123     3569371

和sql select * from MEDIDOR where numero_medidor = '3569371'

NUMERO_MEDIDOR MARCA_MEDIDOR
3569371        general_motors
3569371        kia
3569371        FIAT

所以我想在IList<Medidor> Lista Medidores课程的Cliente上获得3个元素..

修改

我改为:

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; }
}

并将ClienteMap的地图更改为:

Map(x => x.NumeroMedidor).Column("NUMERO_MEDIDOR");    
HasMany(x => x.ListaMedid)
            .KeyColumns.Add("NUMERO_MEDIDOR")
            .Table("MEDID")
            .PropertyRef("CoreNumeroCliente")
            .Cascade.All();

现在列表获得了预期的记录数,但所有记录都与第一个记录相同。即:

预期

NUMERO_MEDIDOR MARCA_MEDIDOR
3569371        general_motors
3569371        kia
3569371        FIAT

我的结果

NUMERO_MEDIDOR MARCA_MEDIDOR
3569371        general_motors
3569371        general_motors
3569371        general_motors

有什么建议吗?到目前为止,我要感谢@RadimKöhler的帮助。

另一个编辑

我找到了解决方案!

我试图将非唯一列映射为主键...我只是将列更改为真正的主键并且工作正常!

所以现在这是解决方案

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
    }
}

这是我的疑问:

Session.Query<CorteReligacao>()
                .Fetch(x => x.ListaMedid)

我真的要感谢RadimKöhler的帮助。他的耐心,关注和帮助解决问题的意愿使我缺乏感谢的方式......我只能祝他生活中一切顺利。

我真的希望这个主题可以帮助那些遇到同样问题的人。

问候。

2 个答案:

答案 0 :(得分:2)

one-to-manymany-to-one始终由一列相关联。这是这样的列,其包含到另一个表/实体的引用ID(外键)。

在我们的情况下,它必须是 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
}

EXTEND

基于扩展问题,当我们可以看到表格的这种结构时

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)
)

数据库引用与C#不同。看起来好像是

  

表CLIENTE仅引用一个 MEDIDOR,而MEDIDOR有许多CLIENTE。

似乎对象看起来像这样:

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; }
}

,映射应为

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();
}

另一个扩展

因为第二个表MEDIDOR没有自己的主键(列NUMERO_MEDIDOR)但它可以包含许多相同的值...来自CLIENT TABLE ...我们应该使用组件映射

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();

}

答案 1 :(得分:1)

毕竟,使用这些SQL脚本(在我的情况下针对SQL Server进行调整)

CREATE TABLE CLIENTE
(
  CORE_ID                      int           NOT NULL,
  CORE_NUMERO_MEDIDOR          VARCHAR(50)
)

CREATE TABLE MEDIDOR
(
  NUMERO_MEDIDOR  VARCHAR(50),
  MARCA_MEDIDOR   VARCHAR(50)
)

使用这些实体(所有属性都是虚拟的)

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; }
}

并且只有这一个映射:

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();
    }
}

我可以确认,这个查询会起作用:

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);

顺便说一句,这将是 (我的首选) 生成的xml映射:

<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>

有关文档,请参阅:

7.2. Collections of dependent objects