Search code examples
c#linqnhibernatenhibernate-mapping

Nhibernate Map composite element


I have DB view ALL_ATM_DEV_SATTRIB_VALS

DEVICE_ID   ATTRIB_ID    INT_VALUE    STRING_VALUE    DATE_VALUE

1           13           null         10.0.3.50       null
1           14           0            null            null
1           15           null         null            null

2           13           null         10.0.3.51       null
2           14           2            null            null
2           15           null         null            null

Entity objects

public class AttributeValue: IAttributeValue
{
    public virtual string StringValue { get; set; }
    public virtual DateTime? DateValue { get; set; }
    public virtual int? IntValue { get; set; }
}

public class Device : IDevice
{
    public virtual long Id { get; set; }        

    public virtual IDictionary<long, IAttributeValue> Values { get; set; }
}

Mapping file(hbm)

<class name="Device" table="DEVICES" lazy="true" >

<id name="Id" column="ID" ></id>

<map name="Values " batch-size="10" table="ALL_ATM_DEV_SATTRIB_VALS" lazy="true">
  <key column="DEVICE_ID" />
  <index column="ATTRIB_ID" type="System.Int64" />

  <composite-element class="AttributeValue">
    <property name="StringValue"  column="STRING_VALUE" />
    <property name="DateValue"  column="DATE_VALUE" />
    <property name="IntValue"  column="INT_VALUE" />
  </composite-element>
</map>

</class>

Selecting dictionaries values work correctly, but when i tried use it in Linq expressions

List<IDevice> a = dc.Get<IDevice>()
                    .Where(x=>x.Values[13].StringValue
                                          .ToLower().Contains("10.0.3"))
                    .ToList();

nhibernate throw exception

System.InvalidOperationException: Cannot create element join for a collection of non-entities!

In the object Device I need to have IDictionary. Key is ATTRIB_ID(long), Value is AttributeValue


Solution

  • Nhibernate can't create a sql-query use IDictionary<long, IAttributeValue> because IAttributeValue don't map in hbm as entity.

    But i solved this problem.

    I replaced the mapping of one IDictionary to three simpler :

     <map name="StringValues" table="ALL_ATM_DEV_SATTRIB_VALS" lazy="true" fetch="select">
      <key column="DEVICE_ID" />
      <index column="ATTRIB_ID" type="System.Int64" />
      <element column="STRING_VALUE" type="System.String"/>
    </map>
    <map name="DateValues" table="ALL_ATM_DEV_SATTRIB_VALS" lazy="true" fetch="select">
      <key column="DEVICE_ID" />
      <index column="ATTRIB_ID" type="System.Int64" />
      <element column="DATE_VALUE" type="System.DateTime"/>
    </map>
    <map name="IntValues" table="ALL_ATM_DEV_SATTRIB_VALS" lazy="true" fetch="select">
      <key column="DEVICE_ID" />
      <index column="ATTRIB_ID" type="System.Int64" />
      <element column="INT_VALUE" type="System.Int64"/>
    </map>
    

    I use this maps only for create result sql-query, thats why lazy=true is present, and never access to them in the entity IDevice

    Data of this maps selected an another sql-query at once, because lazy initialization would cause n+1 sql-queries on DB.

    Linq query in my question transformed in :

    List<IDevice> a = dc.Get<IDevice>()
                    .Where(x=>x.StringValues[13].ToLower().Contains("10.0.3"))
                    .ToList();
    

    In IDevice entity defined three maps :

        public virtual IDictionary<long, long> IntValues { get; set; }
        public virtual IDictionary<long, string> StringValues { get; set; }
        public virtual IDictionary<long, DateTime> DateValues { get; set; }