|
From: <jul...@us...> - 2011-02-11 16:07:48
|
Revision: 5380
http://nhibernate.svn.sourceforge.net/nhibernate/?rev=5380&view=rev
Author: julian-maughan
Date: 2011-02-11 16:07:38 +0000 (Fri, 11 Feb 2011)
Log Message:
-----------
Port of Hibernate read-only entities feature, including tests (NH-908)
Modified Paths:
--------------
branches/ReadOnlyEntities/nhibernate/src/NHibernate/Engine/EntityEntry.cs
branches/ReadOnlyEntities/nhibernate/src/NHibernate/Engine/IPersistenceContext.cs
branches/ReadOnlyEntities/nhibernate/src/NHibernate/Engine/QueryParameters.cs
branches/ReadOnlyEntities/nhibernate/src/NHibernate/Engine/StatefulPersistenceContext.cs
branches/ReadOnlyEntities/nhibernate/src/NHibernate/Engine/TwoPhaseLoad.cs
branches/ReadOnlyEntities/nhibernate/src/NHibernate/Event/Default/AbstractFlushingEventListener.cs
branches/ReadOnlyEntities/nhibernate/src/NHibernate/Event/Default/AbstractReassociateEventListener.cs
branches/ReadOnlyEntities/nhibernate/src/NHibernate/Event/Default/AbstractSaveEventListener.cs
branches/ReadOnlyEntities/nhibernate/src/NHibernate/Event/Default/DefaultAutoFlushEventListener.cs
branches/ReadOnlyEntities/nhibernate/src/NHibernate/Event/Default/DefaultDeleteEventListener.cs
branches/ReadOnlyEntities/nhibernate/src/NHibernate/Event/Default/DefaultEvictEventListener.cs
branches/ReadOnlyEntities/nhibernate/src/NHibernate/Event/Default/DefaultFlushEntityEventListener.cs
branches/ReadOnlyEntities/nhibernate/src/NHibernate/Event/Default/DefaultFlushEventListener.cs
branches/ReadOnlyEntities/nhibernate/src/NHibernate/Event/Default/DefaultLoadEventListener.cs
branches/ReadOnlyEntities/nhibernate/src/NHibernate/Event/Default/DefaultRefreshEventListener.cs
branches/ReadOnlyEntities/nhibernate/src/NHibernate/Event/Default/DefaultReplicateEventListener.cs
branches/ReadOnlyEntities/nhibernate/src/NHibernate/Event/Default/DefaultSaveOrUpdateEventListener.cs
branches/ReadOnlyEntities/nhibernate/src/NHibernate/Hql/Ast/ANTLR/Loader/QueryLoader.cs
branches/ReadOnlyEntities/nhibernate/src/NHibernate/Hql/Ast/ANTLR/QueryTranslatorImpl.cs
branches/ReadOnlyEntities/nhibernate/src/NHibernate/Hql/Classic/QueryTranslator.cs
branches/ReadOnlyEntities/nhibernate/src/NHibernate/Hql/IQueryTranslator.cs
branches/ReadOnlyEntities/nhibernate/src/NHibernate/ICriteria.cs
branches/ReadOnlyEntities/nhibernate/src/NHibernate/IQuery.cs
branches/ReadOnlyEntities/nhibernate/src/NHibernate/ISession.cs
branches/ReadOnlyEntities/nhibernate/src/NHibernate/Impl/AbstractQueryImpl.cs
branches/ReadOnlyEntities/nhibernate/src/NHibernate/Impl/CriteriaImpl.cs
branches/ReadOnlyEntities/nhibernate/src/NHibernate/Impl/EnumerableImpl.cs
branches/ReadOnlyEntities/nhibernate/src/NHibernate/Impl/SessionImpl.cs
branches/ReadOnlyEntities/nhibernate/src/NHibernate/Impl/StatelessSessionImpl.cs
branches/ReadOnlyEntities/nhibernate/src/NHibernate/Loader/Criteria/CriteriaQueryTranslator.cs
branches/ReadOnlyEntities/nhibernate/src/NHibernate/Loader/Loader.cs
branches/ReadOnlyEntities/nhibernate/src/NHibernate/Persister/Entity/AbstractEntityPersister.cs
branches/ReadOnlyEntities/nhibernate/src/NHibernate/Proxy/AbstractLazyInitializer.cs
branches/ReadOnlyEntities/nhibernate/src/NHibernate/Proxy/ILazyInitializer.cs
branches/ReadOnlyEntities/nhibernate/src/NHibernate.Test/Legacy/FooBarTest.cs
branches/ReadOnlyEntities/nhibernate/src/NHibernate.Test/NHibernate.Test.csproj
Added Paths:
-----------
branches/ReadOnlyEntities/nhibernate/src/NHibernate.Test/Immutable/
branches/ReadOnlyEntities/nhibernate/src/NHibernate.Test/Immutable/Contract.cs
branches/ReadOnlyEntities/nhibernate/src/NHibernate.Test/Immutable/ContractVariation.cs
branches/ReadOnlyEntities/nhibernate/src/NHibernate.Test/Immutable/ContractVariation.hbm.xml
branches/ReadOnlyEntities/nhibernate/src/NHibernate.Test/Immutable/EntityWithMutableCollection/
branches/ReadOnlyEntities/nhibernate/src/NHibernate.Test/Immutable/EntityWithMutableCollection/AbstractEntityWithManyToManyTest.cs
branches/ReadOnlyEntities/nhibernate/src/NHibernate.Test/Immutable/EntityWithMutableCollection/AbstractEntityWithOneToManyTest.cs
branches/ReadOnlyEntities/nhibernate/src/NHibernate.Test/Immutable/EntityWithMutableCollection/Contract.cs
branches/ReadOnlyEntities/nhibernate/src/NHibernate.Test/Immutable/EntityWithMutableCollection/ContractVariation.cs
branches/ReadOnlyEntities/nhibernate/src/NHibernate.Test/Immutable/EntityWithMutableCollection/Info.cs
branches/ReadOnlyEntities/nhibernate/src/NHibernate.Test/Immutable/EntityWithMutableCollection/Inverse/
branches/ReadOnlyEntities/nhibernate/src/NHibernate.Test/Immutable/EntityWithMutableCollection/Inverse/ContractVariation.hbm.xml
branches/ReadOnlyEntities/nhibernate/src/NHibernate.Test/Immutable/EntityWithMutableCollection/Inverse/ContractVariationOneToManyJoin.hbm.xml
branches/ReadOnlyEntities/nhibernate/src/NHibernate.Test/Immutable/EntityWithMutableCollection/Inverse/ContractVariationVersioned.hbm.xml
branches/ReadOnlyEntities/nhibernate/src/NHibernate.Test/Immutable/EntityWithMutableCollection/Inverse/ContractVariationVersionedOneToManyJoin.hbm.xml
branches/ReadOnlyEntities/nhibernate/src/NHibernate.Test/Immutable/EntityWithMutableCollection/Inverse/EntityWithInverseManyToManyTest.cs
branches/ReadOnlyEntities/nhibernate/src/NHibernate.Test/Immutable/EntityWithMutableCollection/Inverse/EntityWithInverseOneToManyJoinTest.cs
branches/ReadOnlyEntities/nhibernate/src/NHibernate.Test/Immutable/EntityWithMutableCollection/Inverse/EntityWithInverseOneToManyTest.cs
branches/ReadOnlyEntities/nhibernate/src/NHibernate.Test/Immutable/EntityWithMutableCollection/Inverse/VersionedEntityWithInverseManyToManyTest.cs
branches/ReadOnlyEntities/nhibernate/src/NHibernate.Test/Immutable/EntityWithMutableCollection/Inverse/VersionedEntityWithInverseOneToManyFailureExpectedTest.cs
branches/ReadOnlyEntities/nhibernate/src/NHibernate.Test/Immutable/EntityWithMutableCollection/Inverse/VersionedEntityWithInverseOneToManyJoinFailureExpectedTest.cs
branches/ReadOnlyEntities/nhibernate/src/NHibernate.Test/Immutable/EntityWithMutableCollection/Inverse/VersionedEntityWithInverseOneToManyJoinTest.cs
branches/ReadOnlyEntities/nhibernate/src/NHibernate.Test/Immutable/EntityWithMutableCollection/Inverse/VersionedEntityWithInverseOneToManyTest.cs
branches/ReadOnlyEntities/nhibernate/src/NHibernate.Test/Immutable/EntityWithMutableCollection/NonInverse/
branches/ReadOnlyEntities/nhibernate/src/NHibernate.Test/Immutable/EntityWithMutableCollection/NonInverse/ContractVariation.hbm.xml
branches/ReadOnlyEntities/nhibernate/src/NHibernate.Test/Immutable/EntityWithMutableCollection/NonInverse/ContractVariationOneToManyJoin.hbm.xml
branches/ReadOnlyEntities/nhibernate/src/NHibernate.Test/Immutable/EntityWithMutableCollection/NonInverse/ContractVariationUnidir.hbm.xml
branches/ReadOnlyEntities/nhibernate/src/NHibernate.Test/Immutable/EntityWithMutableCollection/NonInverse/ContractVariationVersioned.hbm.xml
branches/ReadOnlyEntities/nhibernate/src/NHibernate.Test/Immutable/EntityWithMutableCollection/NonInverse/ContractVariationVersionedOneToManyJoin.hbm.xml
branches/ReadOnlyEntities/nhibernate/src/NHibernate.Test/Immutable/EntityWithMutableCollection/NonInverse/EntityWithNonInverseManyToManyTest.cs
branches/ReadOnlyEntities/nhibernate/src/NHibernate.Test/Immutable/EntityWithMutableCollection/NonInverse/EntityWithNonInverseManyToManyUnidirTest.cs
branches/ReadOnlyEntities/nhibernate/src/NHibernate.Test/Immutable/EntityWithMutableCollection/NonInverse/EntityWithNonInverseOneToManyJoinTest.cs
branches/ReadOnlyEntities/nhibernate/src/NHibernate.Test/Immutable/EntityWithMutableCollection/NonInverse/EntityWithNonInverseOneToManyTest.cs
branches/ReadOnlyEntities/nhibernate/src/NHibernate.Test/Immutable/EntityWithMutableCollection/NonInverse/EntityWithNonInverseOneToManyUnidirTest.cs
branches/ReadOnlyEntities/nhibernate/src/NHibernate.Test/Immutable/EntityWithMutableCollection/NonInverse/VersionedEntityWithNonInverseManyToManyTest.cs
branches/ReadOnlyEntities/nhibernate/src/NHibernate.Test/Immutable/EntityWithMutableCollection/NonInverse/VersionedEntityWithNonInverseOneToManyJoinTest.cs
branches/ReadOnlyEntities/nhibernate/src/NHibernate.Test/Immutable/EntityWithMutableCollection/NonInverse/VersionedEntityWithNonInverseOneToManyTest.cs
branches/ReadOnlyEntities/nhibernate/src/NHibernate.Test/Immutable/EntityWithMutableCollection/Owner.cs
branches/ReadOnlyEntities/nhibernate/src/NHibernate.Test/Immutable/EntityWithMutableCollection/Party.cs
branches/ReadOnlyEntities/nhibernate/src/NHibernate.Test/Immutable/EntityWithMutableCollection/Plan.cs
branches/ReadOnlyEntities/nhibernate/src/NHibernate.Test/Immutable/ImmutableTest.cs
branches/ReadOnlyEntities/nhibernate/src/NHibernate.Test/Immutable/Info.cs
branches/ReadOnlyEntities/nhibernate/src/NHibernate.Test/Immutable/Party.cs
branches/ReadOnlyEntities/nhibernate/src/NHibernate.Test/Immutable/Plan.cs
branches/ReadOnlyEntities/nhibernate/src/NHibernate.Test/ReadOnly/
branches/ReadOnlyEntities/nhibernate/src/NHibernate.Test/ReadOnly/AbstractReadOnlyTest.cs
branches/ReadOnlyEntities/nhibernate/src/NHibernate.Test/ReadOnly/Container.cs
branches/ReadOnlyEntities/nhibernate/src/NHibernate.Test/ReadOnly/Course.cs
branches/ReadOnlyEntities/nhibernate/src/NHibernate.Test/ReadOnly/DataPoint.cs
branches/ReadOnlyEntities/nhibernate/src/NHibernate.Test/ReadOnly/DataPoint.hbm.xml
branches/ReadOnlyEntities/nhibernate/src/NHibernate.Test/ReadOnly/Enrolment.cs
branches/ReadOnlyEntities/nhibernate/src/NHibernate.Test/ReadOnly/Enrolment.hbm.xml
branches/ReadOnlyEntities/nhibernate/src/NHibernate.Test/ReadOnly/Info.cs
branches/ReadOnlyEntities/nhibernate/src/NHibernate.Test/ReadOnly/Owner.cs
branches/ReadOnlyEntities/nhibernate/src/NHibernate.Test/ReadOnly/ReadOnlyCriteriaQueryTest.cs
branches/ReadOnlyEntities/nhibernate/src/NHibernate.Test/ReadOnly/ReadOnlyProxyTest.cs
branches/ReadOnlyEntities/nhibernate/src/NHibernate.Test/ReadOnly/ReadOnlySessionLazyNonLazyTest.cs
branches/ReadOnlyEntities/nhibernate/src/NHibernate.Test/ReadOnly/ReadOnlySessionTest.cs
branches/ReadOnlyEntities/nhibernate/src/NHibernate.Test/ReadOnly/ReadOnlyTest.cs
branches/ReadOnlyEntities/nhibernate/src/NHibernate.Test/ReadOnly/ReadOnlyVersionedNodes.cs
branches/ReadOnlyEntities/nhibernate/src/NHibernate.Test/ReadOnly/Student.cs
branches/ReadOnlyEntities/nhibernate/src/NHibernate.Test/ReadOnly/StudentDto.cs
branches/ReadOnlyEntities/nhibernate/src/NHibernate.Test/ReadOnly/TextHolder.cs
branches/ReadOnlyEntities/nhibernate/src/NHibernate.Test/ReadOnly/TextHolder.hbm.xml
branches/ReadOnlyEntities/nhibernate/src/NHibernate.Test/ReadOnly/VersionedNode.cs
branches/ReadOnlyEntities/nhibernate/src/NHibernate.Test/ReadOnly/VersionedNode.hbm.xml
Modified: branches/ReadOnlyEntities/nhibernate/src/NHibernate/Engine/EntityEntry.cs
===================================================================
--- branches/ReadOnlyEntities/nhibernate/src/NHibernate/Engine/EntityEntry.cs 2011-02-11 14:44:33 UTC (rev 5379)
+++ branches/ReadOnlyEntities/nhibernate/src/NHibernate/Engine/EntityEntry.cs 2011-02-11 16:07:38 UTC (rev 5380)
@@ -14,6 +14,7 @@
{
private LockMode lockMode;
private Status status;
+ private Status? previousStatus;
private readonly object id;
private object[] loadedState;
private object[] deletedState;
@@ -21,10 +22,11 @@
private object version;
[NonSerialized]
- private IEntityPersister persister;// for convenience to save some lookups
+ private IEntityPersister persister; // for convenience to save some lookups
private readonly EntityMode entityMode;
private readonly string entityName;
+ private EntityKey cachedEntityKey;
private readonly bool isBeingReplicated;
private readonly bool loadedWithLazyPropertiesUnfetched;
@@ -50,9 +52,11 @@
bool disableVersionIncrement, bool lazyPropertiesAreUnfetched)
{
this.status = status;
- this.loadedState = loadedState;
+ this.previousStatus = null;
+ // only retain loaded state if the status is not Status.ReadOnly
+ if (status != Status.ReadOnly) { this.loadedState = loadedState; }
+ this.id = id;
this.rowId = rowId;
- this.id = id;
this.existsInDatabase = existsInDatabase;
this.version = version;
this.lockMode = lockMode;
@@ -74,7 +78,7 @@
}
/// <summary>
- /// Gets or sets the <see cref="Status"/> of this Entity with respect to its
+ /// Gets or sets the <see cref="Status"/> of this Entity with respect to its
/// persistence in the database.
/// </summary>
/// <value>The <see cref="Status"/> of this Entity.</value>
@@ -86,7 +90,11 @@
if (value == Status.ReadOnly)
loadedState = null; //memory optimization
- status = value;
+ if (this.status != value)
+ {
+ previousStatus = this.status;
+ this.status = value;
+ }
}
}
@@ -94,7 +102,7 @@
/// Gets or sets the identifier of the Entity in the database.
/// </summary>
/// <value>The identifier of the Entity in the database if one has been assigned.</value>
- /// <remarks>This might be <see langword="null" /> when the <see cref="EntityEntry.Status"/> is
+ /// <remarks>This might be <see langword="null" /> when the <see cref="EntityEntry.Status"/> is
/// <see cref="Engine.Status.Saving"/> and the database generates the id.</remarks>
public object Id
{
@@ -129,7 +137,7 @@
/// </summary>
/// <value><see langword="true" /> if it is already in the database.</value>
/// <remarks>
- /// It can also be <see langword="true" /> if it does not exists in the database yet and the
+ /// It can also be <see langword="true" /> if it does not exists in the database yet and the
/// <see cref="IEntityPersister.IsIdentifierAssignedByInsert"/> is <see langword="true" />.
/// </remarks>
public bool ExistsInDatabase
@@ -179,15 +187,33 @@
{
get { return loadedWithLazyPropertiesUnfetched; }
}
+
+ /// <summary>
+ /// Get the EntityKey based on this EntityEntry.
+ /// </summary>
+ public EntityKey EntityKey
+ {
+ get
+ {
+ if (cachedEntityKey == null)
+ {
+ if (id == null)
+ throw new InvalidOperationException("cannot generate an EntityKey when id is null.");
+ cachedEntityKey = new EntityKey(id, persister, entityMode);
+ }
+ return cachedEntityKey;
+ }
+ }
+
public object GetLoadedValue(string propertyName)
{
int propertyIndex = ((IUniqueKeyLoadable) persister).GetPropertyIndex(propertyName);
return loadedState[propertyIndex];
}
- /// <summary>
- /// After actually inserting a row, record the fact that the instance exists on the
+ /// <summary>
+ /// After actually inserting a row, record the fact that the instance exists on the
/// database (needed for identity-column key generation)
/// </summary>
public void PostInsert()
@@ -213,12 +239,13 @@
FieldInterceptionHelper.ClearDirty(entity);
}
- /// <summary>
+ /// <summary>
/// After actually deleting a row, record the fact that the instance no longer
/// exists in the database
/// </summary>
public void PostDelete()
{
+ previousStatus = status;
status = Status.Gone;
existsInDatabase = false;
}
@@ -230,7 +257,7 @@
LockMode = LockMode.Force;
persister.SetPropertyValue(entity, Persister.VersionProperty, nextVersion, entityMode);
}
-
+
public bool IsNullifiable(bool earlyInsert, ISessionImplementor session)
{
return Status == Status.Saving || (earlyInsert ? !ExistsInDatabase : session.PersistenceContext.NullifiableEntityKeys.Contains(new EntityKey(Id, Persister, entityMode)));
@@ -238,21 +265,42 @@
public bool RequiresDirtyCheck(object entity)
{
- bool isMutableInstance = status != Status.ReadOnly && persister.IsMutable;
-
return
- isMutableInstance
- &&
- (Persister.HasMutableProperties || !FieldInterceptionHelper.IsInstrumented(entity)
- || FieldInterceptionHelper.ExtractFieldInterceptor(entity).IsDirty);
+ IsModifiableEntity()
+ && (Persister.HasMutableProperties || !FieldInterceptionHelper.IsInstrumented(entity)
+ || FieldInterceptionHelper.ExtractFieldInterceptor(entity).IsDirty);
}
+
+ /// <summary>
+ /// Can the entity be modified?
+ /// The entity is modifiable if all of the following are true:
+ /// - the entity class is mutable
+ /// - the entity is not read-only
+ /// - if the current status is Status.Deleted, then the entity was not read-only when it was deleted
+ /// </summary>
+ /// <returns>true, if the entity is modifiable; false, otherwise</returns>
+ public bool IsModifiableEntity()
+ {
+ return (status != Status.ReadOnly) && !(status == Status.Deleted && previousStatus == Status.ReadOnly) && Persister.IsMutable;
+ }
+
+ public bool IsReadOnly
+ {
+ get
+ {
+ if (status != Status.Loaded && status != Status.ReadOnly)
+ {
+ throw new HibernateException("instance was not in a valid state");
+ }
+ return status == Status.ReadOnly;
+ }
+ }
public void SetReadOnly(bool readOnly, object entity)
{
- if (status != Status.Loaded && status != Status.ReadOnly)
- {
- throw new HibernateException("instance was not in a valid state");
- }
+ if (readOnly == IsReadOnly)
+ return; // simply return since the status is not being changed
+
if (readOnly)
{
Status = Status.ReadOnly;
@@ -260,6 +308,9 @@
}
else
{
+ if (!persister.IsMutable)
+ throw new InvalidOperationException("Cannot make an immutable entity modifiable.");
+
Status = Status.Loaded;
loadedState = Persister.GetPropertyValues(entity, entityMode);
}
Modified: branches/ReadOnlyEntities/nhibernate/src/NHibernate/Engine/IPersistenceContext.cs
===================================================================
--- branches/ReadOnlyEntities/nhibernate/src/NHibernate/Engine/IPersistenceContext.cs 2011-02-11 14:44:33 UTC (rev 5379)
+++ branches/ReadOnlyEntities/nhibernate/src/NHibernate/Engine/IPersistenceContext.cs 2011-02-11 16:07:38 UTC (rev 5380)
@@ -9,25 +9,25 @@
namespace NHibernate.Engine
{
- /// <summary>
- /// Holds the state of the persistence context, including the
- /// first-level cache, entries, snapshots, proxies, etc.
+ /// <summary>
+ /// Holds the state of the persistence context, including the
+ /// first-level cache, entries, snapshots, proxies, etc.
/// </summary>
public interface IPersistenceContext
{
bool IsStateless { get;}
- /// <summary>
- /// Get the session to which this persistence context is bound.
+ /// <summary>
+ /// Get the session to which this persistence context is bound.
/// </summary>
ISessionImplementor Session { get;}
- /// <summary>
+ /// <summary>
/// Retrieve this persistence context's managed load context.
/// </summary>
LoadContexts LoadContexts { get;}
- /// <summary>
+ /// <summary>
/// Get the <tt>BatchFetchQueue</tt>, instantiating one if necessary.
/// </summary>
BatchFetchQueue BatchFetchQueue { get;}
@@ -52,12 +52,47 @@
/// <summary>Is a flush cycle currently in process?</summary>
/// <remarks>Called before and after the flushcycle</remarks>
- bool Flushing { get;set;}
+ bool Flushing { get; set;}
+
+ /// <summary>
+ /// Change the default for entities and proxies loaded into this persistence
+ /// context from modifiable to read-only mode, or from modifiable to read-only
+ /// mode.
+ /// </summary>
+ /// <remarks>
+ /// <para>
+ /// Read-only entities are not dirty-checked and snapshots of persistent
+ /// state are not maintained. Read-only entities can be modified, but
+ /// changes are not persisted.
+ /// </para>
+ /// <para>
+ /// When a proxy is initialized, the loaded entity will have the same
+ /// read-only/modifiable setting as the uninitialized
+ /// proxy has, regardless of the persistence context's current setting.
+ /// </para>
+ /// <para>
+ /// To change the read-only/modifiable setting for a particular entity
+ /// or proxy that is already in this session:
+ /// <see cref="SetReadOnly(object, bool)" />
+ /// <see cref="ISession.SetReadOnly(object, bool)" />
+ /// </para>
+ /// <para>
+ /// To determine the read-only/modifiable setting for a particular entity or proxy:
+ /// <see cref="IsReadOnly(object)" />
+ /// <see cref="ISession.IsReadOnly(object)" />
+ /// </para>
+ /// <para>
+ /// To override this session's read-only/modifiable setting for entities
+ /// and proxies loaded by an IQuery:
+ /// <see cref="IQuery.SetReadOnly(bool)" />
+ /// </para>
+ /// </remarks>
+ bool DefaultReadOnly { get; set; }
/// <summary> Add a collection which has no owner loaded</summary>
void AddUnownedCollection(CollectionKey key, IPersistentCollection collection);
- /// <summary>
+ /// <summary>
/// Get and remove a collection whose owner is not yet loaded,
/// when its owner is being loaded
/// </summary>
@@ -75,13 +110,13 @@
/// <summary> Called after transactions end</summary>
void AfterTransactionCompletion();
- /// <summary>
+ /// <summary>
/// Get the current state of the entity as known to the underlying
- /// database, or null if there is no corresponding row
+ /// database, or null if there is no corresponding row
/// </summary>
object[] GetDatabaseSnapshot(object id, IEntityPersister persister);
- /// <summary>
+ /// <summary>
/// Retrieve the cached database snapshot for the requested entity key.
/// </summary>
/// <param name="key">The entity key for which to retrieve the cached snapshot </param>
@@ -95,9 +130,9 @@
/// </remarks>
object[] GetCachedDatabaseSnapshot(EntityKey key);
- /// <summary>
- /// Get the values of the natural id fields as known to the underlying
- /// database, or null if the entity has no natural id or there is no
+ /// <summary>
+ /// Get the values of the natural id fields as known to the underlying
+ /// database, or null if the entity has no natural id or there is no
/// corresponding row.
/// </summary>
object[] GetNaturalIdSnapshot(object id, IEntityPersister persister);
@@ -105,7 +140,7 @@
/// <summary> Add a canonical mapping from entity key to entity instance</summary>
void AddEntity(EntityKey key, object entity);
- /// <summary>
+ /// <summary>
/// Get the entity instance associated with the given <tt>EntityKey</tt>
/// </summary>
object GetEntity(EntityKey key);
@@ -113,7 +148,7 @@
/// <summary> Is there an entity with the given key in the persistence context</summary>
bool ContainsEntity(EntityKey key);
- /// <summary>
+ /// <summary>
/// Remove an entity from the session cache, also clear
/// up other state associated with the entity, all except
/// for the <tt>EntityEntry</tt>
@@ -126,8 +161,8 @@
/// <summary> Add an entity to the cache by unique key</summary>
void AddEntity(EntityUniqueKey euk, object entity);
- /// <summary>
- /// Retrieve the EntityEntry representation of the given entity.
+ /// <summary>
+ /// Retrieve the EntityEntry representation of the given entity.
/// </summary>
/// <param name="entity">The entity for which to locate the EntityEntry. </param>
/// <returns> The EntityEntry for the given entity. </returns>
@@ -147,8 +182,8 @@
LockMode lockMode, bool existsInDatabase, IEntityPersister persister,
bool disableVersionIncrement, bool lazyPropertiesAreUnfetched);
- /// <summary>
- /// Generates an appropriate EntityEntry instance and adds it
+ /// <summary>
+ /// Generates an appropriate EntityEntry instance and adds it
/// to the event source's internal caches.
/// </summary>
EntityEntry AddEntry(object entity, Status status, object[] loadedState, object rowId, object id, object version,
@@ -161,34 +196,34 @@
/// <summary> Is the given proxy associated with this persistence context?</summary>
bool ContainsProxy(INHibernateProxy proxy);
- /// <summary>
- /// Takes the given object and, if it represents a proxy, reassociates it with this event source.
+ /// <summary>
+ /// Takes the given object and, if it represents a proxy, reassociates it with this event source.
/// </summary>
/// <param name="value">The possible proxy to be reassociated. </param>
/// <returns> Whether the passed value represented an actual proxy which got initialized. </returns>
bool ReassociateIfUninitializedProxy(object value);
- /// <summary>
+ /// <summary>
/// If a deleted entity instance is re-saved, and it has a proxy, we need to
- /// reset the identifier of the proxy
+ /// reset the identifier of the proxy
/// </summary>
void ReassociateProxy(object value, object id);
- /// <summary>
+ /// <summary>
/// Get the entity instance underlying the given proxy, throwing
/// an exception if the proxy is uninitialized. If the given object
/// is not a proxy, simply return the argument.
/// </summary>
object Unproxy(object maybeProxy);
- /// <summary>
- /// Possibly unproxy the given reference and reassociate it with the current session.
+ /// <summary>
+ /// Possibly unproxy the given reference and reassociate it with the current session.
/// </summary>
/// <param name="maybeProxy">The reference to be unproxied if it currently represents a proxy. </param>
/// <returns> The unproxied instance. </returns>
object UnproxyAndReassociate(object maybeProxy);
- /// <summary>
+ /// <summary>
/// Attempts to check whether the given key represents an entity already loaded within the
/// current session.
/// </summary>
@@ -196,11 +231,11 @@
/// <param name="key">The entity key.</param>
void CheckUniqueness(EntityKey key, object obj);
- /// <summary>
+ /// <summary>
/// If the existing proxy is insufficiently "narrow" (derived), instantiate a new proxy
/// and overwrite the registration of the old one. This breaks == and occurs only for
/// "class" proxies rather than "interface" proxies. Also init the proxy to point to
- /// the given target implementation if necessary.
+ /// the given target implementation if necessary.
/// </summary>
/// <param name="proxy">The proxy instance to be narrowed. </param>
/// <param name="persister">The persister for the proxied entity. </param>
@@ -209,14 +244,14 @@
/// <returns> An appropriately narrowed instance. </returns>
object NarrowProxy(INHibernateProxy proxy, IEntityPersister persister, EntityKey key, object obj);
- /// <summary>
+ /// <summary>
/// Return the existing proxy associated with the given <tt>EntityKey</tt>, or the
/// third argument (the entity associated with the key) if no proxy exists. Init
/// the proxy to the target implementation, if necessary.
/// </summary>
object ProxyFor(IEntityPersister persister, EntityKey key, object impl);
- /// <summary>
+ /// <summary>
/// Return the existing proxy associated with the given <tt>EntityKey</tt>, or the
/// argument (the entity associated with the key) if no proxy exists.
/// (slower than the form above)
@@ -228,7 +263,7 @@
/// <summary> Get the entity that owned this persistent collection when it was loaded </summary>
/// <param name="collection">The persistent collection </param>
- /// <returns>
+ /// <returns>
/// The owner if its entity ID is available from the collection's loaded key
/// and the owner entity is in the persistence context; otherwise, returns null
/// </returns>
@@ -245,7 +280,7 @@
/// <summary> add a detached uninitialized collection</summary>
void AddUninitializedDetachedCollection(ICollectionPersister persister, IPersistentCollection collection);
- /// <summary>
+ /// <summary>
/// Add a new collection (ie. a newly created one, just instantiated by the
/// application, with no database state or snapshot)
/// </summary>
@@ -253,7 +288,7 @@
/// <param name="persister"></param>
void AddNewCollection(ICollectionPersister persister, IPersistentCollection collection);
- /// <summary>
+ /// <summary>
/// add an (initialized) collection that was created by another session and passed
/// into update() (ie. one with a snapshot and existing state on the database)
/// </summary>
@@ -265,12 +300,12 @@
/// <summary> Get the collection instance associated with the <tt>CollectionKey</tt></summary>
IPersistentCollection GetCollection(CollectionKey collectionKey);
- /// <summary>
+ /// <summary>
/// Register a collection for non-lazy loading at the end of the two-phase load
/// </summary>
void AddNonLazyCollection(IPersistentCollection collection);
- /// <summary>
+ /// <summary>
/// Force initialization of all non-lazy collections encountered during
/// the current two-phase load (actually, this is a no-op, unless this
/// is the "outermost" load)
@@ -281,12 +316,12 @@
IPersistentCollection GetCollectionHolder(object array);
/// <summary> Register a <tt>PersistentCollection</tt> object for an array.
- /// Associates a holder with an array - MUST be called after loading
+ /// Associates a holder with an array - MUST be called after loading
/// array, since the array instance is not created until endLoad().
/// </summary>
void AddCollectionHolder(IPersistentCollection holder);
- /// <summary>
+ /// <summary>
/// Remove the mapping of collection to holder during eviction of the owning entity
/// </summary>
IPersistentCollection RemoveCollectionHolder(object array);
@@ -294,7 +329,7 @@
/// <summary> Get the snapshot of the pre-flush collection state</summary>
object GetSnapshot(IPersistentCollection coll);
- /// <summary>
+ /// <summary>
/// Get the collection entry for a collection passed to filter,
/// which might be a collection wrapper, an array, or an unwrapped
/// collection. Return null if there is no entry.
@@ -322,18 +357,18 @@
/// <summary> Call this after finishing a two-phase load</summary>
void AfterLoad();
- /// <summary>
+ /// <summary>
/// Search the persistence context for an owner for the child object,
/// given a collection role
/// </summary>
object GetOwnerId(string entity, string property, object childObject, IDictionary mergeMap);
- /// <summary>
+ /// <summary>
/// Search the persistence context for an index of the child object, given a collection role
/// </summary>
object GetIndexInOwner(string entity, string property, object childObject, IDictionary mergeMap);
- /// <summary>
+ /// <summary>
/// Record the fact that the association belonging to the keyed entity is null.
/// </summary>
void AddNullProperty(EntityKey ownerKey, string propertyName);
@@ -341,9 +376,56 @@
/// <summary> Is the association property belonging to the keyed entity null?</summary>
bool IsPropertyNull(EntityKey ownerKey, string propertyName);
- /// <summary> Set the object to read only and discard it's snapshot</summary>
+ /// <summary>
+ /// Set the entity or proxy to read only and discard it's snapshot.
+ /// Set an unmodified persistent object to read-only mode, or a read-only
+ /// object to modifiable mode.
+ /// </summary>
+ /// <remarks>
+ /// <para>
+ /// Read-only entities are not dirty-checked and snapshots of persistent
+ /// state are not maintained. Read-only entities can be modified, but
+ /// changes are not persisted.
+ /// </para>
+ /// <para>
+ /// When a proxy is initialized, the loaded entity will have the same
+ /// read-only/modifiable setting as the uninitialized
+ /// proxy has, regardless of the session's current setting.
+ /// </para>
+ /// <para>
+ /// If the entity or proxy already has the specified read-only/modifiable
+ /// setting, then this method does nothing.
+ /// </para>
+ /// <para>
+ /// To set the default read-only/modifiable setting used for
+ /// entities and proxies that are loaded into this persistence context:
+ /// <see cref="IPersistenceContext.DefaultReadOnly" />
+ /// <see cref="ISession.DefaultReadOnly" />
+ /// </para>
+ /// <para>
+ /// To override this persistence context's read-only/modifiable setting
+ /// for entities and proxies loaded by a Query:
+ /// <see cref="IQuery.SetReadOnly(bool)" />
+ /// </para>
+ /// </remarks>
+ /// <param name="entity">An entity or INHibernateProxy</param>
+ /// <param name="readOnly">if <c>true</c>, the entity or proxy is made read-only; if <c>false</c>, the entity or proxy is made modifiable.</param>
+ /// <seealso cref="ISession.SetReadOnly(object, bool)" />
void SetReadOnly(object entity, bool readOnly);
+ /// <summary>
+ /// Is the entity or proxy read-only?
+ /// </summary>
+ /// <remarks>
+ /// To get the default read-only/modifiable setting used for
+ /// entities and proxies that are loaded into the session:
+ /// <see cref="ISession.DefaultReadOnly" />
+ /// </remarks>
+ /// <returns>
+ /// <c>true</c>, the object is read-only; <c>false</c>, the object is modifiable.
+ /// </returns>
+ bool IsReadOnly(object entityOrProxy);
+
void ReplaceDelayedEntityIdentityInsertKeys(EntityKey oldKey, object generatedId);
}
}
Modified: branches/ReadOnlyEntities/nhibernate/src/NHibernate/Engine/QueryParameters.cs
===================================================================
--- branches/ReadOnlyEntities/nhibernate/src/NHibernate/Engine/QueryParameters.cs 2011-02-11 14:44:33 UTC (rev 5379)
+++ branches/ReadOnlyEntities/nhibernate/src/NHibernate/Engine/QueryParameters.cs 2011-02-11 16:07:38 UTC (rev 5380)
@@ -15,7 +15,7 @@
namespace NHibernate.Engine
{
/// <summary>
- /// Container for data that is used during the NHibernate query/load process.
+ /// Container for data that is used during the NHibernate query/load process.
/// </summary>
[Serializable]
public sealed class QueryParameters
@@ -39,6 +39,7 @@
private object _optionalObject;
private string _optionalEntityName;
private object _optionalId;
+ private bool _isReadOnlyInitialized;
private string _comment;
private bool _readOnly;
private int? limitParameterIndex = null;
@@ -46,18 +47,14 @@
private IDictionary<int, int> _adjustedParameterLocations;
private IDictionary<int, int> _tempPagingParameterIndexes;
private IDictionary<int, int> _pagingParameterIndexMap;
-
private SqlString processedSQL;
-
private readonly IResultTransformer _resultTransformer;
- // not implemented: private ScrollMode _scrollMode;
-
+
public QueryParameters() : this(ArrayHelper.EmptyTypeArray, ArrayHelper.EmptyObjectArray) {}
public QueryParameters(IType type, object value) : this(new[] {type}, new[] {value}) {}
- public QueryParameters(IType[] positionalParameterTypes, object[] postionalParameterValues, object optionalObject,
- string optionalEntityName, object optionalObjectId)
+ public QueryParameters(IType[] positionalParameterTypes, object[] postionalParameterValues, object optionalObject, string optionalEntityName, object optionalObjectId)
: this(positionalParameterTypes, postionalParameterValues)
{
_optionalObject = optionalObject;
@@ -66,32 +63,22 @@
}
public QueryParameters(IType[] positionalParameterTypes, object[] postionalParameterValues)
- : this(positionalParameterTypes, postionalParameterValues, null, null, false, null, null, false, null, null) {}
+ : this(positionalParameterTypes, postionalParameterValues, null, null, false, false, false, null, null, false, null, null) {}
public QueryParameters(IType[] positionalParameterTypes, object[] postionalParameterValues, object[] collectionKeys)
: this(positionalParameterTypes, postionalParameterValues, null, collectionKeys) {}
- public QueryParameters(IType[] positionalParameterTypes, object[] postionalParameterValues,
- IDictionary<string, TypedValue> namedParameters, object[] collectionKeys)
- : this(
- positionalParameterTypes, postionalParameterValues, namedParameters, null, null, false, false, null, null,
- collectionKeys, null) {}
+ public QueryParameters(IType[] positionalParameterTypes, object[] postionalParameterValues, IDictionary<string, TypedValue> namedParameters, object[] collectionKeys)
+ : this(positionalParameterTypes, postionalParameterValues, namedParameters, null, null, false, false, false, null, null, collectionKeys, null) {}
- public QueryParameters(IType[] positionalParameterTypes, object[] positionalParameterValues,
- IDictionary<string, LockMode> lockModes, RowSelection rowSelection, bool cacheable,
- string cacheRegion, string comment, bool isLookupByNaturalKey, IResultTransformer transformer, IDictionary<int,int> tempPagingParameterIndexes)
- : this(
- positionalParameterTypes, positionalParameterValues, null, lockModes, rowSelection, false, cacheable, cacheRegion,
- comment, null, transformer)
+ public QueryParameters(IType[] positionalParameterTypes, object[] positionalParameterValues, IDictionary<string, LockMode> lockModes, RowSelection rowSelection, bool isReadOnlyInitialized, bool readOnly, bool cacheable, string cacheRegion, string comment, bool isLookupByNaturalKey, IResultTransformer transformer, IDictionary<int,int> tempPagingParameterIndexes)
+ : this(positionalParameterTypes, positionalParameterValues, null, lockModes, rowSelection, isReadOnlyInitialized, readOnly, cacheable, cacheRegion, comment, null, transformer)
{
NaturalKeyLookup = isLookupByNaturalKey;
_tempPagingParameterIndexes = tempPagingParameterIndexes;
}
- public QueryParameters(IType[] positionalParameterTypes, object[] positionalParameterValues,
- IDictionary<string, TypedValue> namedParameters, IDictionary<string, LockMode> lockModes,
- RowSelection rowSelection, bool readOnly, bool cacheable, string cacheRegion, string comment,
- object[] collectionKeys, IResultTransformer transformer)
+ public QueryParameters(IType[] positionalParameterTypes, object[] positionalParameterValues, IDictionary<string, TypedValue> namedParameters, IDictionary<string, LockMode> lockModes, RowSelection rowSelection, bool isReadOnlyInitialized, bool readOnly, bool cacheable, string cacheRegion, string comment, object[] collectionKeys, IResultTransformer transformer)
{
_positionalParameterTypes = positionalParameterTypes;
_positionalParameterValues = positionalParameterValues;
@@ -102,25 +89,19 @@
_cacheRegion = cacheRegion;
_comment = comment;
_collectionKeys = collectionKeys;
+ _isReadOnlyInitialized = isReadOnlyInitialized;
_readOnly = readOnly;
_resultTransformer = transformer;
}
- public QueryParameters(IType[] positionalParameterTypes, object[] positionalParameterValues,
- IDictionary<string, TypedValue> namedParameters, IDictionary<string, LockMode> lockModes,
- RowSelection rowSelection, bool readOnly, bool cacheable, string cacheRegion, string comment,
- object[] collectionKeys, object optionalObject, string optionalEntityName, object optionalId,
- IResultTransformer transformer)
- : this(
- positionalParameterTypes, positionalParameterValues, namedParameters, lockModes, rowSelection, readOnly, cacheable,
- cacheRegion, comment, collectionKeys, transformer)
+ public QueryParameters(IType[] positionalParameterTypes, object[] positionalParameterValues, IDictionary<string, TypedValue> namedParameters, IDictionary<string, LockMode> lockModes, RowSelection rowSelection, bool isReadOnlyInitialized, bool readOnly, bool cacheable, string cacheRegion, string comment, object[] collectionKeys, object optionalObject, string optionalEntityName, object optionalId, IResultTransformer transformer)
+ : this(positionalParameterTypes, positionalParameterValues, namedParameters, lockModes, rowSelection, isReadOnlyInitialized, readOnly, cacheable, cacheRegion, comment, collectionKeys, transformer)
{
_optionalEntityName = optionalEntityName;
_optionalId = optionalId;
_optionalObject = optionalObject;
}
- /// <summary></summary>
public bool HasRowSelection
{
get { return _rowSelection != null; }
@@ -136,9 +117,6 @@
get { return offsetParameterIndex; }
}
- /// <summary>
- /// Named parameters.
- /// </summary>
public IDictionary<string, TypedValue> NamedParameters
{
get { return _namedParameters; }
@@ -146,7 +124,7 @@
}
/// <summary>
- /// Gets or sets an array of <see cref="IType"/> objects that is stored at the index
+ /// Gets or sets an array of <see cref="IType"/> objects that is stored at the index
/// of the Parameter.
/// </summary>
public IType[] PositionalParameterTypes
@@ -161,7 +139,7 @@
}
/// <summary>
- /// Gets or sets an array of <see cref="object"/> objects that is stored at the index
+ /// Gets or sets an array of <see cref="object"/> objects that is stored at the index
/// of the Parameter.
/// </summary>
public object[] PositionalParameterValues
@@ -190,6 +168,19 @@
set { _lockModes = value; }
}
+ /// <summary>
+ /// Has the read-only/modifiable mode been explicitly set?
+ /// </summary>
+ /// <value>
+ /// <c>true</c>, the read-only/modifiable mode was explicitly set; <c>false</c>, the read-only/modifiable mode was not explicitly set
+ /// </value>
+ /// <seealso cref="ReadOnly" />
+ /// <seealso cref="IsReadOnly(ISessionImplementor)" />
+ public bool IsReadOnlyInitialized
+ {
+ get { return _isReadOnlyInitialized; }
+ }
+
private void CreatePositionalParameterLocations(ISessionFactoryImplementor factory)
{
_positionalParameterLocations = new int[_positionalParameterTypes.Length];
@@ -211,7 +202,6 @@
return array.Length;
}
- /// <summary></summary>
public void LogParameters(ISessionFactoryImplementor factory)
{
var print = new Printer(factory);
@@ -248,7 +238,7 @@
/// Ensure the Types and Values are the same length.
/// </summary>
/// <exception cref="QueryException">
- /// If the Lengths of <see cref="PositionalParameterTypes"/> and
+ /// If the Lengths of <see cref="PositionalParameterTypes"/> and
/// <see cref="PositionalParameterValues"/> are not equal.
/// </exception>
public void ValidateParameters()
@@ -290,11 +280,30 @@
}
public bool Callable { get; set; }
-
+
+ /// <summary>
+ /// Should entities and proxies loaded by the query be put in read-only mode?
+ /// </summary>
+ /// <remarks>
+ /// The read-only/modifiable setting has no impact on entities/proxies returned by the
+ /// query that existed in the session before the query was executed.
+ /// </remarks>
+ /// <seealso cref="IsReadOnlyInitialized" />
+ /// <seealso cref="IsReadOnly(ISessionImplementor)" />
public bool ReadOnly
{
- get { return _readOnly; }
- set { _readOnly = value; }
+ get
+ {
+ if (!_isReadOnlyInitialized)
+ throw new InvalidOperationException("cannot call ReadOnly when IsReadOnlyInitialized returns false");
+
+ return _readOnly;
+ }
+ set
+ {
+ _readOnly = value;
+ _isReadOnlyInitialized = true;
+ }
}
/************** Filters ********************************/
@@ -674,7 +683,7 @@
public QueryParameters CreateCopyUsing(RowSelection selection)
{
var copy = new QueryParameters(_positionalParameterTypes, _positionalParameterValues, _namedParameters, _lockModes,
- selection, _readOnly, _cacheable, _cacheRegion, _comment, _collectionKeys,
+ selection, _isReadOnlyInitialized, _readOnly, _cacheable, _cacheRegion, _comment, _collectionKeys,
_optionalObject, _optionalEntityName, _optionalId, _resultTransformer);
copy._positionalParameterLocations = _positionalParameterLocations;
copy.processedSQL = processedSQL;
@@ -683,5 +692,27 @@
copy.filteredParameterLocations = filteredParameterLocations;
return copy;
}
+
+ /// <summary>
+ /// Should entities and proxies loaded by the query be put in read-only mode? If the
+ /// read-only/modifiable setting was not initialized
+ /// (i.e. <see cref="IsReadOnlyInitialized" /> == false), then the default
+ /// read-only/modifiable setting for the persistence context is returned instead.
+ /// </summary>
+ /// <remarks>
+ /// The read-only/modifiable setting has no impact on entities/proxies returned by the
+ /// query that existed in the session before the query was executed.
+ /// </remarks>
+ /// <seealso cref="IsReadOnlyInitialized" />
+ /// <seealso cref="ReadOnly" />
+ /// <seealso cref="IPersistenceContext.DefaultReadOnly" />
+ /// <param name="session"></param>
+ /// <returns>
+ /// <c>true</c>, entities and proxies loaded by the query will be put in read-only mode; <c>false</c>, entities and proxies loaded by the query will be put in modifiable mode
+ /// </returns>
+ public bool IsReadOnly(ISessionImplementor session)
+ {
+ return _isReadOnlyInitialized ? this.ReadOnly : session.PersistenceContext.DefaultReadOnly;
+ }
}
}
\ No newline at end of file
Modified: branches/ReadOnlyEntities/nhibernate/src/NHibernate/Engine/StatefulPersistenceContext.cs
===================================================================
--- branches/ReadOnlyEntities/nhibernate/src/NHibernate/Engine/StatefulPersistenceContext.cs 2011-02-11 14:44:33 UTC (rev 5379)
+++ branches/ReadOnlyEntities/nhibernate/src/NHibernate/Engine/StatefulPersistenceContext.cs 2011-02-11 16:07:38 UTC (rev 5380)
@@ -17,10 +17,10 @@
namespace NHibernate.Engine
{
- /// <summary>
+ /// <summary>
/// A <see cref="IPersistenceContext"/> represents the state of persistent "stuff" which
/// NHibernate is tracking. This includes persistent entities, collections,
- /// as well as proxies generated.
+ /// as well as proxies generated.
/// </summary>
/// <remarks>
/// There is meant to be a one-to-one correspondence between a SessionImpl and
@@ -97,6 +97,8 @@
[NonSerialized]
private BatchFetchQueue batchFetchQueue;
+ private bool defaultReadOnly;
+
/// <summary> Constructs a PersistentContext, bound to the given session. </summary>
/// <param name="session">The session "owning" this context. </param>
public StatefulPersistenceContext(ISessionImplementor session)
@@ -132,15 +134,15 @@
get { return false; }
}
- /// <summary>
- /// Get the session to which this persistence context is bound.
+ /// <summary>
+ /// Get the session to which this persistence context is bound.
/// </summary>
public ISessionImplementor Session
{
get { return session; }
}
- /// <summary>
+ /// <summary>
/// Retrieve this persistence context's managed load context.
/// </summary>
public LoadContexts LoadContexts
@@ -154,7 +156,7 @@
}
}
- /// <summary>
+ /// <summary>
/// Get the <tt>BatchFetchQueue</tt>, instantiating one if necessary.
/// </summary>
public BatchFetchQueue BatchFetchQueue
@@ -221,7 +223,7 @@
unownedCollections[key] = collection;
}
- /// <summary>
+ /// <summary>
/// Get and remove a collection whose owner is not yet loaded,
/// when its owner is being loaded
/// </summary>
@@ -245,7 +247,8 @@
{
foreach (INHibernateProxy proxy in proxiesByKey.Values)
{
- proxy.HibernateLazyInitializer.Session = null;
+ ILazyInitializer li = proxy.HibernateLazyInitializer;
+ li.UnsetSession();
}
ICollection collectionEntryArray = IdentityMap.ConcurrentEntries(collectionEntries);
@@ -283,6 +286,13 @@
{
get { return hasNonReadOnlyEntities; }
}
+
+ /// <inheritdoc />
+ public bool DefaultReadOnly
+ {
+ get { return defaultReadOnly; }
+ set { defaultReadOnly = value; }
+ }
private void SetHasNonReadOnlyEnties(Status value)
{
@@ -307,9 +317,9 @@
entityEntry.LockMode = LockMode.None;
}
- /// <summary>
+ /// <summary>
/// Get the current state of the entity as known to the underlying
- /// database, or null if there is no corresponding row
+ /// database, or null if there is no corresponding row
/// </summary>
public object[] GetDatabaseSnapshot(object id, IEntityPersister persister)
{
@@ -327,7 +337,7 @@
}
}
- /// <summary>
+ /// <summary>
/// Retrieve the cached database snapshot for the requested entity key.
/// </summary>
/// <param name="key">The entity key for which to retrieve the cached snapshot </param>
@@ -352,9 +362,9 @@
return (object[])snapshot;
}
- /// <summary>
- /// Get the values of the natural id fields as known to the underlying
- /// database, or null if the entity has no natural id or there is no
+ /// <summary>
+ /// Get the values of the natural id fields as known to the underlying
+ /// database, or null if the entity has no natural id or there is no
/// corresponding row.
/// </summary>
public object[] GetNaturalIdSnapshot(object id, IEntityPersister persister)
@@ -408,7 +418,7 @@
BatchFetchQueue.RemoveBatchLoadableEntityKey(key);
}
- /// <summary>
+ /// <summary>
/// Get the entity instance associated with the given <tt>EntityKey</tt>
/// </summary>
public object GetEntity(EntityKey key)
@@ -424,7 +434,7 @@
return entitiesByKey.ContainsKey(key);
}
- /// <summary>
+ /// <summary>
/// Remove an entity from the session cache, also clear
/// up other state associated with the entity, all except
/// for the <tt>EntityEntry</tt>
@@ -465,8 +475,8 @@
entitiesByUniqueKey[euk] = entity;
}
- /// <summary>
- /// Retrieve the EntityEntry representation of the given entity.
+ /// <summary>
+ /// Retrieve the EntityEntry representation of the given entity.
/// </summary>
/// <param name="entity">The entity for which to locate the EntityEntry. </param>
/// <returns> The EntityEntry for the given entity. </returns>
@@ -505,8 +515,8 @@
return AddEntry(entity, status, loadedState, null, entityKey.Identifier, version, lockMode, existsInDatabase, persister, disableVersionIncrement, lazyPropertiesAreUnfetched);
}
- /// <summary>
- /// Generates an appropriate EntityEntry instance and adds it
+ /// <summary>
+ /// Generates an appropriate EntityEntry instance and adds it
/// to the event source's internal caches.
/// </summary>
public EntityEntry AddEntry(object entity, Status status, object[] loadedState, object rowId, object id,
@@ -534,8 +544,8 @@
return proxiesByKey.ContainsValue(proxy);
}
- /// <summary>
- /// Takes the given object and, if it represents a proxy, reassociates it with this event source.
+ /// <summary>
+ /// Takes the given object and, if it represents a proxy, reassociates it with this event source.
/// </summary>
/// <param name="value">The possible proxy to be reassociated. </param>
/// <returns> Whether the passed value represented an actual proxy which got initialized. </returns>
@@ -560,9 +570,9 @@
}
}
- /// <summary>
+ /// <summary>
/// If a deleted entity instance is re-saved, and it has a proxy, we need to
- /// reset the identifier of the proxy
+ /// reset the identifier of the proxy
/// </summary>
public void ReassociateProxy(object value, object id)
{
@@ -585,7 +595,7 @@
}
}
- /// <summary>
+ /// <summary>
/// Associate a proxy that was instantiated by another session with this session
/// </summary>
/// <param name="li">The proxy initializer. </param>
@@ -605,7 +615,7 @@
}
}
- /// <summary>
+ /// <summary>
/// Get the entity instance underlying the given proxy, throwing
/// an exception if the proxy is uninitialized. If the given object
/// is not a proxy, simply return the argument.
@@ -626,7 +636,7 @@
if (li.IsUninitialized)
throw new PersistentObjectException("object was an uninitialized proxy for " + li.PersistentClass.FullName);
- return li.GetImplementation(); // unwrap the object
+ return li.GetImplementation(); // unwrap the object
}
else
{
@@ -634,8 +644,8 @@
}
}
- /// <summary>
- /// Possibly unproxy the given reference and reassociate it with the current session.
+ /// <summary>
+ /// Possibly unproxy the given reference and reassociate it with the current session.
/// </summary>
/// <param name="maybeProxy">The reference to be unproxied if it currently represents a proxy. </param>
/// <returns> The unproxied instance. </returns>
@@ -652,12 +662,12 @@
{
ILazyInitializer li = proxy.HibernateLazyInitializer;
ReassociateProxy(li, proxy);
- return li.GetImplementation(); //initialize + unwrap the object
+ return li.GetImplementation(); //initialize + unwrap the object
}
return maybeProxy;
}
- /// <summary>
+ /// <summary>
/// Attempts to check whether the given key represents an entity already loaded within the
/// current session.
/// </summary>
@@ -676,11 +686,11 @@
}
}
- /// <summary>
+ /// <summary>
/// If the existing proxy is insufficiently "narrow" (derived), instantiate a new proxy
/// and overwrite the registration of the old one. This breaks == and occurs only for
/// "class" proxies rather than "interface" proxies. Also init the proxy to point to
- /// the given target implementation if necessary.
+ /// the given target implementation if necessary.
/// </summary>
/// <param name="proxy">The proxy instance to be narrowed. </param>
/// <param name="persister">The persister for the proxied entity. </param>
@@ -706,7 +716,13 @@
else
{
proxy = (INHibernateProxy)persister.CreateProxy(key.Identifier, session);
+ INHibernateProxy proxyOrig = proxiesByKey[key];
proxiesByKey[key] = proxy; //overwrite old proxy
+ if (proxyOrig != null)
+ {
+ bool readOnlyOrig = proxyOrig.HibernateLazyInitializer.ReadOnly;
+ proxy.HibernateLazyInitializer.ReadOnly = readOnlyOrig;
+ }
return proxy;
}
}
@@ -720,7 +736,7 @@
}
}
- /// <summary>
+ /// <summary>
/// Return the existing proxy associated with the given <tt>EntityKey</tt>, or the
/// third argument (the entity associated with the key) if no proxy exists. Init
/// the proxy to the target implementation, if necessary.
@@ -741,7 +757,7 @@
}
}
- /// <summary>
+ /// <summary>
/// Return the existing proxy associated with the given <tt>EntityKey</tt>, or the
/// argument (the entity associated with the key) if no proxy exists.
/// (slower than the form above)
@@ -761,7 +777,7 @@
/// <summary> Get the entity that owned this persistent collection when it was loaded </summary>
/// <param name="collection">The persistent collection </param>
- /// <returns>
+ /// <returns>
/// The owner, if its entity ID is available from the collection's loaded key
/// and the owner entity is in the persistence context; otherwise, returns null
/// </returns>
@@ -819,7 +835,7 @@
AddCollection(collection, ce, collection.Key);
}
- /// <summary>
+ /// <summary>
/// Add a new collection (ie. a newly created one, just instantiated by the
/// application, with no database state or snapshot)
/// </summary>
@@ -865,7 +881,7 @@
collectionEntries[collection] = ce;
}
- /// <summary>
+ /// <summary>
/// add an (initialized) collection that was created by another session and passed
/// into update() (ie. one with a snapshot and existing state on the database)
/// </summary>
@@ -903,7 +919,7 @@
return null;
}
- /// <summary>
+ /// <summary>
/// Register a collection for non-lazy loading at the end of the two-phase load
/// </summary>
public void AddNonLazyCollection(IPersistentCollection collection)
@@ -911,7 +927,7 @@
nonlazyCollections.Add(collection);
}
- /// <summary>
+ /// <summary>
/// Force initialization of all non-lazy collections encountered during
/// the current two-phase load (actually, this is a no-op, unless this
/// is the "outermost" load)
@@ -953,7 +969,7 @@
}
/// <summary> Register a <tt>PersistentCollection</tt> object for an array.
- /// Associates a holder with an array - MUST be called after loading
+ /// Associates a holder with an array - MUST be called after loading
/// array, since the array instance is not created until endLoad().
/// </summary>
public void AddCollectionHolder(IPersistentCollection holder)
@@ -962,7 +978,7 @@
arrayHolders[holder.GetValue()] = holder;
}
- /// <summary>
+ /// <summary>
/// Remove the mapping of collection to holder during eviction of the owning entity
/// </summary>
public IPersistentCollection RemoveCollectionHolder(object array)
@@ -978,7 +994,7 @@
return GetCollectionEntry(coll).Snapshot;
}
- /// <summary>
+ /// <summary>
/// Get the collection entry for a collection passed to filter,
/// which might be a collection wrapper, an array, or an unwrapped
/// collection. Return null if there is no entry.
@@ -1061,7 +1077,7 @@
loadCounter--;
}
- /// <summary>
+ /// <summary>
/// Search the persistence context for an owner for the child object,
/// given a collection role
/// </summary>
@@ -1071,7 +1087,7 @@
// TODO persistent context (BackrefPropertyAccessor)
}
- /// <summary>
+ /// <summary>
/// Search the persistence context for an index of the child object, given a collection role
/// </summary>
public object GetIndexInOwner(string entity, string property, object childObject, IDictionary mergeMap)
@@ -1080,7 +1096,7 @@
// TODO persistent context (IndexPropertyAccessor)
}
- /// <summary>
+ /// <summary>
/// Record the fact that the association belonging to the keyed entity is null.
/// </summary>
public void AddNullProperty(EntityKey ownerKey, string propertyName)
@@ -1094,18 +1110,80 @@
return nullAssociations.Contains(new AssociationKey(ownerKey, propertyName));
}
- /// <summary> Set the object to read only and discard it's snapshot</summary>
- public void SetReadOnly(object entity, bool readOnly)
+ public void SetReadOnly(object entityOrProxy, bool readOnly)
{
+ if (entityOrProxy == null)
+ throw new ArgumentNullException("entityOrProxy");
+
+ if (IsReadOnly(entityOrProxy) == readOnly)
+ return;
+
+ if (entityOrProxy is INHibernateProxy)
+ {
+ INHibernateProxy proxy = (INHibernateProxy)entityOrProxy;
+ SetProxyReadOnly(proxy, readOnly);
+ if (NHibernateUtil.IsInitialized(proxy))
+ {
+ SetEntityReadOnly(proxy.HibernateLazyInitializer.GetImplementation(), readOnly);
+ }
+ }
+ else
+ {
+ SetEntityReadOnly(entityOrProxy, readOnly);
+
+ // PersistenceContext.proxyFor( entity ) returns entity if there is no proxy for that entity
+ // so need to check the return value to be sure it is really a proxy
+ object maybeProxy = this.Session.PersistenceContext.ProxyFor(entityOrProxy);
+ if (maybeProxy is INHibernateProxy )
+ {
+ SetProxyReadOnly((INHibernateProxy)maybeProxy, readOnly);
+ }
+ }
+ }
+
+ private void SetProxyReadOnly(INHibernateProxy proxy, bool readOnly)
+ {
+ if (proxy.HibernateLazyInitializer.Session != this.Session)
+ {
+ throw new AssertionFailure("Attempt to set a proxy to read-only that is associated with a different session");
+ }
+ proxy.HibernateLazyInitializer.ReadOnly = readOnly;
+ }
+
+ private void SetEntityReadOnly(object entity, bool readOnly)
+ {
EntityEntry entry = GetEntry(entity);
if (entry == null)
{
- throw new TransientObjectException("Instance of" + entity.GetType() + " was not associated with the session");
+ throw new TransientObjectException("Instance was not associated with this persistence context");
}
entry.SetReadOnly(readOnly, entity);
hasNonReadOnlyEntities |= !readOnly;
}
-
+
+ public bool IsReadOnly(object entityOrProxy)
+ {
+ if (entityOrProxy == null)
+ {
+ throw new AssertionFailure("object must be non-null.");
+ }
+ bool isReadOnly;
+ if (entityOrProxy is INHibernateProxy)
+ {
+ isReadOnly = ((INHibernateProxy)entityOrProxy).HibernateLazyInitia...
[truncated message content] |