Composite Id's и Restrictions.IdEq или сравнение в Linq не работает должным образом - PullRequest
4 голосов
/ 06 октября 2010

У меня есть объект, в котором используется составной идентификатор.Я изменил код, чтобы использовать оборачивание составного идентификатора в отдельный класс ключей.Я ожидал, что с 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;
        }
    }
}

Рабочие запросы

Запросы ниже работают, и, как вы можете видеть, я сравниваю значения ключей в явном виде.Я не сравниваю ключевой объект.

Linq

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 будут достаточно умными для обработки таких составных идентификаторов в запросах.

Как в запросах linq, так и в критериях API используется сравнение объектов Key..

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

Linq

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

Criteria API

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

Итак, если у кого-то работает такой сценарий, то что я делаю не так?

Ответы [ 2 ]

0 голосов
/ 09 марта 2011

Не является прямым ответом на ваш вопрос, но в любом случае он может быть вам полезен. Вы могли бы избежать (явного) составного ключа, сопоставив 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; }
}

Это приводит к почти одинаковой структуре базы данных, но работать с ней по-другому. Я не могу сказать, действительно ли это то, чего вы хотите, но это выглядит так.

0 голосов
/ 09 марта 2011

Интересно, что я получаю почти полную противоположность этому поведению в 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.Когда у меня будет время, я создам минимальный модульный тест для отправки в качестве примера ошибки.Я был бы заинтересован в любых / всех мыслях у кого-либо об этом ...


edit: Привет, я понял это!

Вещи, которые работают, теперь, когда япрочитал this

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

Это создаст прокси-объект для ключа пользователя без попадания в базу данных (если не требуется).Если вы поменяете Load<User> на Get<User>, вы сразу же попадете в базу данных, чтобы заполнить объект, вместо того, чтобы соблюдать ваши свойства отложенной загрузки.Используйте Load.

И именно так люди и рекомендуют шаблон (тип) репозитория - я могу сделать это за сценой: Get<>(new SK(){User=Load<>(key.User.Id)}, и все же Get(key) с помощью одной клавиши, идентичной каждомудругой объект.

...