在子項集合中有一個項目在其父項中執行SQL選擇時獲得延遲加載後,會爲此子項執行更新語句 - 而不顯式調用更新。NHibernate - 沒有明確更新的意外更新
父映射:
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
namespace="ParentEntity"
assembly="ParentEntity">
<class name="ParentEntity" table="ParentEntity">
<id name="Id" column="ParentEntityId" unsaved-value="-1">
<generator class="identity"/>
</id>
<bag name="addresses" access="field" inverse="true" cascade="all-delete-orphan" where="IsDeleted = 0">
<key column="ParentEntityId"/>
<one-to-many class="Address"/>
</bag>
</class>
</hibernate-mapping>
實現:
public class ParentEntity : IEntity<ParentEntity>, IAuditableEntity, IDeletableEntity
{
private ICollection<Address> addresses;
protected ParentEntity()
{
addresses = new List<Address>();
}
public virtual ICollection<Address> Addresses
{
get
{
return new List<Address>(addresses.Where(a => !a.IsDeleted && !a.Validity.IsExpired)).AsReadOnly();
}
private set
{
addresses = value;
}
}
public virtual ICollection<Address> ExpiredAddresses
{
get
{
return new List<Address>(addresses.Where(a => !a.IsDeleted && a.Validity.IsExpired)).AsReadOnly();
}
}
#region IAuditableEntity Members
public virtual EntityTimestamp Timestamp
{
get { return timestamp; }
set { timestamp = value; }
}
#endregion
public virtual bool AddAddress(Address address)
{
if (addresses.Contains(address) || ExpiredAddresses.Contains(address))
return false;
address.ParentEntity = this;
addresses.Add(address);
return true;
}
public virtual bool RemoveAddress(Address address)
{
if (!addresses.Contains(address) && !ExpiredAddresses.Contains(address))
return false;
address.IsDeleted = true;
return true;
}
}
兒童映射:
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
namespace="..."
assembly="...">
<class name="Address" table="Address">
<id name="Id" column="AddressId" unsaved-value="-1">
<generator class="identity"/>
</id>
<property name="Street" ></property>
<property name="StreetNumber" ></property>
<property name="PostOfficeBox" ></property>
<property name="IsDeleted" not-null="true" ></property>
<many-to-one name="City" not-null="true" column="CityId" lazy="false" cascade="none" fetch="join" class="City"></many-to-one>
<many-to-one name="Type" not-null="true" column="AddressTypeId" lazy="false" cascade="none" fetch="join" class="AddressType"></many-to-one>
<many-to-one name="ParentEntity" not-null="true" update="false" column="ParentEntityId" lazy="false" cascade="none" fetch="join" class="ParentEntity"></many-to-one>
<component name="Timestamp" class="EntityTimestamp">
<property name="CreatedOn" not-null="true" />
<component name="CreatedBy" class="User">
<property name="Name" not-null="true" column="CreatedBy" />
</component>
<property name="ChangedOn" not-null="true" />
<component name="ChangedBy" class="User">
<property name="Name" not-null="true" column="ChangedBy" />
</component>
</component>
</class>
</hibernate-mapping>
兒童實施:
public class Address : IEntity<Address>, IAuditableEntity, IDeletableEntity
{
// id etc...
private EntityTimestamp timestamp;
private City city;
private bool isDeleted;
private string street;
private string postOfficeBox;
private string streetNumber;
private Validity validity;
private AddressType type;
private ParentEntity parentEntity;
public virtual EntityTimestamp Timestamp
{
get { return timestamp; }
set { timestamp = value; }
}
public virtual bool IsDeleted
{
get { return isDeleted; }
set { isDeleted = value; }
}
public virtual string Street
{
get { return street; }
set { street = value; }
}
public virtual string StreetNumber
{
get { return streetNumber; }
set { streetNumber = value; }
}
public virtual string PostOfficeBox
{
get { return postOfficeBox; }
set { postOfficeBox = value; }
}
public virtual City City
{
get { return city; }
set { city = value; }
}
public virtual AddressType Type
{
get { return type; }
set { type = value; }
}
public virtual Validity Validity
{
get { return validity; }
set { validity = value; }
}
protected internal virtual ParentEntity ParentEntity
{
get { return parentEntity; }
set { parentEntity = value; }
}
protected Address()
{
}
public Address(Validity validity)
{
this.validity = validity;
}
}
的entitiy時間戳的樣子:
公共類EntityTimestamp:IValueObject { 私人的DateTime createdOn;
public virtual DateTime CreatedOn
{
get { return createdOn; }
private set { createdOn = value; }
}
private IUser createdBy;
public virtual IUser CreatedBy
{
get { return createdBy; }
private set { createdBy = value; }
}
private DateTime changedOn;
public virtual DateTime ChangedOn
{
get { return changedOn; }
private set { changedOn = value; }
}
private IUser changedBy;
public virtual IUser ChangedBy
{
get { return changedBy; }
private set { changedBy = value; }
}
protected EntityTimestamp()
{
}
private EntityTimestamp(DateTime createdOn, IUser createdBy, DateTime changedOn, IUser changedBy)
{
if (createdBy == null)
throw new ArgumentException("Created by user is null.");
if (changedBy == null)
throw new ArgumentException("Changed by user is null.");
this.createdOn = createdOn;
this.createdBy = createdBy;
this.changedBy = changedBy;
this.changedOn = changedOn;
}
public static EntityTimestamp New()
{
return new EntityTimestamp(new DateTimePrecise().Now, SecurityService.Current.GetCurrentUser(), new DateTimePrecise().Now, SecurityService.Current.GetCurrentUser());
}
public static EntityTimestamp New(IUser forUser)
{
return new EntityTimestamp(new DateTimePrecise().Now, forUser, new DateTimePrecise().Now, forUser);
}
public static EntityTimestamp NewUpdated(IUser forUser, EntityTimestamp oldTimestamp)
{
return new EntityTimestamp(oldTimestamp.CreatedOn, oldTimestamp.CreatedBy, new DateTimePrecise().Now, forUser);
}
public static EntityTimestamp NewUpdated(EntityTimestamp oldTimestamp)
{
return new EntityTimestamp(oldTimestamp.CreatedOn, oldTimestamp.CreatedBy, new DateTimePrecise().Now, SecurityService.Current.GetCurrentUser());
}
}
時間戳設置了事件偵聽器內:
public class EntitySaveEventListener : NHibernate.Event.Default.DefaultSaveEventListener
{
protected override object PerformSaveOrUpdate(SaveOrUpdateEvent e)
{
if (e.Entity is IAuditableEntity)
{
var entity = e.Entity as IAuditableEntity;
//todo: CascadeBeforeSave();
if (entity != null)
{
IsDirtyEntity(e.Session, e.Entity);
if (entity.IsNew)
{
entity.Timestamp = EntityTimestamp.New();
}
else
{
entity.Timestamp = EntityTimestamp.NewUpdated(entity.Timestamp);
}
}
}
return base.PerformSaveOrUpdate(e);
}
所以執行父設備上的SQL選擇時,則執行地址實體的更新。
通過使用另一種方法,我已經檢查過地址是否被自動更新之前傳遞給事件偵聽器,如果它是髒的。但所有的道具似乎都是一樣的。
這可能是什麼?你需要更多信息?
我檢查,如果該地址已被弄髒更新的方法:
public static Boolean IsDirtyEntity(ISession session, Object entity)
{
String className = NHibernateProxyHelper.GuessClass(entity).FullName;
ISessionImplementor sessionImpl = session.GetSessionImplementation();
IPersistenceContext persistenceContext = sessionImpl.PersistenceContext;
IEntityPersister persister = sessionImpl.Factory.GetEntityPersister(className);
EntityEntry oldEntry = sessionImpl.PersistenceContext.GetEntry(entity);
if ((oldEntry == null) && (entity is INHibernateProxy))
{
INHibernateProxy proxy = entity as INHibernateProxy;
Object obj = sessionImpl.PersistenceContext.Unproxy(proxy);
oldEntry = sessionImpl.PersistenceContext.GetEntry(obj);
}
Object [] oldState = oldEntry.LoadedState;
Object [] currentState = persister.GetPropertyValues(entity, sessionImpl.EntityMode);
Int32 [] dirtyProps = persister.FindDirty(currentState, oldState, entity, sessionImpl);
return (dirtyProps != null);
}
NHibernate的SQL調試:
//父實體選擇
NHibernate.SQL :2010-02-17 16:18:39,357 [21] DEBUG NHibernate.SQL [(null) ] - 。 SELECT * FROM( SELECT SPR *, SPFT [等級], ROW_NUMBER()OVER(ORDER BY SPFT [等級] DESC)AS ROWNUM FROM CONTAINSTABLE(ParentEntitySpecialTable, 計算機, ''some text'') AS spft INNER JOIN ParentEntity spr ON spr.ParentEntityId = spft。[鍵]
) AS Results WHERE RowNum BETWEEN (@p0 - 1) * @p1 + 1 AND @p2 * @p3 ORDER BY [Rank] DESC;@p0 = 1, @p1 = 20, @p2 = 1, @p3 = 20
NHibernate.SQL:2010-02-17 16:18:39513 [21] DEBUG NHibernate.SQL [(空)] - 選擇 addresses0_.ParentEntityId如 ServiceP8_3_,addresses0_。 AddressId爲 AddressId3_,addresses0_.AddressId爲 AddressId11_2_,addresses0_.Street爲 Street11_2_,addresses0_.StreetNumber 爲StreetNu3_11_2_, addresses0_.PostOfficeBox爲 PostOffi4_11_2_,addresses0_.IsDeleted 爲IsDeleted11_2_,addresses0_.CityId 爲CityId 11_2_, addresses0_.AddressTypeId如 AddressT7_11_2_, addresses0_.ParentEntityId如 ServiceP8_11_2_, addresses0_.ValidityPeriodFrom如 Validity9_11_2_, addresses0_.ValidityPeriodTo如 Validit10_11_2_,addresses0_.CreatedOn 如CreatedOn11_2_, addresses0_.CreatedBy如 CreatedBy11_2_,addresses0_ .ChangedOn 如ChangedOn11_2_, addresses0_.ChangedBy如 ChangedBy11_2_,city1_.CityId如 CityId9_0_,city1_.IsDeleted如 IsDeleted9_0_,city1_.Name如 Name9_0_,CIT y1_.ZipCode如 ZipCode9_0_,city1_.CountryId如 CountryId9_0_,city1_.CreatedOn如 CreatedOn9_0_,city1_.CreatedBy如 CreatedBy9_0_,city1_.ChangedOn如 ChangedOn9_0_,city1_.ChangedBy如 ChangedBy9_0_, addresstyp2_.AddressTypeId如 AddressT1_6_1_, addresstyp2_.IsDeleted 如IsDeleted6_1_, addresstyp2_.IsSystemDefault如 IsSystem3_6_1_,addresstyp2_.Name如 Name6_1_,addresstyp2 _ [鍵]如 column5_6_1_,addresstyp2_.CreatedOn 如CreatedOn6_1_, addresstyp2_.CreatedBy如 CreatedBy6_1_,一個ddresstyp2_.ChangedOn 爲ChangedOn6_1_, addresstyp2_.ChangedBy作爲 ChangedBy6_1_發件人地址addresses0_ 內加入市city1_上 addresses0_.CityId = city1_.CityId內 上 addresses0_.AddressTypeId = addresstyp2_.AddressTypeId WHERE(addresses0_加入地址類型addresstyp2_。請將isDeleted = 0)和 [email protected]; @ P0 = 345625 'ASPNET_WP.EXE'(管理): 加載 'CountryProxyAssembly' 'ASPNET_WP.EXE'(管理):加載 'CountryProxyModule'
//地址i š更新
NHibernate.SQL:
2010-02-17 16:18:51607 [21] DEBUG NHibernate的。SQL [(null)] - 批處理 命令:命令0:UPDATE地址SET Street = @ p0,StreetNumber = @ p1, PostOfficeBox = @ p2,IsDeleted = @ p3, CityId = @ p4,AddressTypeId = @ p5, ValidityPeriodFrom = @ P6, ValidityPeriodTo = @ P7,CreatedOn = @ P8,CreatedBy = @ P9,ChangedOn = @ P10,ChangedBy = @ P11 WHERE AddressId = @ P12; @ P0 = 'FFF',@ P1 = '',@ p2 = NULL,@ p3 = False,@ p4 = 116644,@ p5 = 1,@ p6 = 20.01.2010 17:28:15,@ p7 = 31.12.9999 00:00:00, @ p8 = 20.01.2010 17:29:52,@ p9 = 'fff',@ p10 = 17.02.2010 16:18:51, @ p11 ='fff',@ p12 = 117390
//地址被更新
NHibernate.SQL:
2010-02-17 16:19:03748 [21] DEBUG NHibernate.SQL [(空)] - 批量 命令:命令0:更新地址SET 街= @ P0,StreetNumber = @ P1, PostOfficeBox = @ P2,請將isDeleted = @ P3, CityId = @ P4,AddressTypeId = @ P5, ValidityPeriodFrom = @ P6, ValidityPeriodTo = @ p7,Crea tedOn = @ p8,CreatedBy = @ p9,ChangedOn = @ p10,ChangedBy = @ p11 WHERE AddressId = @ p12; @ p0 ='fff',@ p1 ='',@ p2 = NULL,@ p3 = False ,@ p4 = 116644,@ p5 = 1,@ p6 = 20.01.2010 17:28:15,@ p7 = 31.12.9999 00:00:00, @ p8 = 20.01.2010 17:29:52, @ P9 = 'FFF',@ P10 = 2010年2月17日16時19分03秒, @ P11 = 'FFF',@ P12 = 117390
如果你註釋掉的部分,你更新的時間戳在SaveEventListener中。我懷疑是造成這個問題......如果是這樣,我會發布我如何解決這個問題。 – dotjoe 2010-02-17 16:18:32
是的,你是對的。如果從地址中刪除時間戳記,或者如果我評論設置在事件偵聽器中的時間戳記,則不會執行意外更新。 – Chris 2010-02-17 16:40:18