Mapping entity oneToMany with fluent nhibernate

后端 未结 2 960
孤城傲影
孤城傲影 2021-01-21 02:20

The problem appears to be simple however I\'m having so much trouble trying to map this entities. I just can\'t see what am I doing wrong. Can you guys help me?

I have th

相关标签:
2条回答
  • 2021-01-21 02:38

    After all, 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 with only this one mapping in place:

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

    BTW, this will be (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:

    7.2. Collections of dependent objects

    0 讨论(0)
  • 2021-01-21 02:42

    The one-to-many and many-to-one are always related by one column. This is such column, which contains reference ID (foreign key) to the other table / entity.

    In our case, it must be column in table of Medidor, and its name would be "CORE_NUMERO_MEDIDOR". The mapping should look like this

    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

    Based on extended question, when we can see this structure of tables

    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 reference is different then in C#. It seems, like if

    table CLIENTE references just one MEDIDOR, while 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 mapping 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

    Because the second table MEDIDOR is not having its own primary key (column NUMERO_MEDIDOR) but it could contain many same values... coming from CLIENT TABLE... we should 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();
    
    }
    
    0 讨论(0)
提交回复
热议问题