NHibernate обновляет мою измененную запись во время вызова .list - PullRequest
1 голос
/ 29 июля 2011

У меня есть запись, которую я редактирую в веб-приложении ASP.net с использованием nhibernate и MVP.Когда я делаю изменения в записи, мне нужно проверить, чтобы убедиться, что одно поле уникально (см. «FriendlyUrl» ниже).Однако, когда я делаю свой вызов Criteria.List, он сначала обновляет запись, а также все дочерние записи (PharmacyStoreHours), а затем делает выбор (обнаружил это с помощью профиля SQL).Очевидно, что я не хочу обновлять запись до того, как проверка будет завершена, и проверка завершится неудачно, так как данные были обновлены до выбора.Как сделать выборку без обновления записи?

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

Класс файла конфигурации NHibernate:

  <class table="Pharmacy" name="DataAccess.Domains.Pharmacy, DataAccess">
    <id name="PharmacyId" column="PharmacyId" unsaved-value="0">
      <generator class="increment" />
    </id>
    <property name="StoreAccountNumber" column="StoreAccountNumber" type="System.String" not-null="true" />
    <property name="ClientName" column="ClientName" type="System.String" not-null="true" />
    <property name="PharmacyName" column="PharmacyName" type="System.String" not-null="true" />
    <property name="Address" column="Address" type="System.String" not-null="false" />
    <property name="AddressContinued" column="AddressContinued" type="System.String" not-null="false" />
    <property name="City" column="City" type="System.String" not-null="false" />
    <property name="State" column="State" type="System.String" not-null="false" />
    <property name="Zipcode" column="Zipcode" type="System.String" not-null="false" />
    <property name="Phone" column="Phone" type="System.String" not-null="false" />
    <property name="Fax" column="Fax" type="System.String" not-null="false" />
    <property name="Email" column="Email" type="System.String" not-null="false" />
    <property name="FriendlyUrl" column="FriendlyUrl" type="System.String" not-null="false" />
    <property name="OwnersName" column="OwnersName" type="System.String" not-null="false" />
    <property name="Latitude" column="Latitude" type="System.Decimal" not-null="false" />
    <property name="Longitude" column="Longitude" type="System.Decimal" not-null="false" />
    <bag name="Hours" table="PharmacyStoreHours" cascade="all" inverse="true">
      <key column="PharmacyId" />
      <one-to-many class="DataAccess.Domains.PharmacyWorkDays, DataAccess" />
    </bag>
  </class>
  <class table="PharmacyStoreHours" name="DataAccess.Domains.PharmacyWorkDays, DataAccess">
    <id name="PharmacyStoreHourId" column="PharmacyStoreHourId" unsaved-value="0">
      <generator class="increment" />
    </id>
    <property name="WorkDay" column="WorkDay" type="System.Int32" not-null="true" />
    <property name="OpenTime" column="OpenTime" type="System.DateTime" not-null="true" />
    <property name="CloseTime" column="CloseTime" type="System.DateTime" not-null="true" />
    <many-to-one name="PharmacyMap" class="DataAccess.Domains.Pharmacy, DataAccess" column="PharmacyId" not-null="true" />
  </class>

Код:

public bool IsFriendlyUrlUnique(string clientName, string friendlyUrl)
{
    bool result = false;

    ICriteria crit = CurrentSession.CreateCriteria(typeof (Pharmacy));
    crit.Add(new EqExpression("ClientName", clientName));
    crit.Add(new EqExpression("FriendlyUrl", friendlyUrl));
    crit.AddOrder(Order.Asc(PHARMACY_NAME));

    if (crit.List<Pharmacy>().Count == 0)
        result = true;

    return result;
}

Заранее благодарен за помощь.

Ответы [ 2 ]

2 голосов
/ 29 июля 2011

NHibernate по существу отслеживает все изменения, которые вы вносите в объекты, которые вы прикрепили к сеансу NHibernate.Когда вы извлекаете объект, изменяете его и затем делаете запрос к базе данных, NHibernate сталкивается с проблемой.Допустим, ради аргумента, что вы извлекаете клиента из базы данных, задайте customer.IsValuedCustomer = true и следующий запрос для всех ценных клиентов.Теперь NHibernate имеет (как минимум) две опции:

  1. Если NHibernate просто переводит запрос, который вы делаете, в SQL и возвращает результат, результат может быть не тем, что вы ожидаете, потому что он будетне включает недавно продвинутого клиента.

  2. NHibernate может получить правильный результат, сначала сбросив все изменения, внесенные в базу данных, и только , а затем , выполнив SQLзапрос.Это, однако, означает, что даже если вы думаете, что делаете безобидный запрос, он фактически инициирует запись в базу данных.

Основываясь на вашем описании, я не совсем уверен, но яПодумайте, что вы видите сценарий 2 (между прочим, Entity Framework решил использовать вариант 1).

Возможно, вы могли бы выполнить проверку уникальности, прежде чем вносить какие-либо изменения в объект.Однако это все равно оставит место для возникновения состояния гонки: сразу после того, как вы проверили уникальность, какой-то другой поток может записать изменение в базу данных, в результате чего ваше новое значение больше не будет уникальным.Чтобы обойти это, вы можете поместить ограничение уникальности прямо в схему базы данных, к которой оно обычно относится.

Надеюсь, это поможет

1 голос
/ 29 июля 2011

Рекомендуемое решение

Я бы посоветовал в запросе отфильтровать текущую запись.

ICriteria crit = CurrentSession.CreateCriteria(typeof (Pharmacy));
crit.Add(Restrictions.Not(Restrictions.Eq("PharmacyId", pharmacyIdToIgnore)));
crit.Add(Restrictions.Eq("ClientName", clientName));
crit.Add(Restrictions.Eq("FriendlyUrl", friendlyUrl));
crit.AddOrder(Order.Asc(PHARMACY_NAME));
crit.SetProjection(Projections.RowCount);

return crit.UniqueResult<int> == 0;

В идеале, вы должны выполнить эту проверку, прежде чем изменять саму сущность. Также см. ответ Руны о размещении ограничения в БД для обеспечения соблюдения этого ограничения. Эта проверка не для того, чтобы предотвратить дубликаты, а для того, чтобы попытаться обнаружить их раньше, чтобы вы могли представить более приятное сообщение об ошибке и не пытаться зафиксировать изменение, которое, как вы знаете, завершится неудачей.

Почему это происходит (FlushMode)

Это сделано для того, чтобы результаты вашего запроса соответствовали изменениям, которые вы внесли локально. Поскольку NHibernate пытается смоделировать ваши сущности так, как будто они всегда доступны в памяти, автоматически сохраняя их в базе данных при необходимости.

Вы можете изменить это, посмотрев на настройку автоматического смыва. Существует значение по умолчанию на фабрике сеансов, используемое для всех вновь создаваемых сеансов, и индивидуальный параметр для каждого сеанса.

Значением по умолчанию является FlushMode.Auto, которое будет сохраняться при фиксации транзакции или перед выполнением любого запроса, на который могут повлиять изменения в памяти. Если вы измените его на FlushMode.Commit, то он сохранит изменения в базе данных только при фиксации транзакции или при сохранении нового объекта, который использует стратегию первичного ключа, например, идентификатор.

Вы также можете вручную сохранить изменения текущего сеанса обратно в базу данных в любое время, используя Session.Flush.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...