4

我有一个使用复合 id 的实体。我更改了代码以利用将复合 id 包装在单独的键类中。我希望使用 Linq 可以对关键对象和 Criteria API 进行比较以使用 Restrictions.IdEq 但两者都失败了。我需要明确比较关键值以使其工作。

如果这应该起作用,我找不到任何文档,所以目前我坚持直接比较,但这意味着当我更改密钥时,我还需要更新查询代码,这显然不是我想要的。

附带说明一下,我在 NHibernate 3.0.0 Alpha 2 和 3 中进行了尝试。

领域

映射

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
                   assembly="Cwc.Pulse.Dal"
                   namespace="Cwc.Pulse.Dal">
  <class name="AddonStatus">
    <composite-id name="Id">
      <key-many-to-one name="Context" column="Context_Id" class="Context" />
      <key-property name="AddonType" column="Addon_Id"/>
    </composite-id>
    <property name="Status" />
  </class>
</hibernate-mapping>

班级

public class AddonStatus
{
    public virtual string Status { get; set; }
    public virtual Key Id { get; protected set; }

    public AddonStatus()
    {
        Id = new Key();
    }

    public class Key
    {
        public virtual Context Context { get; set; }
        public virtual AddonType AddonType { get; set; }

        public override int GetHashCode()
        {
            return ContextId.GetHashCode() ^ AddonType.GetHashCode();
        }

        public override bool Equals(object obj)
        {
            if (this == obj) return true;
            var o = obj as Key;
            if (null == o) return false;
            return Context == o.Context && AddonType == o.AddonType;
        }
    }
}

工作查询

下面的查询有效,如您所见,我明确比较了键值。我不比较关键对象。

林克

from status
in session.Query<AddonStatus>()
where status.Id.Context == context && status.Id.AddonType == addonType
select status

标准 API

session.CreateCriteria<AddonStatus>()
.Add(Restrictions.Eq("Id.Context", context))
.Add(Restrictions.Eq("Id.AddonType", addonType))

预计工作,但不

我希望以下查询能够正常工作。要么在内存而不是数据库中有效地使用 linq,但我希望标准 api 足够聪明,可以处理查询中的此类复合 id。

linq 和条件 api 查询都使用 Key 对象比较。

var key = new AddonStatus.Key
{
    Context = context,
    AddonType = addonType
};

林克

from status
in session.Query<AddonStatus>()
where status.Id == key
select status

标准 API

session.CreateCriteria<AddonStatus>()
.Add(Restrictions.IdEq(key))

因此,如果有人有这样的场景工作,那么我做错了什么?

4

2 回答 2

0

不是直接回答您的问题,但无论如何它可能对您有用。您可以通过将 AddonStatus 映射为所有者(很可能是上下文)上的复合元素来避免(显式)复合键:

  <class name="Context">
    <map name="AddonStates" table="AddonStatus">
      <key column="Context_Id" /> <!-- Foreign key to the Context -->
      <index column="Addon_Id" /> <!-- Dictionary key -->
      <composite-element>
        <property name="Status" /> <!-- data -->
      </composite-element>
    </map>
  </class>

在课堂Context上是这样的:

class Context
{
  IDictionary<AddonType, AddonStatus> AddonStates { get; private set; }
}

这与数据库结构几乎相同,但使用起来不同。我不能说这是否是你真正想要的,但它看起来就是这样。

于 2011-03-09T19:43:56.530 回答
0

有趣的是,我在 2.1.2 中得到的这种行为几乎完全相反。

我的映射(简化):

<!-- Subscriber class -->
<class name="Subscriber" >
<composite-id name="SubscriberKey" class="SubscriberKey">
  <key-property name="Request" column="RequestID" type="int"/>
  <key-many-to-one name="User" column="UserID" class="User" not-found="ignore" />
</composite-id>

<!-- User class - note that this goes to a different schema, 
  and is not mutable.  Who knows if that's important... -->
<class name="User" schema="AnotherDb.dbo" mutable="false">
<id name="Id" column="UserID" type="int">
  <generator class="native" />
</id>
<property name="FirstName" column="FirstName" type="string" />
<property name="LastName" column="LastName" type="string" />

去:

public class User
{
    public virtual int? Id {get; protected set;}
    public virtual string FirstName { get; protected set; }
    public virtual string LastName { get; protected set; }

    public User() { }
}

public class Subscriber
{
    public virtual SubscriberKey SubscriberKey { get; set; }
    public virtual User User { get; set; }

    public Subscriber() { }
}

public class SubscriberKey
{
    public override bool Equals(object obj)
    {
        if (obj is SubscriberKey && obj != null)
            return ((SubscriberKey)obj).Request == Request 
                && ((SubscriberKey)obj).User.Id == User.Id;

        return false;
    }

    public override int GetHashCode()
    {
        return (Request.ToString() + User.Id.ToString()).GetHashCode();
    }

    public virtual int Request { get; set; }
    public virtual User User { get; set; }
    public SubscriberKey() { }
}

有效的东西:

CreateCriteria<Subscriber>()
    .Add(Restrictions.IdEq(keyInstance))
    .UniqueResult<Subscriber>();
CreateCriteria<Subscriber>()
    .Add(Restrictions.Eq("SubscriberKey.User.Id", aUserID))
    .Add(Restrictions.Eq("SubscriberKey.Request", aRequestID))
    .UniqueResult<Subscriber>();

不起作用的事情:

Get<Subscriber>(keyInstance);

我认为这是他们各种 ID 相等查询表单之间的不一致。当我有时间时,我将构建一个最小的单元测试作为错误示例提交。我会对任何人可能对此有任何/所有想法感兴趣......


编辑:嘿嘿,我想通了!

确实有效的事情,现在我已经阅读了这个

Get<Subscriber>(new SubscriberKey() { 
    User = Load<User>(aUserID), // the important part!
    Request = aRequestID
});

这将为 User 键创建一个代理对象,而无需访问数据库(除非必要)。如果您交换Load<User>Get<User>您将立即访问数据库以填充对象,而不是尊重您的延迟加载属性。使用负载。

像这样的事情正是人们建议 (type)Repository 模式的原因——我可以在幕后做到这一点:Get<>(new SK(){User=Load<>(key.User.Id)},并且仍然Get(key)通过一个键,与其他所有对象相同。

于 2011-03-09T03:13:09.717 回答