문제

I'm just feeling the n00b today with nHibernate and struggling mapping to this legacy data set.

I have two tables that do not have unique keys that when joined and filtered return a collection of properties for a user -- the result set almost looking like a key/value pair.

The relevant tables:

User: UserId, ...

ProfileField: FieldId, Name, ...

ProfileProperty: UserId, FieldId, Value ...

An SQL query that garners the correct data for a user would be:

select 
     profilefield.fieldid, 
     profilefield.Name, 
     ProfileProperty.Value 
from profilefield,
     profileproperty 
where profilefield.fieldid=profileproperty.fieldid 
      and profileproperty.userid={UserID}

I can't map either to classes as they don't have keys, I don't think I can use a bag since I need data from both tables (name in profilefield, value in profileproperty).

So I decided to try a named query. I've been using examples from both the nHibernate documents, Oren's blogs, and this site without luck. In all cases either no data is returned or nHibernate throws a nullvalue exception that has no addition info.

Here is my current mapping:

<class name="User" ...>
    ...
    <set name="Properties" lazy="true">
       <key/>
       <one-to-many class="UserProperty"/>
       <loader query-ref="GetUserProperties"/>
    </set>
</class>

<class name="UserProperty" mutable="false">
    <id name="Id" column="Id" />
    <property name="Name"/>
    <property name="Value" />
   <loader query-ref="GetUserProperties" />        
</class>

<sql-query name="GetUserProperties">
    <return alias="pp" class="UserProperty" lock-mode="upgrade"/>
    select
    profilefield.fieldid as {pp.Id},
    profilefield.name as {pp.Name},
    profileproperty.value as {pp.Value}
    from
    profileproperty,profilefield
    where profilefield.fieldid=profileproperty.fieldid
    and profileproperty.userid=?
</sql-query>

I've tried using the <load-collection alias="pp" role="User.Properties"/> but that throws an Error in Named Query exception that I can't resolve. I believe that has something to do with the parameter '?' as all of the examples for load-collection involve named parameters rather than the generic '?'.

when using the statement above nHibernate does in fact run the correct query:

  NHibernate: select
      profilefield.fieldid as Id31_0_,
      profilefield.name as Name31_0_,
      profileproperty.value as Value31_0_
      from
      profileproperty,profilefield
      where profilefield.fieldid=profileproperty.fieldid
      and profileproperty.userid=:p0;:p0 = 88162 [Type: Int32 (0)]

However when attempting to access the collection in my unit test nHibernate throws:

  System.NullReferenceException : Object reference not set to an
  instance of an object.

Updated:

If I call Session.GetNamedParameter("GetUserProperties").SetParameter("id",88162).List() and change the '?' to ':id' I am able to retrieve data. So I'm assuming the named sql-query syntax is correct and the problem lies with the collection set of the parent object.

도움이 되었습니까?

해결책

I would suggest, at least in this case, use the mapping close to the DB structure. Which does not seem to be so bad. We should have a User which has a ProfileProperty collection. Each ProfileProperty has a reference to ProfileField

<class name="User" ...>
    ...
    <set name="ProfileProperties" lazy="true">
       <key column="UserId" />
       <one-to-many class="ProfileProperty"/>
    </set>
</class>

<class name="ProfileProperty" ...>
    ...
    <many-to-one class="ProfileField" column="FieldId" />
</class>

<class name="ProfileField" ... />

the C# :

public class User
{
    public virtual IList<ProfileProperty> ProfileProperties { get; set; }
    ...

public class ProfileProperty
{
    public virtual ProfileField ProfileField { get; set; }
    ...

public class ProfileField 
{
    public virtual string Name { get; set; }
    ...

This is a native structure. This way we can use the power of the NHibernate querying. And also the Write operations will go smoothly.

If you really need that artificial collection, NHibernate has solution as well. It would be readonly, but in some scenarios it could help...

The way to go here, if we really would like to solve it on the mapping layer, is <subselect> and <composite-element>

<bag name="UserProperties">
 <subselect>
    select 
     profilefield.fieldid,  AS FieldId
     profilefield.Name,     AS Name
     ProfileProperty.Value, AS Value
     profileproperty.userid AS UserId
    from profilefield,
     profileproperty 
    where profilefield.fieldid=profileproperty.fieldid       
 </subselect>

  <key column="UserId" />
  <composite-element class="UserProperty" >
      <parent name="Parent" />
      <property name="FieldId" />
      <property name="Name" />
      <property name="Value" />
  </composite-element>
</bag>

and C# :

public class UserProperty
{
    public virtual User Parent { get; set; }
    public virtual string Name { get; set; }
    public virtual string Value { get; set; }
    public virtual int FieldId { get; set; }
}
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top