Nhibernate doing updates on select?
Asked Answered
H

2

6

I have the following class:

public class Product
{
  public virtual Guid Id { get; set; }
  public virtual string Name { get; set; }
  public virtual Decimal PricePerMonth { get; set; }
  public virtual BillingInterval DefaultBillingInterval { get; set; }
  public virtual string AdditionalInfo { get; set; }
}

and the mapping looks like this:

 <class name="Product" table="Products">
    <id name="Id" column="ProductId">
      <generator class="guid.comb"/>
    </id>
    <property name="Name" column="ProductName" not-null="true" type="String" />
    <property name="PricePerMonth" column="PricePerMonth" not-null="true" type="Decimal" />
    <property name="DefaultBillingInterval" type="int" not-null="true" />
    <property name="AdditionalInfo" type="string" not-null="false" />
</class>

I use a Repository<T> class with the following method (Session is a property that returns the current session):

public IEnumerable<T> FindAll(DetachedCriteria criteria)
{
  return criteria.GetExecutableCriteria(Session).List<T>();
}

Now when I do the following (the session is the same session used in the repository):

IEnumerable<ProductDTO> productDTOs = null;
using(ITransaction tx = session.BeginTransaction(IsolationLevel.ReadCommitted))
{
    var products = repository.FindAll(new DetachedCriteria.For<Product>().Add(Restrictions.Like("Name", "Some Product%")));
    productDTOs = ToDTOs(products);
    tx.Commit();
}
// Do stuff with DTO's

The commit statement is there, because I use a service layer which automatically commits every transaction if no errors occured. I just collapsed my service layer here for easier visualization..

My ToDTOs method simply converts to a DTO:

private IEnumerable<ProductDTO> ToDTO(IEnumerable<Product> products)
{
  return products.Select(x => new ProductDTO()
    {
      Id = x.Id,
      Name = x.Name,
      PricePerMonth = x.PricePerMonth,
      AdditionalInfo = x.AdditionalInfo
    }).ToList();
}

My nhibernate log shows the following output:

2010-01-04 19:13:11,140 [4] DEBUG NHibernate.SQL - SELECT ... From Products ...
2010-01-04 19:13:11,237 [4] DEBUG NHibernate.SQL - UPDATE Products ...
2010-01-04 19:13:11,548 [4] DEBUG NHibernate.SQL - UPDATE Products ...
...

So by selecting the products it issues an update statement for every product returned when the session commits, even though nothing has been changed in the products..

Any ideas?

Helton answered 4/1, 2010 at 18:34 Comment(2)
Is this really how the entity is implemented? I don't believe you.Maltzman
It is... True, I had a list of orders to which the product was attached, but I have removed this list, both from the class and the mapping (like shown here) and it is the same result.. My confusion is even more profound by the fact that this does not happen to my other entities..Helton
M
7

I only had this effect when I had an entity that does not return the same value from the property than the value that has been assigned to it. Then it is treated as dirty by NH.

Example:

class Foo
{
  private string name;

  public string Name 
  { 
    // does not return null when null had been set
    get { return name ?? "No Name"; }
    set { name = value; }
  }

}

This is how I would write the mapping file.

<class name="Product" table="Products">
    <id name="Id" column="ProductId">
      <generator class="guid.comb"/>
    </id>
    <property name="Name" column="ProductName" not-null="true" />
    <property name="PricePerMonth" not-null="true" />
    <property name="DefaultBillingInterval" not-null="true" />
    <property name="AdditionalInfo" />
</class>

You don't need to specify types. They are determined by NHibernate at runtime.

Maltzman answered 4/1, 2010 at 18:53 Comment(5)
Aha, so it must be the BillingInterval enum that is causing the problem since it is mapped as an int? How do I map an enum (backed by an int) without the update statements occuring?Helton
You don't need to specify any type when mapping enums.Maltzman
Does this work even when the database stores DefaultBillingInterval as a smallint (the enum values has the integer values 3,6,12)?Helton
Just try it ... there is also an sql-type attribute, which is probably only needed for CreateSchema.Maltzman
How would I fix this if I was using FluentNhibernateJacklin
S
2

Older post, but maybe this will help someone down the road.

I have a Data Repository C# Class Library (Oracle as the database). The table value was NULL but in my repo the value was defined as Decimal and should have been ?Decimal. That fixed this update issue when running a select.

Scatterbrain answered 4/5, 2017 at 18:42 Comment(1)
Another scenario where you can get an unanticipated UPDATE is when a new property has been added to the database mapping and a record is read from a database created with the older version of the mapping.Dobruja

© 2022 - 2024 — McMap. All rights reserved.