diff --git a/src/NHibernate.DomainModel/CustomPersister.cs b/src/NHibernate.DomainModel/CustomPersister.cs index 47d6c9a7692..556eabbd021 100644 --- a/src/NHibernate.DomainModel/CustomPersister.cs +++ b/src/NHibernate.DomainModel/CustomPersister.cs @@ -316,8 +316,8 @@ public object Load(object id, object optionalObject, LockMode lockMode, ISession Custom obj = (Custom)Instances[id]; if (obj != null) { - clone = (Custom)obj.Clone(); - TwoPhaseLoad.AddUninitializedEntity(new EntityKey(id, this, session.EntityMode), clone, this, LockMode.None, false, + clone = (Custom)obj.Clone(); + TwoPhaseLoad.AddUninitializedEntity(session.GenerateEntityKey(id, this), clone, this, LockMode.None, false, session); TwoPhaseLoad.PostHydrate(this, id, new String[] {obj.Name}, null, clone, LockMode.None, false, session); TwoPhaseLoad.InitializeEntity(clone, false, session, new PreLoadEvent((IEventSource) session), diff --git a/src/NHibernate.Test/CacheTest/CacheFixture.cs b/src/NHibernate.Test/CacheTest/CacheFixture.cs index 8c41063d7b9..dadc7008649 100644 --- a/src/NHibernate.Test/CacheTest/CacheFixture.cs +++ b/src/NHibernate.Test/CacheTest/CacheFixture.cs @@ -18,7 +18,7 @@ public void TestSimpleCache() private CacheKey CreateCacheKey(string text) { - return new CacheKey(text, NHibernateUtil.String, "Foo", EntityMode.Poco, null); + return new CacheKey(text, NHibernateUtil.String, "Foo", EntityMode.Poco, null, null); } public void DoTestCache(ICacheProvider cacheProvider) diff --git a/src/NHibernate.Test/FilterTest/DynamicFilterTest.cs b/src/NHibernate.Test/FilterTest/DynamicFilterTest.cs index d1a1d37299f..096ebb3ecf0 100644 --- a/src/NHibernate.Test/FilterTest/DynamicFilterTest.cs +++ b/src/NHibernate.Test/FilterTest/DynamicFilterTest.cs @@ -32,7 +32,7 @@ public void SecondLevelCachedCollectionsFiltering() .GetCollectionPersister(typeof(Salesperson).FullName + ".Orders"); Assert.IsTrue(persister.HasCache, "No cache for collection"); CacheKey cacheKey = - new CacheKey(testData.steveId, persister.KeyType, persister.Role, EntityMode.Poco, (ISessionFactoryImplementor) sessions); + new CacheKey(testData.steveId, persister.KeyType, persister.Role, EntityMode.Poco, null, (ISessionFactoryImplementor) sessions); CollectionCacheEntry cachedData = (CollectionCacheEntry)persister.Cache.Cache.Get(cacheKey); Assert.IsNotNull(cachedData, "collection was not in cache"); diff --git a/src/NHibernate/Action/CollectionAction.cs b/src/NHibernate/Action/CollectionAction.cs index 7dc692bb99e..a28b28a8213 100644 --- a/src/NHibernate/Action/CollectionAction.cs +++ b/src/NHibernate/Action/CollectionAction.cs @@ -96,7 +96,7 @@ public virtual void BeforeExecutions() // second-level cache invalidation only) if (persister.HasCache) { - CacheKey ck = new CacheKey(key, persister.KeyType, persister.Role, session.EntityMode, session.Factory); + CacheKey ck = session.GenerateCacheKey(key, persister.KeyType, persister.Role); softLock = persister.Cache.Lock(ck, null); } } @@ -120,7 +120,7 @@ public virtual AfterTransactionCompletionProcessDelegate AfterTransactionComplet { if (persister.HasCache) { - CacheKey ck = new CacheKey(key, persister.KeyType, persister.Role, Session.EntityMode, Session.Factory); + CacheKey ck = Session.GenerateCacheKey(key, persister.KeyType, persister.Role); persister.Cache.Release(ck, softLock); } }); @@ -138,7 +138,7 @@ protected internal void Evict() { if (persister.HasCache) { - CacheKey ck = new CacheKey(key, persister.KeyType, persister.Role, session.EntityMode, session.Factory); + CacheKey ck = session.GenerateCacheKey(key, persister.KeyType, persister.Role); persister.Cache.Evict(ck); } } diff --git a/src/NHibernate/Action/CollectionUpdateAction.cs b/src/NHibernate/Action/CollectionUpdateAction.cs index c56f9f5bb11..7c9c91b0b4c 100644 --- a/src/NHibernate/Action/CollectionUpdateAction.cs +++ b/src/NHibernate/Action/CollectionUpdateAction.cs @@ -130,7 +130,7 @@ public override AfterTransactionCompletionProcessDelegate AfterTransactionComple // NH Different behavior: to support unlocking collections from the cache.(r3260) if (Persister.HasCache) { - CacheKey ck = new CacheKey(Key, Persister.KeyType, Persister.Role, Session.EntityMode, Session.Factory); + CacheKey ck = Session.GenerateCacheKey(Key, Persister.KeyType, Persister.Role); if (success) { diff --git a/src/NHibernate/Action/EntityDeleteAction.cs b/src/NHibernate/Action/EntityDeleteAction.cs index a38b792e361..665a6cead8c 100644 --- a/src/NHibernate/Action/EntityDeleteAction.cs +++ b/src/NHibernate/Action/EntityDeleteAction.cs @@ -57,7 +57,7 @@ public override void Execute() CacheKey ck; if (persister.HasCache) { - ck = new CacheKey(id, persister.IdentifierType, persister.RootEntityName, session.EntityMode, session.Factory); + ck = session.GenerateCacheKey(id, persister.IdentifierType, persister.RootEntityName); sLock = persister.Cache.Lock(ck, version); } else @@ -83,7 +83,7 @@ public override void Execute() } entry.PostDelete(); - EntityKey key = new EntityKey(entry.Id, entry.Persister, session.EntityMode); + EntityKey key = session.GenerateEntityKey(entry.Id, entry.Persister); persistenceContext.RemoveEntity(key); persistenceContext.RemoveProxy(key); @@ -131,7 +131,7 @@ protected override void AfterTransactionCompletionProcessImpl(bool success) { if (Persister.HasCache) { - CacheKey ck = new CacheKey(Id, Persister.IdentifierType, Persister.RootEntityName, Session.EntityMode, Session.Factory); + CacheKey ck = Session.GenerateCacheKey(Id, Persister.IdentifierType, Persister.RootEntityName); Persister.Cache.Release(ck, sLock); } if (success) diff --git a/src/NHibernate/Action/EntityIdentityInsertAction.cs b/src/NHibernate/Action/EntityIdentityInsertAction.cs index c041cc2113d..114f6360bc8 100644 --- a/src/NHibernate/Action/EntityIdentityInsertAction.cs +++ b/src/NHibernate/Action/EntityIdentityInsertAction.cs @@ -41,7 +41,7 @@ private EntityKey GenerateDelayedEntityKey() if (!isDelayed) throw new HibernateException("Cannot request delayed entity-key for non-delayed post-insert-id generation"); - return new EntityKey(new DelayedPostInsertIdentifier(), Persister, Session.EntityMode); + return Session.GenerateEntityKey(new DelayedPostInsertIdentifier(), Persister); } } diff --git a/src/NHibernate/Action/EntityInsertAction.cs b/src/NHibernate/Action/EntityInsertAction.cs index 882ed904525..d4b450155be 100644 --- a/src/NHibernate/Action/EntityInsertAction.cs +++ b/src/NHibernate/Action/EntityInsertAction.cs @@ -84,7 +84,7 @@ public override void Execute() CacheEntry ce = new CacheEntry(state, persister, persister.HasUninitializedLazyProperties(instance, session.EntityMode), version, session, instance); cacheEntry = persister.CacheEntryStructure.Structure(ce); - CacheKey ck = new CacheKey(id, persister.IdentifierType, persister.RootEntityName, Session.EntityMode, Session.Factory); + CacheKey ck = Session.GenerateCacheKey(id, persister.IdentifierType, persister.RootEntityName); bool put = persister.Cache.Insert(ck, cacheEntry, version); if (put && factory.Statistics.IsStatisticsEnabled) @@ -108,7 +108,7 @@ protected override void AfterTransactionCompletionProcessImpl(bool success) IEntityPersister persister = Persister; if (success && IsCachePutEnabled(persister)) { - CacheKey ck = new CacheKey(Id, persister.IdentifierType, persister.RootEntityName, Session.EntityMode, Session.Factory); + CacheKey ck = Session.GenerateCacheKey(Id, persister.IdentifierType, persister.RootEntityName); bool put = persister.Cache.AfterInsert(ck, cacheEntry, version); if (put && Session.Factory.Statistics.IsStatisticsEnabled) diff --git a/src/NHibernate/Action/EntityUpdateAction.cs b/src/NHibernate/Action/EntityUpdateAction.cs index 4751a9a6d13..d361c48f3b6 100644 --- a/src/NHibernate/Action/EntityUpdateAction.cs +++ b/src/NHibernate/Action/EntityUpdateAction.cs @@ -70,7 +70,7 @@ public override void Execute() CacheKey ck = null; if (persister.HasCache) { - ck = new CacheKey(id, persister.IdentifierType, persister.RootEntityName, session.EntityMode, factory); + ck = session.GenerateCacheKey(id, persister.IdentifierType, persister.RootEntityName); slock = persister.Cache.Lock(ck, previousVersion); } @@ -140,7 +140,7 @@ protected override void AfterTransactionCompletionProcessImpl(bool success) IEntityPersister persister = Persister; if (persister.HasCache) { - CacheKey ck = new CacheKey(Id, persister.IdentifierType, persister.RootEntityName, Session.EntityMode, Session.Factory); + CacheKey ck = Session.GenerateCacheKey(Id, persister.IdentifierType, persister.RootEntityName); if (success && cacheEntry != null) { diff --git a/src/NHibernate/Cache/CacheKey.cs b/src/NHibernate/Cache/CacheKey.cs index af17ec233d4..ff6a11c1c3e 100644 --- a/src/NHibernate/Cache/CacheKey.cs +++ b/src/NHibernate/Cache/CacheKey.cs @@ -17,25 +17,27 @@ public class CacheKey private readonly string entityOrRoleName; private readonly int hashCode; private readonly EntityMode entityMode; + private readonly string tenantId; - /// - /// Construct a new key for a collection or entity instance. - /// Note that an entity name should always be the root entity - /// name, not a subclass entity name. - /// - /// The identifier associated with the cached data - /// The Hibernate type mapping - /// The entity or collection-role name. - /// The entiyt mode of the originating session - /// The session factory for which we are caching - public CacheKey(object id, IType type, string entityOrRoleName, EntityMode entityMode, ISessionFactoryImplementor factory) - { - key = id; - this.type = type; - this.entityOrRoleName = entityOrRoleName; - this.entityMode = entityMode; - hashCode = type.GetHashCode(key, entityMode, factory); - } + /// + /// Construct a new key for a collection or entity instance. + /// Note that an entity name should always be the root entity + /// name, not a subclass entity name. + /// + /// The identifier associated with the cached data + /// The Hibernate type mapping + /// The entity or collection-role name. + /// The entiyt mode of the originating session + /// The session factory for which we are caching + public CacheKey(object id, IType type, string entityOrRoleName, EntityMode entityMode, string tenantId, ISessionFactoryImplementor factory) + { + key = id; + this.type = type; + this.entityOrRoleName = entityOrRoleName; + this.entityMode = entityMode; + this.tenantId = tenantId; + hashCode = type.GetHashCode(key, entityMode, factory); + } //Mainly for SysCache and Memcache public override String ToString() diff --git a/src/NHibernate/Cfg/Environment.cs b/src/NHibernate/Cfg/Environment.cs index 020742eedbd..cab6a711728 100644 --- a/src/NHibernate/Cfg/Environment.cs +++ b/src/NHibernate/Cfg/Environment.cs @@ -90,6 +90,9 @@ public static string Version /// The EntityMode in which set the Session opened from the SessionFactory. public const string DefaultEntityMode = "default_entity_mode"; + /// The multi tenancy strategy to use + public const string MultiTenancyStrategy = "multi_tenancy_strategy"; + /// /// When using an enhanced id generator and pooled optimizers (), /// prefer interpreting the database value as the lower (lo) boundary. The default is to interpret it as the high boundary. diff --git a/src/NHibernate/Cfg/MultiTenancyStrategy.cs b/src/NHibernate/Cfg/MultiTenancyStrategy.cs new file mode 100644 index 00000000000..e8e0c1a31ca --- /dev/null +++ b/src/NHibernate/Cfg/MultiTenancyStrategy.cs @@ -0,0 +1,51 @@ + +namespace NHibernate.Cfg +{ + public enum MultiTenancyStrategy + { + /// + /// Multi-tenancy implemented by use of discriminator columns (i.e. tenantId) + /// + Discriminator, + + /// + /// Tenant per-schema + /// + Schema, + + /// + /// Tenant per-database + /// + Database, + + /// + /// No multi-tenancy (default) + /// + None + } + + public static class MultiTenancyStrategyParser + { + public const string DiscriminatorXmlName = "discriminator"; + public const string SchemaXmlName = "per-schema"; + public const string DatabaseXmlName = "per-database"; + public const string NoneXmlName = "none"; + + public static MultiTenancyStrategy Convert(string value) + { + switch (value) + { + case NoneXmlName: + return MultiTenancyStrategy.None; + case SchemaXmlName: + throw new HibernateException("multi-tenancy strategies are not yet supported"); + case DatabaseXmlName: + throw new HibernateException("multi-tenancy strategies are not yet supported"); + case DiscriminatorXmlName: + throw new HibernateException("multi-tenancy strategies are not yet supported"); + default: + throw new HibernateException(string.Format("unknown multi-tenancy strategy: {0}", value)); + } + } + } +} diff --git a/src/NHibernate/Cfg/Settings.cs b/src/NHibernate/Cfg/Settings.cs index 211e79b5e5d..6b7c5efdf79 100644 --- a/src/NHibernate/Cfg/Settings.cs +++ b/src/NHibernate/Cfg/Settings.cs @@ -48,7 +48,9 @@ public Settings() public string DefaultSchemaName { get; set; } - public string DefaultCatalogName { get; internal set; } + public string DefaultCatalogName { get; internal set; } + + public MultiTenancyStrategy MultiTenancyStrategy { get; internal set; } public string SessionFactoryName { get; internal set; } diff --git a/src/NHibernate/Cfg/SettingsFactory.cs b/src/NHibernate/Cfg/SettingsFactory.cs index 5383973f21f..41504b67fb8 100644 --- a/src/NHibernate/Cfg/SettingsFactory.cs +++ b/src/NHibernate/Cfg/SettingsFactory.cs @@ -109,7 +109,12 @@ public Settings BuildSettings(IDictionary properties) if (defaultCatalog != null) log.Info("Default catalog: " + defaultCatalog); settings.DefaultSchemaName = defaultSchema; - settings.DefaultCatalogName = defaultCatalog; + settings.DefaultCatalogName = defaultCatalog; + + string multiTenancyStrategy = PropertiesHelper.GetString( + Environment.MultiTenancyStrategy, properties, MultiTenancyStrategyParser.NoneXmlName); + settings.MultiTenancyStrategy = MultiTenancyStrategyParser.Convert(multiTenancyStrategy); + log.Info("multi-tenancy strategy: " + multiTenancyStrategy); int batchFetchSize = PropertiesHelper.GetInt32(Environment.DefaultBatchFetchSize, properties, 1); log.Info("Default batch fetch size: " + batchFetchSize); diff --git a/src/NHibernate/Engine/BatchFetchQueue.cs b/src/NHibernate/Engine/BatchFetchQueue.cs index 89501701bdb..042085aeb45 100644 --- a/src/NHibernate/Engine/BatchFetchQueue.cs +++ b/src/NHibernate/Engine/BatchFetchQueue.cs @@ -167,7 +167,7 @@ public object[] GetCollectionBatch(ICollectionPersister collectionPersister, obj end = i; //checkForEnd = false; } - else if (!IsCached(ce.LoadedKey, collectionPersister, entityMode)) + else if (!IsCached(ce.LoadedKey, collectionPersister)) { keys[i++] = ce.LoadedKey; //count++; @@ -220,7 +220,7 @@ public object[] GetEntityBatch(IEntityPersister persister,object id,int batchSiz } else { - if (!IsCached(key, persister, entityMode)) + if (!IsCached(key, persister)) { ids[i++] = key.Identifier; } @@ -236,24 +236,21 @@ public object[] GetEntityBatch(IEntityPersister persister,object id,int batchSiz return ids; //we ran out of ids to try } - private bool IsCached(EntityKey entityKey, IEntityPersister persister, EntityMode entityMode) + private bool IsCached(EntityKey entityKey, IEntityPersister persister) { if (persister.HasCache) { - CacheKey key = - new CacheKey(entityKey.Identifier, persister.IdentifierType, entityKey.EntityName, entityMode, - context.Session.Factory); + CacheKey key = context.Session.GenerateCacheKey(entityKey.Identifier, persister.IdentifierType, entityKey.EntityName); return persister.Cache.Cache.Get(key) != null; } return false; } - private bool IsCached(object collectionKey, ICollectionPersister persister, EntityMode entityMode) + private bool IsCached(object collectionKey, ICollectionPersister persister) { if (persister.HasCache) { - CacheKey cacheKey = - new CacheKey(collectionKey, persister.KeyType, persister.Role, entityMode, context.Session.Factory); + CacheKey cacheKey = context.Session.GenerateCacheKey(collectionKey, persister.KeyType, persister.Role); return persister.Cache.Cache.Get(cacheKey) != null; } return false; diff --git a/src/NHibernate/Engine/Collections.cs b/src/NHibernate/Engine/Collections.cs index 0e792889843..ca40300ae32 100644 --- a/src/NHibernate/Engine/Collections.cs +++ b/src/NHibernate/Engine/Collections.cs @@ -60,7 +60,7 @@ private static void ProcessDereferencedCollection(IPersistentCollection coll, IS // throw new AssertionFailure("Unable to determine collection owner identifier for orphan-delete processing"); // } //} - EntityKey key = new EntityKey(ownerId, loadedPersister.OwnerEntityPersister, session.EntityMode); + EntityKey key = session.GenerateEntityKey(ownerId, loadedPersister.OwnerEntityPersister); object owner = persistenceContext.GetEntity(key); if (owner == null) { diff --git a/src/NHibernate/Engine/EntityEntry.cs b/src/NHibernate/Engine/EntityEntry.cs index 9320a9dc585..fd933cc582d 100644 --- a/src/NHibernate/Engine/EntityEntry.cs +++ b/src/NHibernate/Engine/EntityEntry.cs @@ -22,52 +22,55 @@ public sealed class EntityEntry private object version; [NonSerialized] - 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; - - [NonSerialized] - private readonly object rowId; - - /// - /// Initializes a new instance of EntityEntry. - /// - /// The current of the Entity. - /// The snapshot of the Entity's state when it was loaded. - /// - /// The identifier of the Entity in the database. - /// The version of the Entity. - /// The for the Entity. - /// A boolean indicating if the Entity exists in the database. - /// The that is responsible for this Entity. - /// - /// - /// - internal EntityEntry(Status status, object[] loadedState, object rowId, object id, object version, LockMode lockMode, - bool existsInDatabase, IEntityPersister persister, EntityMode entityMode, - bool disableVersionIncrement, bool lazyPropertiesAreUnfetched) - { - this.status = status; - 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.existsInDatabase = existsInDatabase; - this.version = version; - this.lockMode = lockMode; - isBeingReplicated = disableVersionIncrement; - loadedWithLazyPropertiesUnfetched = lazyPropertiesAreUnfetched; - this.persister = persister; - this.entityMode = entityMode; - entityName = persister == null ? null : persister.EntityName; - } - - /// + private IEntityPersister persister; // for convenience to save some lookups + + private readonly EntityMode entityMode; + private readonly string tenantId; + private readonly string entityName; + private EntityKey cachedEntityKey; + private readonly bool isBeingReplicated; + private readonly bool loadedWithLazyPropertiesUnfetched; + + [NonSerialized] + private readonly object rowId; + + /// + /// Initializes a new instance of EntityEntry. + /// + /// The current of the Entity. + /// The snapshot of the Entity's state when it was loaded. + /// + /// The identifier of the Entity in the database. + /// The version of the Entity. + /// The for the Entity. + /// A boolean indicating if the Entity exists in the database. + /// The that is responsible for this Entity. + /// + /// The tenant for the current session + /// + /// + internal EntityEntry(Status status, object[] loadedState, object rowId, object id, object version, LockMode lockMode, + bool existsInDatabase, IEntityPersister persister, EntityMode entityMode, string tenantId, + bool disableVersionIncrement, bool lazyPropertiesAreUnfetched) + { + this.status = status; + 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.existsInDatabase = existsInDatabase; + this.version = version; + this.lockMode = lockMode; + isBeingReplicated = disableVersionIncrement; + loadedWithLazyPropertiesUnfetched = lazyPropertiesAreUnfetched; + this.persister = persister; + this.entityMode = entityMode; + this.tenantId = tenantId; + entityName = persister == null ? null : persister.EntityName; + } + + /// /// Gets or sets the current of the Entity. /// /// The of the Entity. @@ -200,7 +203,7 @@ public EntityKey EntityKey if (id == null) throw new InvalidOperationException("cannot generate an EntityKey when id is null."); - cachedEntityKey = new EntityKey(id, persister, entityMode); + cachedEntityKey = new EntityKey(id, persister, entityMode, tenantId); } return cachedEntityKey; } @@ -259,8 +262,8 @@ public void ForceLocked(object entity, object nextVersion) } public bool IsNullifiable(bool earlyInsert, ISessionImplementor session) - { - return Status == Status.Saving || (earlyInsert ? !ExistsInDatabase : session.PersistenceContext.NullifiableEntityKeys.Contains(new EntityKey(Id, Persister, entityMode))); + { + return Status == Status.Saving || (earlyInsert ? !ExistsInDatabase : session.PersistenceContext.NullifiableEntityKeys.Contains(session.GenerateEntityKey(Id, Persister))); } public bool RequiresDirtyCheck(object entity) diff --git a/src/NHibernate/Engine/EntityKey.cs b/src/NHibernate/Engine/EntityKey.cs index bc8a4682df3..53350e95254 100644 --- a/src/NHibernate/Engine/EntityKey.cs +++ b/src/NHibernate/Engine/EntityKey.cs @@ -16,40 +16,45 @@ public sealed class EntityKey private readonly string rootEntityName; private readonly string entityName; private readonly IType identifierType; - private readonly bool isBatchLoadable; + private readonly bool isBatchLoadable; + private readonly string tenantId; [NonSerialized] private ISessionFactoryImplementor factory; private int hashCode; - private readonly EntityMode entityMode; - - /// Construct a unique identifier for an entity class instance - public EntityKey(object id, IEntityPersister persister, EntityMode entityMode) - : this(id, persister.RootEntityName, persister.EntityName, persister.IdentifierType, persister.IsBatchLoadable, persister.Factory, entityMode) {} - - /// Used to reconstruct an EntityKey during deserialization. - /// The identifier value - /// The root entity name - /// The specific entity name - /// The type of the identifier value - /// Whether represented entity is eligible for batch loading - /// The session factory - /// The entity's entity mode - private EntityKey(object identifier, string rootEntityName, string entityName, IType identifierType, bool batchLoadable, ISessionFactoryImplementor factory, EntityMode entityMode) - { - if (identifier == null) - throw new AssertionFailure("null identifier"); - - this.identifier = identifier; - this.rootEntityName = rootEntityName; - this.entityName = entityName; - this.identifierType = identifierType; - isBatchLoadable = batchLoadable; - this.factory = factory; - this.entityMode = entityMode; - hashCode = GenerateHashCode(); - } + private readonly EntityMode entityMode; + + /// Construct a unique identifier for an entity class instance + /// + public EntityKey(object id, IEntityPersister persister, EntityMode entityMode, string tenantId) + : this(id, persister.RootEntityName, persister.EntityName, persister.IdentifierType, persister.IsBatchLoadable, persister.Factory, entityMode, tenantId) { } + + /// Used to reconstruct an EntityKey during deserialization. + /// The identifier value + /// The root entity name + /// The specific entity name + /// The type of the identifier value + /// Whether represented entity is eligible for batch loading + /// The session factory + /// The entity's entity mode + /// The tenant identifier of the session to which this key belongs + private EntityKey(object identifier, string rootEntityName, string entityName, IType identifierType, bool batchLoadable, ISessionFactoryImplementor factory, EntityMode entityMode, string tenantId) + { + if (identifier == null) + throw new AssertionFailure("null identifier"); + + this.identifier = identifier; + this.rootEntityName = rootEntityName; + this.entityName = entityName; + this.identifierType = identifierType; + isBatchLoadable = batchLoadable; + this.factory = factory; + this.entityMode = entityMode; + this.tenantId = tenantId; + hashCode = GenerateHashCode(); + + } public bool IsBatchLoadable { @@ -73,7 +78,8 @@ public override bool Equals(object other) return otherKey.rootEntityName.Equals(rootEntityName) - && identifierType.IsEqual(otherKey.Identifier, Identifier, entityMode, factory); + && identifierType.IsEqual(otherKey.Identifier, Identifier, entityMode, factory) + && string.Equals(tenantId, otherKey.tenantId); } private int GenerateHashCode() diff --git a/src/NHibernate/Engine/ISessionImplementor.cs b/src/NHibernate/Engine/ISessionImplementor.cs index f6eed43ef2f..1daf21f244d 100644 --- a/src/NHibernate/Engine/ISessionImplementor.cs +++ b/src/NHibernate/Engine/ISessionImplementor.cs @@ -2,7 +2,8 @@ using System.Collections; using System.Collections.Generic; using System.Data; -using NHibernate.AdoNet; +using NHibernate.AdoNet; +using NHibernate.Cache; using NHibernate.Collection; using NHibernate.Engine.Query.Sql; using NHibernate.Event; @@ -302,6 +303,15 @@ public interface ISessionImplementor ITransactionContext TransactionContext { get; set; } - void CloseSessionFromDistributedTransaction(); + void CloseSessionFromDistributedTransaction(); + + /// + /// The tenant identifier of this session + /// + string TenantIdentifier { get; } + + EntityKey GenerateEntityKey(object id, IEntityPersister persister); + + CacheKey GenerateCacheKey(object id, IType type, string entityOrRoleName); } } diff --git a/src/NHibernate/Engine/Loading/CollectionLoadContext.cs b/src/NHibernate/Engine/Loading/CollectionLoadContext.cs index 34ab36ef59c..e6a69a1d22f 100644 --- a/src/NHibernate/Engine/Loading/CollectionLoadContext.cs +++ b/src/NHibernate/Engine/Loading/CollectionLoadContext.cs @@ -323,7 +323,7 @@ private void AddCollectionToCache(LoadingCollectionEntry lce, ICollectionPersist } CollectionCacheEntry entry = new CollectionCacheEntry(lce.Collection, persister); - CacheKey cacheKey = new CacheKey(lce.Key, persister.KeyType, persister.Role, session.EntityMode, factory); + CacheKey cacheKey = session.GenerateCacheKey(lce.Key, persister.KeyType, persister.Role); bool put = persister.Cache.Put(cacheKey, persister.CacheEntryStructure.Structure(entry), session.Timestamp, version, versionComparator, factory.Settings.IsMinimalPutsEnabled && session.CacheMode != CacheMode.Refresh); diff --git a/src/NHibernate/Engine/StatefulPersistenceContext.cs b/src/NHibernate/Engine/StatefulPersistenceContext.cs index 349991b27c3..551f22fdaa4 100644 --- a/src/NHibernate/Engine/StatefulPersistenceContext.cs +++ b/src/NHibernate/Engine/StatefulPersistenceContext.cs @@ -330,7 +330,7 @@ public void AfterTransactionCompletion() /// public object[] GetDatabaseSnapshot(object id, IEntityPersister persister) { - EntityKey key = new EntityKey(id, persister, session.EntityMode); + EntityKey key = session.GenerateEntityKey(id, persister); object cached; if (entitySnapshotsByKey.TryGetValue(key, out cached)) { @@ -532,7 +532,7 @@ public EntityEntry AddEntry(object entity, Status status, object[] loadedState, bool disableVersionIncrement, bool lazyPropertiesAreUnfetched) { EntityEntry e = - new EntityEntry(status, loadedState, rowId, id, version, lockMode, existsInDatabase, persister, session.EntityMode, + new EntityEntry(status, loadedState, rowId, id, version, lockMode, existsInDatabase, persister, session.EntityMode, session.TenantIdentifier, disableVersionIncrement, lazyPropertiesAreUnfetched); entityEntries[entity] = e; @@ -614,7 +614,7 @@ private void ReassociateProxy(ILazyInitializer li, INHibernateProxy proxy) if (li.Session != Session) { IEntityPersister persister = session.Factory.GetEntityPersister(li.EntityName); - EntityKey key = new EntityKey(li.Identifier, persister, session.EntityMode); + EntityKey key = session.GenerateEntityKey(li.Identifier, persister); // any earlier proxy takes precedence if (!proxiesByKey.ContainsKey(key)) { @@ -777,13 +777,13 @@ public object ProxyFor(object impl) { EntityEntry e = GetEntry(impl); IEntityPersister p = e.Persister; - return ProxyFor(p, new EntityKey(e.Id, p, session.EntityMode), impl); + return ProxyFor(p, session.GenerateEntityKey(e.Id, p), impl); } /// Get the entity that owns this persistent collection public object GetCollectionOwner(object key, ICollectionPersister collectionPersister) { - return GetEntity(new EntityKey(key, collectionPersister.OwnerEntityPersister, session.EntityMode)); + return GetEntity(session.GenerateEntityKey(key, collectionPersister.OwnerEntityPersister)); } /// Get the entity that owned this persistent collection when it was loaded @@ -1351,7 +1351,7 @@ public void ReplaceDelayedEntityIdentityInsertKeys(EntityKey oldKey, object gene var oldEntry = (EntityEntry) tempObject2; parentsByChild.Clear(); - var newKey = new EntityKey(generatedId, oldEntry.Persister, Session.EntityMode); + var newKey = Session.GenerateEntityKey(generatedId, oldEntry.Persister); AddEntity(newKey, entity); AddEntry(entity, oldEntry.Status, oldEntry.LoadedState, oldEntry.RowId, generatedId, oldEntry.Version, oldEntry.LockMode, oldEntry.ExistsInDatabase, oldEntry.Persister, oldEntry.IsBeingReplicated, diff --git a/src/NHibernate/Engine/TwoPhaseLoad.cs b/src/NHibernate/Engine/TwoPhaseLoad.cs index 5bc30f4e616..ace3d277707 100644 --- a/src/NHibernate/Engine/TwoPhaseLoad.cs +++ b/src/NHibernate/Engine/TwoPhaseLoad.cs @@ -106,7 +106,7 @@ public static void InitializeEntity(object entity, bool readOnly, ISessionImplem object version = Versioning.GetVersion(hydratedState, persister); CacheEntry entry = new CacheEntry(hydratedState, persister, entityEntry.LoadedWithLazyPropertiesUnfetched, version, session, entity); - CacheKey cacheKey = new CacheKey(id, persister.IdentifierType, persister.RootEntityName, session.EntityMode, session.Factory); + CacheKey cacheKey = session.GenerateCacheKey(id, persister.IdentifierType, persister.RootEntityName); bool put = persister.Cache.Put(cacheKey, persister.CacheEntryStructure.Structure(entry), session.Timestamp, version, persister.IsVersioned ? persister.VersionType.Comparator : null, diff --git a/src/NHibernate/Event/Default/AbstractLockUpgradeEventListener.cs b/src/NHibernate/Event/Default/AbstractLockUpgradeEventListener.cs index e8b1ced368f..96db51e2388 100644 --- a/src/NHibernate/Event/Default/AbstractLockUpgradeEventListener.cs +++ b/src/NHibernate/Event/Default/AbstractLockUpgradeEventListener.cs @@ -47,7 +47,7 @@ protected virtual void UpgradeLock(object entity, EntityEntry entry, LockMode re CacheKey ck; if (persister.HasCache) { - ck = new CacheKey(entry.Id, persister.IdentifierType, persister.RootEntityName, source.EntityMode, source.Factory); + ck = source.GenerateCacheKey(entry.Id, persister.IdentifierType, persister.RootEntityName); slock = persister.Cache.Lock(ck, entry.Version); } else diff --git a/src/NHibernate/Event/Default/AbstractReassociateEventListener.cs b/src/NHibernate/Event/Default/AbstractReassociateEventListener.cs index e5ce1fa5466..7e653829825 100644 --- a/src/NHibernate/Event/Default/AbstractReassociateEventListener.cs +++ b/src/NHibernate/Event/Default/AbstractReassociateEventListener.cs @@ -33,7 +33,7 @@ protected EntityEntry Reassociate(AbstractEvent @event, object entity, object id } IEventSource source = @event.Session; - EntityKey key = new EntityKey(id, persister, source.EntityMode); + EntityKey key = source.GenerateEntityKey(id, persister); source.PersistenceContext.CheckUniqueness(key, entity); diff --git a/src/NHibernate/Event/Default/AbstractSaveEventListener.cs b/src/NHibernate/Event/Default/AbstractSaveEventListener.cs index fa8512a8f37..d7c94621c18 100644 --- a/src/NHibernate/Event/Default/AbstractSaveEventListener.cs +++ b/src/NHibernate/Event/Default/AbstractSaveEventListener.cs @@ -160,7 +160,7 @@ protected virtual object PerformSave(object entity, object id, IEntityPersister EntityKey key; if (!useIdentityColumn) { - key = new EntityKey(id, persister, source.EntityMode); + key = source.GenerateEntityKey(id, persister); object old = source.PersistenceContext.GetEntity(key); if (old != null) { @@ -260,7 +260,7 @@ protected virtual object PerformSaveOrReplicate(object entity, EntityKey key, IE id = insert.GeneratedId; //now done in EntityIdentityInsertAction //persister.setIdentifier( entity, id, source.getEntityMode() ); - key = new EntityKey(id, persister, source.EntityMode); + key = source.GenerateEntityKey(id, persister); source.PersistenceContext.CheckUniqueness(key, entity); //source.getBatcher().executeBatch(); //found another way to ensure that all batched joined inserts have been executed } diff --git a/src/NHibernate/Event/Default/DefaultDeleteEventListener.cs b/src/NHibernate/Event/Default/DefaultDeleteEventListener.cs index 42d366efb23..96d38e5d5f2 100644 --- a/src/NHibernate/Event/Default/DefaultDeleteEventListener.cs +++ b/src/NHibernate/Event/Default/DefaultDeleteEventListener.cs @@ -65,7 +65,7 @@ public virtual void OnDelete(DeleteEvent @event, ISet transientEntities) throw new TransientObjectException("the detached instance passed to delete() had a null identifier"); } - EntityKey key = new EntityKey(id, persister, source.EntityMode); + EntityKey key = source.GenerateEntityKey(id, persister); persistenceContext.CheckUniqueness(key, entity); @@ -197,7 +197,7 @@ protected virtual void DeleteEntity(IEventSource session, object entity, EntityE // before any callbacks, etc, so subdeletions see that this deletion happened first persistenceContext.SetEntryStatus(entityEntry, Status.Deleted); - EntityKey key = new EntityKey(entityEntry.Id, persister, session.EntityMode); + EntityKey key = session.GenerateEntityKey(entityEntry.Id, persister); CascadeBeforeDelete(session, persister, entity, entityEntry, transientEntities); diff --git a/src/NHibernate/Event/Default/DefaultEvictEventListener.cs b/src/NHibernate/Event/Default/DefaultEvictEventListener.cs index e3c2e561c6d..01dd86fe7ed 100644 --- a/src/NHibernate/Event/Default/DefaultEvictEventListener.cs +++ b/src/NHibernate/Event/Default/DefaultEvictEventListener.cs @@ -33,7 +33,7 @@ public virtual void OnEvict(EvictEvent @event) { throw new ArgumentException("null identifier"); } - EntityKey key = new EntityKey(id, persister, source.EntityMode); + EntityKey key = source.GenerateEntityKey(id, persister); persistenceContext.RemoveProxy(key); if (!li.IsUninitialized) { diff --git a/src/NHibernate/Event/Default/DefaultFlushEntityEventListener.cs b/src/NHibernate/Event/Default/DefaultFlushEntityEventListener.cs index acc80da7285..96cfa9859d1 100644 --- a/src/NHibernate/Event/Default/DefaultFlushEntityEventListener.cs +++ b/src/NHibernate/Event/Default/DefaultFlushEntityEventListener.cs @@ -522,7 +522,7 @@ private object[] GetDatabaseSnapshot(ISessionImplementor session, IEntityPersist else { //TODO: optimize away this lookup for entities w/o unsaved-value="undefined" - EntityKey entityKey = new EntityKey(id, persister, session.EntityMode); + EntityKey entityKey = session.GenerateEntityKey(id, persister); return session.PersistenceContext.GetCachedDatabaseSnapshot(entityKey); } } diff --git a/src/NHibernate/Event/Default/DefaultInitializeCollectionEventListener.cs b/src/NHibernate/Event/Default/DefaultInitializeCollectionEventListener.cs index 0d391528ef5..78984ff134b 100644 --- a/src/NHibernate/Event/Default/DefaultInitializeCollectionEventListener.cs +++ b/src/NHibernate/Event/Default/DefaultInitializeCollectionEventListener.cs @@ -80,7 +80,7 @@ private bool InitializeCollectionFromCache(object id, ICollectionPersister persi { ISessionFactoryImplementor factory = source.Factory; - CacheKey ck = new CacheKey(id, persister.KeyType, persister.Role, source.EntityMode, factory); + CacheKey ck = source.GenerateCacheKey(id, persister.KeyType, persister.Role); object ce = persister.Cache.Get(ck, source.Timestamp); if (factory.Statistics.IsStatisticsEnabled) diff --git a/src/NHibernate/Event/Default/DefaultLoadEventListener.cs b/src/NHibernate/Event/Default/DefaultLoadEventListener.cs index e4e934ffd6b..a4669cdfbf3 100644 --- a/src/NHibernate/Event/Default/DefaultLoadEventListener.cs +++ b/src/NHibernate/Event/Default/DefaultLoadEventListener.cs @@ -65,7 +65,7 @@ public virtual void OnLoad(LoadEvent @event, LoadType loadType) } } - EntityKey keyToLoad = new EntityKey(@event.EntityId, persister, source.EntityMode); + EntityKey keyToLoad = source.GenerateEntityKey(@event.EntityId, persister); try { if (loadType.IsNakedEntityReturned) @@ -248,7 +248,7 @@ protected virtual object LockAndLoad(LoadEvent @event, IEntityPersister persiste CacheKey ck; if (persister.HasCache) { - ck = new CacheKey(@event.EntityId, persister.IdentifierType, persister.RootEntityName, source.EntityMode, source.Factory); + ck = source.GenerateCacheKey(@event.EntityId, persister.IdentifierType, persister.RootEntityName); sLock = persister.Cache.Lock(ck, null); } else @@ -420,7 +420,7 @@ protected virtual object LoadFromSecondLevelCache(LoadEvent @event, IEntityPersi { ISessionFactoryImplementor factory = source.Factory; - CacheKey ck = new CacheKey(@event.EntityId, persister.IdentifierType, persister.RootEntityName, source.EntityMode, factory); + CacheKey ck = source.GenerateCacheKey(@event.EntityId, persister.IdentifierType, persister.RootEntityName); object ce = persister.Cache.Get(ck, source.Timestamp); if (factory.Statistics.IsStatisticsEnabled) @@ -468,7 +468,7 @@ private object AssembleCacheEntry(CacheEntry entry, object id, IEntityPersister object result = optionalObject ?? session.Instantiate(subclassPersister, id); // make it circular-reference safe - EntityKey entityKey = new EntityKey(id, subclassPersister, session.EntityMode); + EntityKey entityKey = session.GenerateEntityKey(id, subclassPersister); TwoPhaseLoad.AddUninitializedCachedEntity(entityKey, result, subclassPersister, LockMode.None, entry.AreLazyPropertiesUnfetched, entry.Version, session); IType[] types = subclassPersister.PropertyTypes; diff --git a/src/NHibernate/Event/Default/DefaultMergeEventListener.cs b/src/NHibernate/Event/Default/DefaultMergeEventListener.cs index 68235a8d146..78f3dcd625f 100644 --- a/src/NHibernate/Event/Default/DefaultMergeEventListener.cs +++ b/src/NHibernate/Event/Default/DefaultMergeEventListener.cs @@ -138,7 +138,7 @@ public virtual void OnMerge(MergeEvent @event, IDictionary copiedAlready) object id = persister.GetIdentifier(entity, source.EntityMode); if (id != null) { - EntityKey key = new EntityKey(id, persister, source.EntityMode); + EntityKey key = source.GenerateEntityKey(id, persister); object managedEntity = source.PersistenceContext.GetEntity(key); entry = source.PersistenceContext.GetEntry(managedEntity); if (entry != null) @@ -437,7 +437,7 @@ private static bool ExistsInDatabase(object entity, IEventSource source, IEntity object id = persister.GetIdentifier(entity, source.EntityMode); if (id != null) { - EntityKey key = new EntityKey(id, persister, source.EntityMode); + EntityKey key = source.GenerateEntityKey(id, persister); object managedEntity = source.PersistenceContext.GetEntity(key); entry = source.PersistenceContext.GetEntry(managedEntity); } diff --git a/src/NHibernate/Event/Default/DefaultRefreshEventListener.cs b/src/NHibernate/Event/Default/DefaultRefreshEventListener.cs index 97cd2eeac56..b591b6d5308 100644 --- a/src/NHibernate/Event/Default/DefaultRefreshEventListener.cs +++ b/src/NHibernate/Event/Default/DefaultRefreshEventListener.cs @@ -56,7 +56,7 @@ public virtual void OnRefresh(RefreshEvent @event, IDictionary refreshedAlready) { log.Debug("refreshing transient " + MessageHelper.InfoString(persister, id, source.Factory)); } - EntityKey key = new EntityKey(id, persister, source.EntityMode); + EntityKey key = source.GenerateEntityKey(id, persister); if (source.PersistenceContext.GetEntry(key) != null) { throw new PersistentObjectException("attempted to refresh transient instance when persistent instance was already associated with the Session: " + @@ -84,7 +84,7 @@ public virtual void OnRefresh(RefreshEvent @event, IDictionary refreshedAlready) if (e != null) { - EntityKey key = new EntityKey(id, persister, source.EntityMode); + EntityKey key = source.GenerateEntityKey(id, persister); source.PersistenceContext.RemoveEntity(key); if (persister.HasCollections) new EvictVisitor(source).Process(obj, persister); @@ -92,7 +92,7 @@ public virtual void OnRefresh(RefreshEvent @event, IDictionary refreshedAlready) if (persister.HasCache) { - CacheKey ck = new CacheKey(id, persister.IdentifierType, persister.RootEntityName, source.EntityMode, source.Factory); + CacheKey ck = source.GenerateCacheKey(id, persister.IdentifierType, persister.RootEntityName); persister.Cache.Remove(ck); } diff --git a/src/NHibernate/Event/Default/DefaultReplicateEventListener.cs b/src/NHibernate/Event/Default/DefaultReplicateEventListener.cs index bf48655d5cf..0d242538ff9 100644 --- a/src/NHibernate/Event/Default/DefaultReplicateEventListener.cs +++ b/src/NHibernate/Event/Default/DefaultReplicateEventListener.cs @@ -95,7 +95,7 @@ public virtual void OnReplicate(ReplicateEvent @event) } bool regenerate = persister.IsIdentifierAssignedByInsert; // prefer re-generation of identity! - EntityKey key = regenerate ? null : new EntityKey(id, persister, source.EntityMode); + EntityKey key = regenerate ? null : source.GenerateEntityKey(id, persister); PerformSaveOrReplicate(entity, key, persister, regenerate, replicationMode, source, true); } @@ -113,8 +113,8 @@ private void PerformReplication(object entity, object id, object version, IEntit source.PersistenceContext.AddEntity( entity, persister.IsMutable ? Status.Loaded : Status.ReadOnly, - null, - new EntityKey(id, persister, source.EntityMode), + null, + source.GenerateEntityKey(id, persister), version, LockMode.None, true, diff --git a/src/NHibernate/Event/Default/DefaultSaveOrUpdateEventListener.cs b/src/NHibernate/Event/Default/DefaultSaveOrUpdateEventListener.cs index e5d1c473bb8..cce3a374256 100644 --- a/src/NHibernate/Event/Default/DefaultSaveOrUpdateEventListener.cs +++ b/src/NHibernate/Event/Default/DefaultSaveOrUpdateEventListener.cs @@ -222,7 +222,7 @@ protected virtual void PerformUpdate(SaveOrUpdateEvent @event, object entity, IE IEventSource source = @event.Session; - EntityKey key = new EntityKey(@event.RequestedId, persister, source.EntityMode); + EntityKey key = source.GenerateEntityKey(@event.RequestedId, persister); source.PersistenceContext.CheckUniqueness(key, entity); diff --git a/src/NHibernate/Impl/AbstractSessionImpl.cs b/src/NHibernate/Impl/AbstractSessionImpl.cs index 5ff5df6294f..0fdd0399627 100644 --- a/src/NHibernate/Impl/AbstractSessionImpl.cs +++ b/src/NHibernate/Impl/AbstractSessionImpl.cs @@ -3,6 +3,8 @@ using System.Collections.Generic; using System.Data; using NHibernate.AdoNet; +using NHibernate.Cache; +using NHibernate.Cfg; using NHibernate.Collection; using NHibernate.Engine; using NHibernate.Engine.Query; @@ -29,6 +31,21 @@ public abstract class AbstractSessionImpl : ISessionImplementor private readonly Guid sessionId = Guid.NewGuid(); private bool closed; + private string _tenantIdentifier; + public string TenantIdentifier + { + get + { + return _tenantIdentifier; + } + set + { + if (MultiTenancyStrategy.None == factory.Settings.MultiTenancyStrategy) + throw new HibernateException("Sessionfactory was not configured for multi-tenancy"); + _tenantIdentifier = value; + } + } + public ITransactionContext TransactionContext { get; set; @@ -71,6 +88,21 @@ public void Initialize() public abstract object ImmediateLoad(string entityName, object id); public abstract long Timestamp { get; } + public EntityKey GenerateEntityKey(object id, IEntityPersister persister) + { + return GenerateEntityKey(id, persister, EntityMode); + } + + public EntityKey GenerateEntityKey(object id, IEntityPersister persister, EntityMode entityMode) + { + return new EntityKey(id, persister, entityMode, TenantIdentifier); + } + + public CacheKey GenerateCacheKey(object id, IType type, string entityOrRoleName) + { + return new CacheKey(id, type, entityOrRoleName, EntityMode, TenantIdentifier, Factory); + } + public ISessionFactoryImplementor Factory { get { return factory; } diff --git a/src/NHibernate/Impl/SessionFactoryImpl.cs b/src/NHibernate/Impl/SessionFactoryImpl.cs index 55dbbd06a5d..adbe84ee57c 100644 --- a/src/NHibernate/Impl/SessionFactoryImpl.cs +++ b/src/NHibernate/Impl/SessionFactoryImpl.cs @@ -819,8 +819,8 @@ public void Evict(System.Type persistentClass, object id) if (log.IsDebugEnabled) { log.Debug("evicting second-level cache: " + MessageHelper.InfoString(p, id)); - } - CacheKey ck = new CacheKey(id, p.IdentifierType, p.RootEntityName, EntityMode.Poco, this); + } + CacheKey ck = GenerateCacheKeyForEvict(id, p.IdentifierType, p.RootEntityName); p.Cache.Remove(ck); } } @@ -849,35 +849,54 @@ public void EvictEntity(string entityName) } p.Cache.Clear(); } - } - - public void EvictEntity(string entityName, object id) - { - IEntityPersister p = GetEntityPersister(entityName); - if (p.HasCache) - { - if (log.IsDebugEnabled) - { - log.Debug("evicting second-level cache: " + MessageHelper.InfoString(p, id, this)); - } - CacheKey cacheKey = new CacheKey(id, p.IdentifierType, p.RootEntityName, EntityMode.Poco, this); - p.Cache.Remove(cacheKey); - } - } - - public void EvictCollection(string roleName, object id) - { - ICollectionPersister p = GetCollectionPersister(roleName); - if (p.HasCache) - { - if (log.IsDebugEnabled) - { - log.Debug("evicting second-level cache: " + MessageHelper.InfoString(p, id)); - } - CacheKey ck = new CacheKey(id, p.KeyType, p.Role, EntityMode.Poco, this); - p.Cache.Remove(ck); - } - } + } + + public void EvictEntity(string entityName, object id) + { + IEntityPersister p = GetEntityPersister(entityName); + if (p.HasCache) + { + if (log.IsDebugEnabled) + { + log.Debug("evicting second-level cache: " + MessageHelper.InfoString(p, id, this)); + } + CacheKey cacheKey = GenerateCacheKeyForEvict(id, p.IdentifierType, p.RootEntityName); + p.Cache.Remove(cacheKey); + } + } + + public void EvictCollection(string roleName, object id) + { + ICollectionPersister p = GetCollectionPersister(roleName); + if (p.HasCache) + { + if (log.IsDebugEnabled) + { + log.Debug("evicting second-level cache: " + MessageHelper.InfoString(p, id)); + } + CacheKey ck = GenerateCacheKeyForEvict(id, p.KeyType, p.Role); + p.Cache.Remove(ck); + } + } + + private CacheKey GenerateCacheKeyForEvict(object id, IType type, string entityOrRoleName) + { + // if there is a session context, use that to generate the key (may be tenant-specific session) + if (CurrentSessionContext != null) + { + return CurrentSessionContext + .CurrentSession() + .GetSessionImplementation() + .GenerateCacheKey(id, type, entityOrRoleName); + } + + if (MultiTenancyStrategy.None != Settings.MultiTenancyStrategy) + { + throw new ApplicationException("Could not find an open session. When using a multi-tenant Session Factory, a open session context is required to evict entities by id"); + } + return new CacheKey(id, type, entityOrRoleName, EntityMode.Poco, null, this); + + } public void EvictCollection(string roleName) { diff --git a/src/NHibernate/Impl/StatelessSessionImpl.cs b/src/NHibernate/Impl/StatelessSessionImpl.cs index 2fe64bf5554..e2e2278132b 100644 --- a/src/NHibernate/Impl/StatelessSessionImpl.cs +++ b/src/NHibernate/Impl/StatelessSessionImpl.cs @@ -70,7 +70,7 @@ public override object InternalLoad(string entityName, object id, bool eager, bo { CheckAndUpdateSessionStatus(); IEntityPersister persister = Factory.GetEntityPersister(entityName); - object loaded = temporaryPersistenceContext.GetEntity(new EntityKey(id, persister, EntityMode.Poco)); + object loaded = temporaryPersistenceContext.GetEntity(GenerateEntityKey(id, persister, EntityMode.Poco)); if (loaded != null) { return loaded; @@ -827,7 +827,7 @@ public void Refresh(string entityName, object entity, LockMode lockMode) if (persister.HasCache) { - CacheKey ck = new CacheKey(id, persister.IdentifierType, persister.RootEntityName, EntityMode, Factory); + CacheKey ck = GenerateCacheKey(id, persister.IdentifierType, persister.RootEntityName); persister.Cache.Remove(ck); } diff --git a/src/NHibernate/Loader/Loader.cs b/src/NHibernate/Loader/Loader.cs index e5c039e93b0..01a622e78e6 100644 --- a/src/NHibernate/Loader/Loader.cs +++ b/src/NHibernate/Loader/Loader.cs @@ -313,7 +313,7 @@ internal static EntityKey GetOptionalObjectKey(QueryParameters queryParameters, if (optionalObject != null && !string.IsNullOrEmpty(optionalEntityName)) { - return new EntityKey(optionalId, session.GetEntityPersister(optionalEntityName, optionalObject), session.EntityMode); + return session.GenerateEntityKey(optionalId, session.GetEntityPersister(optionalEntityName, optionalObject)); } else { @@ -799,7 +799,7 @@ private EntityKey GetKeyFromResultSet(int i, IEntityPersister persister, object } } - return resultId == null ? null : new EntityKey(resultId, persister, session.EntityMode); + return resultId == null ? null : session.GenerateEntityKey(resultId, persister); } /// diff --git a/src/NHibernate/NHibernate.csproj b/src/NHibernate/NHibernate.csproj index 5c4221830e1..49036787e23 100644 --- a/src/NHibernate/NHibernate.csproj +++ b/src/NHibernate/NHibernate.csproj @@ -125,13 +125,16 @@ + + + diff --git a/src/NHibernate/Persister/Collection/AbstractCollectionPersister.cs b/src/NHibernate/Persister/Collection/AbstractCollectionPersister.cs index c46f7e44cad..3988c1641eb 100644 --- a/src/NHibernate/Persister/Collection/AbstractCollectionPersister.cs +++ b/src/NHibernate/Persister/Collection/AbstractCollectionPersister.cs @@ -612,8 +612,8 @@ private ICollectionInitializer GetSubselectInitializer(object key, ISessionImple IPersistenceContext persistenceContext = session.PersistenceContext; - SubselectFetch subselect = - persistenceContext.BatchFetchQueue.GetSubselect(new EntityKey(key, OwnerEntityPersister, session.EntityMode)); + SubselectFetch subselect = + persistenceContext.BatchFetchQueue.GetSubselect(session.GenerateEntityKey(key, OwnerEntityPersister)); if (subselect == null) { diff --git a/src/NHibernate/Persister/Entity/AbstractEntityPersister.cs b/src/NHibernate/Persister/Entity/AbstractEntityPersister.cs index cea61588399..6403da9be14 100644 --- a/src/NHibernate/Persister/Entity/AbstractEntityPersister.cs +++ b/src/NHibernate/Persister/Entity/AbstractEntityPersister.cs @@ -1208,7 +1208,7 @@ public virtual object InitializeLazyProperty(string fieldName, object entity, IS if (HasCache) { - CacheKey cacheKey = new CacheKey(id, IdentifierType, EntityName, session.EntityMode, Factory); + CacheKey cacheKey = session.GenerateCacheKey(id, IdentifierType, EntityName); object ce = Cache.Get(cacheKey, session.Timestamp); if (ce != null) { @@ -3076,7 +3076,7 @@ public void Delete(object id, object version, object obj, ISessionImplementor se // first we need to locate the "loaded" state // // Note, it potentially could be a proxy, so perform the location the safe way... - EntityKey key = new EntityKey(id, this, session.EntityMode); + EntityKey key = session.GenerateEntityKey(id, this); object entity = session.PersistenceContext.GetEntity(key); if (entity != null) { @@ -3719,7 +3719,7 @@ public virtual void AfterReassociate(object entity, ISessionImplementor session) // check to see if it is in the second-level cache if (HasCache) { - CacheKey ck = new CacheKey(id, IdentifierType, RootEntityName, session.EntityMode, session.Factory); + CacheKey ck = session.GenerateCacheKey(id, IdentifierType, RootEntityName); if (Cache.Get(ck, session.Timestamp) != null) return false; } diff --git a/src/NHibernate/Persister/Entity/NamedQueryLoader.cs b/src/NHibernate/Persister/Entity/NamedQueryLoader.cs index 6dc46f0ba10..fd955f7538f 100644 --- a/src/NHibernate/Persister/Entity/NamedQueryLoader.cs +++ b/src/NHibernate/Persister/Entity/NamedQueryLoader.cs @@ -46,7 +46,7 @@ public object Load(object id, object optionalObject, ISessionImplementor session // now look up the object we are really interested in! // (this lets us correctly handle proxies and multi-row // or multi-column queries) - return session.PersistenceContext.GetEntity(new EntityKey(id, persister, session.EntityMode)); + return session.PersistenceContext.GetEntity(session.GenerateEntityKey(id, persister)); } } } \ No newline at end of file diff --git a/src/NHibernate/Proxy/AbstractLazyInitializer.cs b/src/NHibernate/Proxy/AbstractLazyInitializer.cs index 71f3f34a5bd..038acd30c9b 100644 --- a/src/NHibernate/Proxy/AbstractLazyInitializer.cs +++ b/src/NHibernate/Proxy/AbstractLazyInitializer.cs @@ -198,8 +198,8 @@ public object GetImplementation() /// The Session to get the object from. /// The Persistent Object this proxy is Proxying, or . public object GetImplementation(ISessionImplementor s) - { - EntityKey key = new EntityKey(Identifier, s.Factory.GetEntityPersister(EntityName), s.EntityMode); + { + EntityKey key = s.GenerateEntityKey(Identifier, s.Factory.GetEntityPersister(EntityName)); return s.PersistenceContext.GetEntity(key); } @@ -247,9 +247,9 @@ private void ErrorIfReadOnlySettingNotAvailable() private static EntityKey GenerateEntityKeyOrNull(object id, ISessionImplementor s, string entityName) { if (id == null || s == null || entityName == null) - return null; - - return new EntityKey(id, s.Factory.GetEntityPersister(entityName), s.EntityMode); + return null; + + return s.GenerateEntityKey(id, s.Factory.GetEntityPersister(entityName)); } private void CheckTargetState() diff --git a/src/NHibernate/Proxy/Poco/BasicLazyInitializer.cs b/src/NHibernate/Proxy/Poco/BasicLazyInitializer.cs index 35e9f6d5ff3..a6f33a909e8 100644 --- a/src/NHibernate/Proxy/Poco/BasicLazyInitializer.cs +++ b/src/NHibernate/Proxy/Poco/BasicLazyInitializer.cs @@ -111,7 +111,7 @@ public virtual object Invoke(MethodInfo method, object[] args, object proxy) if (Target == null & Session != null) { - EntityKey key = new EntityKey(Identifier, Session.Factory.GetEntityPersister(EntityName), Session.EntityMode); + EntityKey key = Session.GenerateEntityKey(Identifier, Session.Factory.GetEntityPersister(EntityName)); object entity = Session.PersistenceContext.GetEntity(key); if (entity != null) SetImplementation(entity); diff --git a/src/NHibernate/Type/ManyToOneType.cs b/src/NHibernate/Type/ManyToOneType.cs index 1b11d9dae07..c5744090372 100644 --- a/src/NHibernate/Type/ManyToOneType.cs +++ b/src/NHibernate/Type/ManyToOneType.cs @@ -92,7 +92,7 @@ private void ScheduleBatchLoadIfNeeded(object id, ISessionImplementor session) if (uniqueKeyPropertyName == null && id != null) { IEntityPersister persister = session.Factory.GetEntityPersister(GetAssociatedEntityName()); - EntityKey entityKey = new EntityKey(id, persister, session.EntityMode); + EntityKey entityKey = session.GenerateEntityKey(id, persister); if (!session.PersistenceContext.ContainsEntity(entityKey)) { session.PersistenceContext.BatchFetchQueue.AddBatchLoadableEntityKey(entityKey); diff --git a/src/NHibernate/Type/OneToOneType.cs b/src/NHibernate/Type/OneToOneType.cs index c72db0af253..85da360128f 100644 --- a/src/NHibernate/Type/OneToOneType.cs +++ b/src/NHibernate/Type/OneToOneType.cs @@ -74,7 +74,7 @@ public override bool IsNull(object owner, ISessionImplementor session) IEntityPersister ownerPersister = session.Factory.GetEntityPersister(entityName); object id = session.GetContextEntityIdentifier(owner); - EntityKey entityKey = new EntityKey(id, ownerPersister, session.EntityMode); + EntityKey entityKey = session.GenerateEntityKey(id, ownerPersister); return session.PersistenceContext.IsPropertyNull(entityKey, PropertyName); } @@ -103,8 +103,8 @@ public override object Hydrate(IDataReader rs, string[] names, ISessionImplement { object[] values = ownerIdType.GetPropertyValues(identifier, session); object id = componentType.ResolveIdentifier(values, session, null); - IEntityPersister persister = session.Factory.GetEntityPersister(type.ReturnedClass.FullName); - var key = new EntityKey(id, persister, session.EntityMode); + IEntityPersister persister = session.Factory.GetEntityPersister(type.ReturnedClass.FullName); + var key = session.GenerateEntityKey(id, persister); return session.PersistenceContext.GetEntity(key); } }