MetadataCache.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ Dotnetfx_Vista_SP2 / Dotnetfx_Vista_SP2 / 8.0.50727.4016 / DEVDIV / depot / DevDiv / releases / Orcas / QFE / ndp / fx / src / DataEntity / System / Data / Metadata / MetadataCache.cs / 2 / MetadataCache.cs

                            //---------------------------------------------------------------------- 
// 
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// 
// 
// @owner  [....]
//--------------------------------------------------------------------- 
 
using System;
using System.Collections.Generic; 
using System.Collections.ObjectModel;
using System.Data.Common;
using System.Data.Mapping;
using System.Diagnostics; 
using System.Diagnostics.CodeAnalysis;
using System.Threading; 
using System.IO; 
using System.Xml;
using System.Security.Permissions; 
using System.Data.Entity;


namespace System.Data.Metadata.Edm 
{
    ///  
    /// Runtime Metadata Cache - this class contains the metadata cache entry for edm and store collections. 
    /// 
    internal static class MetadataCache 
    {
        #region Fields

        // This is the period in the periodic cleanup measured in milliseconds 
        private const int cleanupPeriod = 5 * 60 * 1000;
 
        // This dictionary contains the cache entry for the edm item collection. The reason why we need to keep a seperate dictionary 
        // for CSpace item collection is that the same model can be used for different providers. We don't want to load the model
        // again and again 
        private static readonly Dictionary _edmLevelCache = new Dictionary(StringComparer.OrdinalIgnoreCase);

        /// 
        /// This dictionary contains the store cache entry - this entry will only keep track of StorageMappingItemCollection, since internally 
        /// storage mapping item collection keeps strong references to both edm item collection and store item collection.
        ///  
        private static readonly Dictionary _storeLevelCache = new Dictionary(StringComparer.OrdinalIgnoreCase); 

        ///  
        /// The list maintains the store metadata entries that are still in use, maybe because someone is still holding a strong reference
        /// to it. We need to scan this list everytime the clean up thread wakes up and make sure if the item collection is no longer in use,
        /// call clear on query cache
        ///  
        private static readonly List _metadataEntriesRemovedFromCache = new List();
 
        ///  
        /// Read/Write lock for edm cache
        ///  
        private static readonly object _edmLevelLock = new object();

        /// 
        /// Read/Write lock for the store cache 
        /// 
        private static readonly object _storeLevelLock = new object(); 
 
        // Periodic thread which runs every n mins (look up the cleanupPeriod variable to see the exact time), walks through
        // every item in other store and edm cache and tries to do some cleanup 
        private static Timer timer = new Timer(PeriodicCleanupCallback, null, cleanupPeriod, cleanupPeriod);

        #endregion
 
        #region Methods
 
        ///  
        /// The purpose of the thread is to do cleanup. It marks the object in various stages before it actually cleans up the object
        /// Here's what this does for each entry in the cache: 
        ///     1> First checks if the entry is marked for cleanup.
        ///     2> If the entry is marked for cleanup, that means its in one of the following 3 states
        ///         a) If the strong reference to item collection is not null, it means that this item was marked for cleanup in
        ///            the last cleanup cycle and we must make the strong reference set to null so that it can be garbage collected. 
        ///         b) Otherwise, we are waiting for GC to collect the item collection so that we can remove this entry from the cache
        ///            If the weak reference to item collection is still alive, we don't do anything 
        ///         c) If the weak reference to item collection is not alive, we need to remove this entry from the cache 
        ///     3> If the entry is not marked for cleanup, then check whether the weak reference to entry token is alive
        ///         a) if it is alive, then this entry is in use and we must do nothing 
        ///         b) Otherwise, we can mark this entry for cleanup
        /// 
        /// 
        private static void PeriodicCleanupCallback(object state) 
        {
            // Perform clean up on edm cache 
            DoCacheClean(_edmLevelCache, _edmLevelLock); 

            // Perform clean up on store cache 
            DoCacheClean(_storeLevelCache, _storeLevelLock);
        }

        ///  
        /// Walks through the given cache and calls cleanup on each entry in the cache
        ///  
        ///  
        /// 
        ///  
        private static void DoCacheClean(Dictionary cache, object objectToLock) where T: MetadataEntry
        {
            // Sometime, for some reason, timer can be initialized and the cache is still not initialized.
            if (cache != null) 
            {
                List> keysForRemoval = null; 
 
                lock (objectToLock)
                { 
                    // we should check for type of the lock object first, since otherwise we might be reading the count of the list
                    // while some other thread might be modifying it. For e.g. when this function is called for edmcache,
                    // we will be acquiring edmlock and trying to get the count for the list, while some other thread
                    // might be calling ClearCache and we might be adding entries to the list 
                    if (objectToLock == _storeLevelLock && _metadataEntriesRemovedFromCache.Count != 0)
                    { 
                        // First check the list of entries and remove things which are no longer in use 
                        for (int i = _metadataEntriesRemovedFromCache.Count - 1; 0 <= i; i--)
                        { 
                            if (!_metadataEntriesRemovedFromCache[i].IsEntryStillValid())
                            {
                                // Clear the query cache
                                _metadataEntriesRemovedFromCache[i].CleanupQueryCache(); 
                                // Remove the entry at the current index. This is the reason why we
                                // go backwards. 
                                _metadataEntriesRemovedFromCache.RemoveAt(i); 
                            }
                        } 
                    }

                    // We have to use a list to keep track of the keys to remove because we can't remove while enumerating
                    foreach (KeyValuePair pair in cache) 
                    {
                        if (pair.Value.PeriodicCleanUpThread()) 
                        { 
                            if (keysForRemoval == null)
                            { 
                                keysForRemoval = new List>();
                            }
                            keysForRemoval.Add(pair);
                        } 
                    }
 
                    // Remove all the entries from the cache 
                    if (keysForRemoval != null)
                    { 
                        for (int i = 0; i < keysForRemoval.Count; i++)
                        {
                            keysForRemoval[i].Value.Clear();
                            cache.Remove(keysForRemoval[i].Key); 
                        }
                    } 
                } 
            }
        } 

        /// 
        /// Retrieves an cache entry holding to edm metadata for a given cache key
        ///  
        /// string containing all the files from which edm metadata is to be retrieved
        /// An instance of the composite MetadataArtifactLoader 
        /// The metadata entry token for the returned entry 
        /// Returns the entry containing the edm metadata
        internal static EdmItemCollection GetOrCreateEdmItemCollection(string cacheKey, 
                                                             MetadataArtifactLoader loader,
                                                             out object entryToken)
        {
            EdmMetadataEntry entry = GetCacheEntry(_edmLevelCache, cacheKey, _edmLevelLock, 
                new EdmMetadataEntryConstructor(), out entryToken);
 
            // Load the edm item collection or if the collection is already loaded, check for security permission 
            LoadItemCollection(new EdmItemCollectionLoader(loader), entry);
 
            return entry.EdmItemCollection;
        }

        ///  
        /// Retrieves an entry holding store metadata for a given cache key
        ///  
        /// The connection string whose store metadata is to be retrieved 
        /// An instance of the composite MetadataArtifactLoader
        /// The metadata entry token for the returned entry 
        /// the entry containing the information on how to load store metadata
        internal static StorageMappingItemCollection GetOrCreateStoreAndMappingItemCollections(
                                                                 string cacheKey,
                                                                 MetadataArtifactLoader loader, 
                                                                 EdmItemCollection edmItemCollection,
                                                                 out object entryToken) 
        { 
            StoreMetadataEntry entry = GetCacheEntry(_storeLevelCache, cacheKey, _storeLevelLock,
                new StoreMetadataEntryConstructor(), out entryToken); 

            // Load the store item collection or if the collection is already loaded, check for security permission
            LoadItemCollection(new StoreItemCollectionLoader(edmItemCollection, loader), entry);
 
            return entry.StorageMappingItemCollection;
        } 
 
        /// 
        /// Get the entry from the cache given the cache key. If the entry is not present, it creates a new entry and 
        /// adds it to the cache
        /// 
        /// 
        ///  
        /// 
        ///  
        ///  
        /// 
        ///  
        private static T GetCacheEntry(Dictionary cache, string cacheKey, object objectToLock,
            IMetadataEntryConstructor metadataEntry, out object entryToken) where T: MetadataEntry
        {
            T entry; 

            // In the critical section, we need to do the minimal thing to ensure correctness 
            // Within the lock, we will see if an entry is present. If it is not, we will create a new entry and 
            // add it to the cache. In either case, we need to ensure the token to make sure so that any other
            // thread that comes looking for the same entry does nothing in this critical section 
            // Also the cleanup thread doesn't do anything since the token is alive
            lock (objectToLock)
            {
                if (cache.TryGetValue(cacheKey, out entry)) 
                {
                    entryToken = entry.EnsureToken(); 
                } 
                else
                { 
                    entry = metadataEntry.GetMetadataEntry();
                    entryToken = entry.EnsureToken();
                    cache.Add(cacheKey, entry);
                } 
            }
 
            return entry; 
        }
 
        /// 
        /// Loads the item collection for the entry
        /// 
        /// struct which loads an item collection 
        /// entry whose item collection needs to be loaded
        private static void LoadItemCollection(IItemCollectionLoader itemCollectionLoader, T entry) where T : MetadataEntry 
        { 
            // At this point, you have made sure that there is an entry with an alive token in the cache so that
            // other threads can find it if they come querying for it, and cleanup thread won't clean the entry 
            // If two or more threads come one after the other, we don't won't both of them to load the metadata.
            // So if one of them is loading the metadata, the other should wait and then use the same metadata.
            // For that reason, we have this lock on the entry itself to make sure that this happens. Its okay to
            // update the item collection outside the lock, since assignment are guarantees to be atomic and no two 
            // thread are updating this at the same time
            bool isItemCollectionAlreadyLoaded = true; 
 
            if (!entry.IsLoaded)
            { 
                lock (entry)
                {
                    if (!entry.IsLoaded)
                    { 
                        itemCollectionLoader.LoadItemCollection(entry);
                        isItemCollectionAlreadyLoaded = false; 
                    } 
                }
            } 

            Debug.Assert(entry.IsLoaded, "The entry must be loaded at this point");

            // Making sure that the thread which loaded the item collection is not checking for file permisssions 
            // again
            if (isItemCollectionAlreadyLoaded) 
            { 
                entry.CheckFilePermission();
            } 
        }

        /// 
        /// Remove all the entries from the cache 
        /// 
        internal static void Clear() 
        { 
            lock (_edmLevelLock)
            { 
                _edmLevelCache.Clear();
            }

            lock (_storeLevelLock) 
            {
                // Call clear on each of the metadata entries. This is to make sure we clear all the performance 
                // counters associated with the query cache 
                foreach (StoreMetadataEntry entry in _storeLevelCache.Values)
                { 
                    // Check if the weak reference to item collection is still alive
                    if (entry.IsEntryStillValid())
                    {
                        _metadataEntriesRemovedFromCache.Add(entry); 
                    }
                    else 
                    { 
                        entry.Clear();
                    } 
                }
                _storeLevelCache.Clear();
            }
        } 

        #endregion 
 
        #region InlineClasses
 
        /// 
        /// The base class having common implementation for all metadata entry classes
        /// 
        private abstract class MetadataEntry 
        {
            private WeakReference _entryTokenReference; 
            private ItemCollection _itemCollection; 
            private WeakReference _weakReferenceItemCollection;
            private bool _markEntryForCleanup; 
            private FileIOPermission _filePermissions;

            /// 
            /// The constructor for constructing this MetadataEntry 
            /// 
            internal MetadataEntry() 
            { 
                // Create this once per life time of the object. Creating extra weak references causing unnecessary GC pressure
                _entryTokenReference = new WeakReference(null); 
                _weakReferenceItemCollection = new WeakReference(null);
            }

            ///  
            /// returns the item collection inside this metadata entry
            ///  
            protected ItemCollection ItemCollection { get { return _itemCollection; } } 

            ///  
            /// Update the entry with the given item collection
            /// 
            /// 
            protected void UpdateMetadataEntry(ItemCollection itemCollection, FileIOPermission filePermissions) 
            {
                Debug.Assert(_entryTokenReference.IsAlive, "You must call Ensure token before you call this method"); 
                Debug.Assert(_markEntryForCleanup == false, "The entry must not be marked for cleanup"); 
                Debug.Assert(_itemCollection == null, "Item collection must be null");
                Debug.Assert(_filePermissions == null, "filePermissions must be null"); 

                // Update strong and weak reference for item collection
                _weakReferenceItemCollection.Target = itemCollection;
                _filePermissions = filePermissions; 

                // do this last, because it signals that we are loaded 
                _itemCollection = itemCollection; 
            }
 
            internal bool IsLoaded { get { return _itemCollection != null; } }

            /// 
            /// This method is called periodically by the cleanup thread to make the unused entries 
            /// go through various stages, before it is ready for cleanup. If it is ready, this method
            /// returns true and then the entry is completely removed from the cache 
            ///  
            /// 
            internal bool PeriodicCleanUpThread() 
            {
                // Here's what this does for each entry in the cache:
                //     1> First checks if the entry is marked for cleanup.
                //     2> If the entry is marked for cleanup, that means its in one of the following 3 states 
                //         a) If the strong reference to item collection is not null, it means that this item was marked for cleanup in
                //            the last cleanup cycle and we must make the strong reference set to null so that it can be garbage collected. (GEN 2) 
                //         b) Otherwise, we are waiting for GC to collect the item collection so that we can remove this entry from the cache 
                //            If the weak reference to item collection is still alive, we don't do anything
                //         c) If the weak reference to item collection is not alive, we need to remove this entry from the cache (GEN 3) 
                //     3> If the entry is not marked for cleanup, then check whether the weak reference to entry token is alive
                //         a) if it is alive, then this entry is in use and we must do nothing
                //         b) Otherwise, we can mark this entry for cleanup (GEN 1)
                if (_markEntryForCleanup) 
                {
                    Debug.Assert(_entryTokenReference.IsAlive == false, "Entry Token must never be alive if the entry is marked for cleanup"); 
 
                    if (_itemCollection != null)
                    { 
                        // GEN 2
                        _itemCollection = null;
                    }
                    else if (!_weakReferenceItemCollection.IsAlive) 
                    {
                        // GEN 3 
                        _filePermissions = null; 
                        // this entry must be removed from the cache
                        return true; 
                    }
                }
                else if (!_entryTokenReference.IsAlive)
                { 
                    // GEN 1
 
                    // If someone creates a entity connection, and calls GetMetadataWorkspace. This creates an cache entry, 
                    // but the item collection is not initialized yet (since store item collection are initialized only
                    // when one calls connection.Open()). Suppose now the connection is no longer used - in other words, 
                    // open was never called and it goes out of scope. After some time when the connection gets GC'ed,
                    // entry token won't be alive any longer, but item collection inside it will be null, since it was never initialized.
                    // So we can't assert that item collection must be always initialized here
                    _markEntryForCleanup = true; 
                }
 
                return false; 
            }
 
            /// 
            /// Make sure that the entry has a alive token and returns that token - it can be new token or an existing
            /// one, depending on the state of the entry
            ///  
            /// 
            internal object EnsureToken() 
            { 
                object entryToken = _entryTokenReference.Target;
                ItemCollection itemCollection = (ItemCollection)_weakReferenceItemCollection.Target; 

                // When ensure token is called, the entry can be in different stages
                // 1> Its a newly created entry - no token, no item collection, etc. Just create a new token and
                //    return back 
                // 2> An entry already in use - the weak reference to token must be alive. We just need to grab the token
                //    and return it 
                // 3> No one is using this entry and hence the token is no longer alive. If we have strong reference to item 
                //    collection, then create a new token and return it
                // 4> No one has used this token for one cleanup cycle and hence strong reference is null. But the weak reference 
                //    is still alive. We need to make the initialize the strong reference again, create a new token and return it
                // 5> This entry has not been used for long enough that even the weak reference is no longer alive. This entry is
                //    now exactly like a new entry, except that it is still marked for cleanup. Create a new token, set mark for
                //    cleanup to false and return the token 
                if (_entryTokenReference.IsAlive)
                { 
                    Debug.Assert(_markEntryForCleanup == false, "An entry with alive token cannot be marked for cleanup"); 
                    // ItemCollection strong pointer can be null or not null. If the entry has been created, and loadItemCollection
                    // hasn't been called yet, the token will be alive, but item collection will be null. If someone called 
                    // load item collection, then item collection will not be non-null
                    return entryToken;
                }
                // If the entry token is not alive, then it can be either a new created entry with everything set 
                // to null or it must be one of the entries which is no longer in use
                else if (_itemCollection != null) 
                { 
                    Debug.Assert(_weakReferenceItemCollection.IsAlive, "Since the strong reference is still there, weak reference must also be alive");
                    // This means that no one is using the item collection, and its waiting to be cleanuped 
                }
                else
                {
                    if (_weakReferenceItemCollection.IsAlive) 
                    {
                        Debug.Assert(_markEntryForCleanup, "Since the strong reference is null, this entry must be marked for cleanup"); 
                        // Initialize the strong reference to item collection 
                        _itemCollection = itemCollection;
                    } 
                    else
                    {
                        // no more references to the collection
                        // are available, so get rid of the permissions 
                        // object.  We will get a new one when we get a new collection
                        _filePermissions = null; 
                    } 
                }
                // Even if the _weakReferenceItemCollection is no longer alive, we will reuse this entry. Assign a new entry token and set mark for cleanup to false 
                // so that this entry is not cleared by the cleanup thread

                entryToken = new object();
                _entryTokenReference.Target = entryToken; 
                _markEntryForCleanup = false;
                return entryToken; 
            } 

            ///  
            /// Check if the thread has appropriate permissions to use the already loaded metadata
            /// 
            internal void CheckFilePermission()
            { 
                Debug.Assert(_itemCollection != null, "Item collection must be present since we want to reuse the metadata");
                Debug.Assert(_entryTokenReference.IsAlive, "This entry must be in use"); 
                Debug.Assert(_markEntryForCleanup == false, "The entry must not marked for cleanup"); 
                Debug.Assert(_weakReferenceItemCollection.IsAlive, "Weak reference to item collection must be alive");
 
                // we will have an empty ItemCollection (no files were used to load it)
                if (_filePermissions != null)
                {
                    _filePermissions.Demand(); 
                }
            } 
 
            /// 
            /// Dispose the composite loader that encapsulates all artifacts 
            /// 
            internal virtual void Clear()
            {
            } 

            ///  
            /// This returns true if the entry is still in use - the entry can be use if the entry token is 
            /// still alive.If the entry token is still not alive, it means that no one is using this entry
            /// and its okay to remove it. Today there is no 
            /// 
            /// 
            internal bool IsEntryStillValid()
            { 
                return _entryTokenReference.IsAlive;
            } 
        } 

        ///  
        /// A metadata entry holding EdmItemCollection object for the cache
        /// 
        private class EdmMetadataEntry : MetadataEntry
        { 
            /// 
            /// Gets the EdmItemCollection for this entry 
            ///  
            internal EdmItemCollection EdmItemCollection
            { 
                get
                {
                    return (EdmItemCollection)this.ItemCollection;
                } 
            }
 
            ///  
            /// Just loads the edm item collection
            ///  
            /// 
            [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2103:ReviewImperativeSecurity")]
            internal void LoadEdmItemCollection(MetadataArtifactLoader loader)
            { 
                Debug.Assert(loader != null, "loader is null");
 
                List readers = loader.CreateReaders(DataSpace.CSpace); 
                try
                { 
                    EdmItemCollection itemCollection = new EdmItemCollection(
                                                           readers,
                                                           loader.GetPaths(DataSpace.CSpace)
                                                            ); 

                    List permissionPaths = new List(); 
                    loader.CollectFilePermissionPaths(permissionPaths, DataSpace.CSpace); 
                    FileIOPermission filePermissions = null;
                    if (permissionPaths.Count > 0) 
                    {
                        filePermissions = new FileIOPermission(FileIOPermissionAccess.Read, permissionPaths.ToArray());
                    }
 
                    UpdateMetadataEntry(itemCollection, filePermissions);
                } 
                finally 
                {
                    Helper.DisposeXmlReaders(readers); 
                }
            }
        }
 
        /// 
        /// A metadata entry holding a StoreItemCollection and a StorageMappingItemCollection objects for the cache 
        ///  
        private class StoreMetadataEntry : MetadataEntry
        { 
            private System.Data.Common.QueryCache.QueryCacheManager _queryCacheManager;

            /// 
            /// The constructor for constructing this entry with an StoreItemCollection and a StorageMappingItemCollection 
            /// 
            /// An instance of the composite MetadataArtifactLoader 
            internal StoreMetadataEntry() 
            {
            } 

            /// 
            /// Gets the StorageMappingItemCollection for this entry
            ///  
            internal StorageMappingItemCollection StorageMappingItemCollection
            { 
                get 
                {
                    return (StorageMappingItemCollection)this.ItemCollection; 
                }
            }

            ///  
            /// Load store specific metadata into the StoreItemCollection for this entry
            ///  
            /// The store-specific provider factory 
            /// edmItemCollection
            [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2103:ReviewImperativeSecurity")] 
            internal void LoadStoreCollection(EdmItemCollection edmItemCollection, MetadataArtifactLoader loader)
            {
                StoreItemCollection storeItemCollection = null;
                IEnumerable sSpaceXmlReaders = loader.CreateReaders(DataSpace.SSpace); 
                try
                { 
                    // Load the store side, however, only do so if we don't already have one 
                    storeItemCollection = new StoreItemCollection(
                                    sSpaceXmlReaders, 
                                    loader.GetPaths(DataSpace.SSpace));

                }
                finally 
                {
                    Helper.DisposeXmlReaders(sSpaceXmlReaders); 
                } 

                // If this entry is getting re-used, make sure that the previous query cache manager gets 
                // cleared up
                if (_queryCacheManager != null)
                {
                    _queryCacheManager.Clear(); 
                }
 
                // Update the query cache manager reference 
                _queryCacheManager = storeItemCollection.QueryCacheManager;
 
                // With the store metadata in place, we can then load the mappings, however, only use it
                // if we don't already have one
                //
                StorageMappingItemCollection storageMappingItemCollection = null; 
                IEnumerable csSpaceXmlReaders = loader.CreateReaders(DataSpace.CSSpace);
                try 
                { 
                    storageMappingItemCollection = new StorageMappingItemCollection(
                                                                        edmItemCollection, 
                                                                        storeItemCollection,
                                                                        csSpaceXmlReaders,
                                                                        loader.GetPaths(DataSpace.CSSpace));
                } 
                finally
                { 
                    Helper.DisposeXmlReaders(csSpaceXmlReaders); 
                }
 
                List permissionPaths = new List();
                loader.CollectFilePermissionPaths(permissionPaths, DataSpace.SSpace);
                loader.CollectFilePermissionPaths(permissionPaths, DataSpace.CSSpace);
                FileIOPermission filePermissions = null; 
                if (permissionPaths.Count > 0)
                { 
                    filePermissions = new FileIOPermission(FileIOPermissionAccess.Read, permissionPaths.ToArray()); 
                }
                this.UpdateMetadataEntry(storageMappingItemCollection, filePermissions); 

            }

            ///  
            /// Calls clear on query cache manager to make sure all the performance counters associated with the query
            /// cache are gone 
            ///  
            internal override void Clear()
            { 
                // there can be entries in cache for which the store item collection was never created. For e.g.
                // if you create a new entity connection, but never call open on it
                CleanupQueryCache();
                base.Clear(); 
            }
 
            ///  
            /// Cleans and Dispose query cache manager
            ///  
            internal void CleanupQueryCache()
            {
                if (null != _queryCacheManager)
                { 
                    _queryCacheManager.Dispose();
                    _queryCacheManager = null; 
                } 
            }
 
        }

        /// 
        /// Interface to construct the metadata entry so that code can be reused 
        /// 
        ///  
        interface IMetadataEntryConstructor 
        {
            T GetMetadataEntry(); 
        }

        /// 
        /// Struct for creating EdmMetadataEntry 
        /// 
        private struct EdmMetadataEntryConstructor : IMetadataEntryConstructor 
        { 
            public EdmMetadataEntry GetMetadataEntry()
            { 
                return new EdmMetadataEntry();
            }
        }
 
        /// 
        /// Struct for creating StoreMetadataEntry 
        ///  
        private struct StoreMetadataEntryConstructor : IMetadataEntryConstructor
        { 
            public StoreMetadataEntry GetMetadataEntry()
            {
                return new StoreMetadataEntry();
            } 
        }
 
        ///  
        /// Interface which constructs a new Item collection
        ///  
        /// 
        interface IItemCollectionLoader where T : MetadataEntry
        {
            void LoadItemCollection(T entry); 
        }
 
        private struct EdmItemCollectionLoader : IItemCollectionLoader 
        {
 
            private MetadataArtifactLoader _loader;

            public EdmItemCollectionLoader(MetadataArtifactLoader loader)
            { 
                Debug.Assert(loader != null, "loader must never be null");
                _loader = loader; 
            } 

            ///  
            /// Creates a new item collection and updates the entry with the item collection
            /// 
            /// 
            ///  
            public void LoadItemCollection(EdmMetadataEntry entry)
            { 
                entry.LoadEdmItemCollection(_loader); 
            }
        } 

        private struct StoreItemCollectionLoader : IItemCollectionLoader
        {
            private EdmItemCollection _edmItemCollection; 
            private MetadataArtifactLoader _loader;
 
            ///  
            /// Constructs a struct from which you can load edm item collection
            ///  
            /// 
            /// 
            internal StoreItemCollectionLoader(EdmItemCollection edmItemCollection, MetadataArtifactLoader loader)
            { 
                Debug.Assert(edmItemCollection != null, "EdmItemCollection must never be null");
                Debug.Assert(loader != null, "loader must never be null"); 
                //StoreItemCollection requires atleast one SSDL path. 
                if ((loader.GetPaths(DataSpace.SSpace) == null) || (loader.GetPaths(DataSpace.SSpace).Count == 0))
                { 
                    throw EntityUtil.Metadata(Strings.AtleastOneSSDLNeeded);
                }

                _edmItemCollection = edmItemCollection; 
                _loader = loader;
            } 
 
            public void LoadItemCollection(StoreMetadataEntry entry)
            { 
                entry.LoadStoreCollection(_edmItemCollection, _loader);
            }
        }
 
        #endregion
    } 
} 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
//---------------------------------------------------------------------- 
// 
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// 
// 
// @owner  [....]
//--------------------------------------------------------------------- 
 
using System;
using System.Collections.Generic; 
using System.Collections.ObjectModel;
using System.Data.Common;
using System.Data.Mapping;
using System.Diagnostics; 
using System.Diagnostics.CodeAnalysis;
using System.Threading; 
using System.IO; 
using System.Xml;
using System.Security.Permissions; 
using System.Data.Entity;


namespace System.Data.Metadata.Edm 
{
    ///  
    /// Runtime Metadata Cache - this class contains the metadata cache entry for edm and store collections. 
    /// 
    internal static class MetadataCache 
    {
        #region Fields

        // This is the period in the periodic cleanup measured in milliseconds 
        private const int cleanupPeriod = 5 * 60 * 1000;
 
        // This dictionary contains the cache entry for the edm item collection. The reason why we need to keep a seperate dictionary 
        // for CSpace item collection is that the same model can be used for different providers. We don't want to load the model
        // again and again 
        private static readonly Dictionary _edmLevelCache = new Dictionary(StringComparer.OrdinalIgnoreCase);

        /// 
        /// This dictionary contains the store cache entry - this entry will only keep track of StorageMappingItemCollection, since internally 
        /// storage mapping item collection keeps strong references to both edm item collection and store item collection.
        ///  
        private static readonly Dictionary _storeLevelCache = new Dictionary(StringComparer.OrdinalIgnoreCase); 

        ///  
        /// The list maintains the store metadata entries that are still in use, maybe because someone is still holding a strong reference
        /// to it. We need to scan this list everytime the clean up thread wakes up and make sure if the item collection is no longer in use,
        /// call clear on query cache
        ///  
        private static readonly List _metadataEntriesRemovedFromCache = new List();
 
        ///  
        /// Read/Write lock for edm cache
        ///  
        private static readonly object _edmLevelLock = new object();

        /// 
        /// Read/Write lock for the store cache 
        /// 
        private static readonly object _storeLevelLock = new object(); 
 
        // Periodic thread which runs every n mins (look up the cleanupPeriod variable to see the exact time), walks through
        // every item in other store and edm cache and tries to do some cleanup 
        private static Timer timer = new Timer(PeriodicCleanupCallback, null, cleanupPeriod, cleanupPeriod);

        #endregion
 
        #region Methods
 
        ///  
        /// The purpose of the thread is to do cleanup. It marks the object in various stages before it actually cleans up the object
        /// Here's what this does for each entry in the cache: 
        ///     1> First checks if the entry is marked for cleanup.
        ///     2> If the entry is marked for cleanup, that means its in one of the following 3 states
        ///         a) If the strong reference to item collection is not null, it means that this item was marked for cleanup in
        ///            the last cleanup cycle and we must make the strong reference set to null so that it can be garbage collected. 
        ///         b) Otherwise, we are waiting for GC to collect the item collection so that we can remove this entry from the cache
        ///            If the weak reference to item collection is still alive, we don't do anything 
        ///         c) If the weak reference to item collection is not alive, we need to remove this entry from the cache 
        ///     3> If the entry is not marked for cleanup, then check whether the weak reference to entry token is alive
        ///         a) if it is alive, then this entry is in use and we must do nothing 
        ///         b) Otherwise, we can mark this entry for cleanup
        /// 
        /// 
        private static void PeriodicCleanupCallback(object state) 
        {
            // Perform clean up on edm cache 
            DoCacheClean(_edmLevelCache, _edmLevelLock); 

            // Perform clean up on store cache 
            DoCacheClean(_storeLevelCache, _storeLevelLock);
        }

        ///  
        /// Walks through the given cache and calls cleanup on each entry in the cache
        ///  
        ///  
        /// 
        ///  
        private static void DoCacheClean(Dictionary cache, object objectToLock) where T: MetadataEntry
        {
            // Sometime, for some reason, timer can be initialized and the cache is still not initialized.
            if (cache != null) 
            {
                List> keysForRemoval = null; 
 
                lock (objectToLock)
                { 
                    // we should check for type of the lock object first, since otherwise we might be reading the count of the list
                    // while some other thread might be modifying it. For e.g. when this function is called for edmcache,
                    // we will be acquiring edmlock and trying to get the count for the list, while some other thread
                    // might be calling ClearCache and we might be adding entries to the list 
                    if (objectToLock == _storeLevelLock && _metadataEntriesRemovedFromCache.Count != 0)
                    { 
                        // First check the list of entries and remove things which are no longer in use 
                        for (int i = _metadataEntriesRemovedFromCache.Count - 1; 0 <= i; i--)
                        { 
                            if (!_metadataEntriesRemovedFromCache[i].IsEntryStillValid())
                            {
                                // Clear the query cache
                                _metadataEntriesRemovedFromCache[i].CleanupQueryCache(); 
                                // Remove the entry at the current index. This is the reason why we
                                // go backwards. 
                                _metadataEntriesRemovedFromCache.RemoveAt(i); 
                            }
                        } 
                    }

                    // We have to use a list to keep track of the keys to remove because we can't remove while enumerating
                    foreach (KeyValuePair pair in cache) 
                    {
                        if (pair.Value.PeriodicCleanUpThread()) 
                        { 
                            if (keysForRemoval == null)
                            { 
                                keysForRemoval = new List>();
                            }
                            keysForRemoval.Add(pair);
                        } 
                    }
 
                    // Remove all the entries from the cache 
                    if (keysForRemoval != null)
                    { 
                        for (int i = 0; i < keysForRemoval.Count; i++)
                        {
                            keysForRemoval[i].Value.Clear();
                            cache.Remove(keysForRemoval[i].Key); 
                        }
                    } 
                } 
            }
        } 

        /// 
        /// Retrieves an cache entry holding to edm metadata for a given cache key
        ///  
        /// string containing all the files from which edm metadata is to be retrieved
        /// An instance of the composite MetadataArtifactLoader 
        /// The metadata entry token for the returned entry 
        /// Returns the entry containing the edm metadata
        internal static EdmItemCollection GetOrCreateEdmItemCollection(string cacheKey, 
                                                             MetadataArtifactLoader loader,
                                                             out object entryToken)
        {
            EdmMetadataEntry entry = GetCacheEntry(_edmLevelCache, cacheKey, _edmLevelLock, 
                new EdmMetadataEntryConstructor(), out entryToken);
 
            // Load the edm item collection or if the collection is already loaded, check for security permission 
            LoadItemCollection(new EdmItemCollectionLoader(loader), entry);
 
            return entry.EdmItemCollection;
        }

        ///  
        /// Retrieves an entry holding store metadata for a given cache key
        ///  
        /// The connection string whose store metadata is to be retrieved 
        /// An instance of the composite MetadataArtifactLoader
        /// The metadata entry token for the returned entry 
        /// the entry containing the information on how to load store metadata
        internal static StorageMappingItemCollection GetOrCreateStoreAndMappingItemCollections(
                                                                 string cacheKey,
                                                                 MetadataArtifactLoader loader, 
                                                                 EdmItemCollection edmItemCollection,
                                                                 out object entryToken) 
        { 
            StoreMetadataEntry entry = GetCacheEntry(_storeLevelCache, cacheKey, _storeLevelLock,
                new StoreMetadataEntryConstructor(), out entryToken); 

            // Load the store item collection or if the collection is already loaded, check for security permission
            LoadItemCollection(new StoreItemCollectionLoader(edmItemCollection, loader), entry);
 
            return entry.StorageMappingItemCollection;
        } 
 
        /// 
        /// Get the entry from the cache given the cache key. If the entry is not present, it creates a new entry and 
        /// adds it to the cache
        /// 
        /// 
        ///  
        /// 
        ///  
        ///  
        /// 
        ///  
        private static T GetCacheEntry(Dictionary cache, string cacheKey, object objectToLock,
            IMetadataEntryConstructor metadataEntry, out object entryToken) where T: MetadataEntry
        {
            T entry; 

            // In the critical section, we need to do the minimal thing to ensure correctness 
            // Within the lock, we will see if an entry is present. If it is not, we will create a new entry and 
            // add it to the cache. In either case, we need to ensure the token to make sure so that any other
            // thread that comes looking for the same entry does nothing in this critical section 
            // Also the cleanup thread doesn't do anything since the token is alive
            lock (objectToLock)
            {
                if (cache.TryGetValue(cacheKey, out entry)) 
                {
                    entryToken = entry.EnsureToken(); 
                } 
                else
                { 
                    entry = metadataEntry.GetMetadataEntry();
                    entryToken = entry.EnsureToken();
                    cache.Add(cacheKey, entry);
                } 
            }
 
            return entry; 
        }
 
        /// 
        /// Loads the item collection for the entry
        /// 
        /// struct which loads an item collection 
        /// entry whose item collection needs to be loaded
        private static void LoadItemCollection(IItemCollectionLoader itemCollectionLoader, T entry) where T : MetadataEntry 
        { 
            // At this point, you have made sure that there is an entry with an alive token in the cache so that
            // other threads can find it if they come querying for it, and cleanup thread won't clean the entry 
            // If two or more threads come one after the other, we don't won't both of them to load the metadata.
            // So if one of them is loading the metadata, the other should wait and then use the same metadata.
            // For that reason, we have this lock on the entry itself to make sure that this happens. Its okay to
            // update the item collection outside the lock, since assignment are guarantees to be atomic and no two 
            // thread are updating this at the same time
            bool isItemCollectionAlreadyLoaded = true; 
 
            if (!entry.IsLoaded)
            { 
                lock (entry)
                {
                    if (!entry.IsLoaded)
                    { 
                        itemCollectionLoader.LoadItemCollection(entry);
                        isItemCollectionAlreadyLoaded = false; 
                    } 
                }
            } 

            Debug.Assert(entry.IsLoaded, "The entry must be loaded at this point");

            // Making sure that the thread which loaded the item collection is not checking for file permisssions 
            // again
            if (isItemCollectionAlreadyLoaded) 
            { 
                entry.CheckFilePermission();
            } 
        }

        /// 
        /// Remove all the entries from the cache 
        /// 
        internal static void Clear() 
        { 
            lock (_edmLevelLock)
            { 
                _edmLevelCache.Clear();
            }

            lock (_storeLevelLock) 
            {
                // Call clear on each of the metadata entries. This is to make sure we clear all the performance 
                // counters associated with the query cache 
                foreach (StoreMetadataEntry entry in _storeLevelCache.Values)
                { 
                    // Check if the weak reference to item collection is still alive
                    if (entry.IsEntryStillValid())
                    {
                        _metadataEntriesRemovedFromCache.Add(entry); 
                    }
                    else 
                    { 
                        entry.Clear();
                    } 
                }
                _storeLevelCache.Clear();
            }
        } 

        #endregion 
 
        #region InlineClasses
 
        /// 
        /// The base class having common implementation for all metadata entry classes
        /// 
        private abstract class MetadataEntry 
        {
            private WeakReference _entryTokenReference; 
            private ItemCollection _itemCollection; 
            private WeakReference _weakReferenceItemCollection;
            private bool _markEntryForCleanup; 
            private FileIOPermission _filePermissions;

            /// 
            /// The constructor for constructing this MetadataEntry 
            /// 
            internal MetadataEntry() 
            { 
                // Create this once per life time of the object. Creating extra weak references causing unnecessary GC pressure
                _entryTokenReference = new WeakReference(null); 
                _weakReferenceItemCollection = new WeakReference(null);
            }

            ///  
            /// returns the item collection inside this metadata entry
            ///  
            protected ItemCollection ItemCollection { get { return _itemCollection; } } 

            ///  
            /// Update the entry with the given item collection
            /// 
            /// 
            protected void UpdateMetadataEntry(ItemCollection itemCollection, FileIOPermission filePermissions) 
            {
                Debug.Assert(_entryTokenReference.IsAlive, "You must call Ensure token before you call this method"); 
                Debug.Assert(_markEntryForCleanup == false, "The entry must not be marked for cleanup"); 
                Debug.Assert(_itemCollection == null, "Item collection must be null");
                Debug.Assert(_filePermissions == null, "filePermissions must be null"); 

                // Update strong and weak reference for item collection
                _weakReferenceItemCollection.Target = itemCollection;
                _filePermissions = filePermissions; 

                // do this last, because it signals that we are loaded 
                _itemCollection = itemCollection; 
            }
 
            internal bool IsLoaded { get { return _itemCollection != null; } }

            /// 
            /// This method is called periodically by the cleanup thread to make the unused entries 
            /// go through various stages, before it is ready for cleanup. If it is ready, this method
            /// returns true and then the entry is completely removed from the cache 
            ///  
            /// 
            internal bool PeriodicCleanUpThread() 
            {
                // Here's what this does for each entry in the cache:
                //     1> First checks if the entry is marked for cleanup.
                //     2> If the entry is marked for cleanup, that means its in one of the following 3 states 
                //         a) If the strong reference to item collection is not null, it means that this item was marked for cleanup in
                //            the last cleanup cycle and we must make the strong reference set to null so that it can be garbage collected. (GEN 2) 
                //         b) Otherwise, we are waiting for GC to collect the item collection so that we can remove this entry from the cache 
                //            If the weak reference to item collection is still alive, we don't do anything
                //         c) If the weak reference to item collection is not alive, we need to remove this entry from the cache (GEN 3) 
                //     3> If the entry is not marked for cleanup, then check whether the weak reference to entry token is alive
                //         a) if it is alive, then this entry is in use and we must do nothing
                //         b) Otherwise, we can mark this entry for cleanup (GEN 1)
                if (_markEntryForCleanup) 
                {
                    Debug.Assert(_entryTokenReference.IsAlive == false, "Entry Token must never be alive if the entry is marked for cleanup"); 
 
                    if (_itemCollection != null)
                    { 
                        // GEN 2
                        _itemCollection = null;
                    }
                    else if (!_weakReferenceItemCollection.IsAlive) 
                    {
                        // GEN 3 
                        _filePermissions = null; 
                        // this entry must be removed from the cache
                        return true; 
                    }
                }
                else if (!_entryTokenReference.IsAlive)
                { 
                    // GEN 1
 
                    // If someone creates a entity connection, and calls GetMetadataWorkspace. This creates an cache entry, 
                    // but the item collection is not initialized yet (since store item collection are initialized only
                    // when one calls connection.Open()). Suppose now the connection is no longer used - in other words, 
                    // open was never called and it goes out of scope. After some time when the connection gets GC'ed,
                    // entry token won't be alive any longer, but item collection inside it will be null, since it was never initialized.
                    // So we can't assert that item collection must be always initialized here
                    _markEntryForCleanup = true; 
                }
 
                return false; 
            }
 
            /// 
            /// Make sure that the entry has a alive token and returns that token - it can be new token or an existing
            /// one, depending on the state of the entry
            ///  
            /// 
            internal object EnsureToken() 
            { 
                object entryToken = _entryTokenReference.Target;
                ItemCollection itemCollection = (ItemCollection)_weakReferenceItemCollection.Target; 

                // When ensure token is called, the entry can be in different stages
                // 1> Its a newly created entry - no token, no item collection, etc. Just create a new token and
                //    return back 
                // 2> An entry already in use - the weak reference to token must be alive. We just need to grab the token
                //    and return it 
                // 3> No one is using this entry and hence the token is no longer alive. If we have strong reference to item 
                //    collection, then create a new token and return it
                // 4> No one has used this token for one cleanup cycle and hence strong reference is null. But the weak reference 
                //    is still alive. We need to make the initialize the strong reference again, create a new token and return it
                // 5> This entry has not been used for long enough that even the weak reference is no longer alive. This entry is
                //    now exactly like a new entry, except that it is still marked for cleanup. Create a new token, set mark for
                //    cleanup to false and return the token 
                if (_entryTokenReference.IsAlive)
                { 
                    Debug.Assert(_markEntryForCleanup == false, "An entry with alive token cannot be marked for cleanup"); 
                    // ItemCollection strong pointer can be null or not null. If the entry has been created, and loadItemCollection
                    // hasn't been called yet, the token will be alive, but item collection will be null. If someone called 
                    // load item collection, then item collection will not be non-null
                    return entryToken;
                }
                // If the entry token is not alive, then it can be either a new created entry with everything set 
                // to null or it must be one of the entries which is no longer in use
                else if (_itemCollection != null) 
                { 
                    Debug.Assert(_weakReferenceItemCollection.IsAlive, "Since the strong reference is still there, weak reference must also be alive");
                    // This means that no one is using the item collection, and its waiting to be cleanuped 
                }
                else
                {
                    if (_weakReferenceItemCollection.IsAlive) 
                    {
                        Debug.Assert(_markEntryForCleanup, "Since the strong reference is null, this entry must be marked for cleanup"); 
                        // Initialize the strong reference to item collection 
                        _itemCollection = itemCollection;
                    } 
                    else
                    {
                        // no more references to the collection
                        // are available, so get rid of the permissions 
                        // object.  We will get a new one when we get a new collection
                        _filePermissions = null; 
                    } 
                }
                // Even if the _weakReferenceItemCollection is no longer alive, we will reuse this entry. Assign a new entry token and set mark for cleanup to false 
                // so that this entry is not cleared by the cleanup thread

                entryToken = new object();
                _entryTokenReference.Target = entryToken; 
                _markEntryForCleanup = false;
                return entryToken; 
            } 

            ///  
            /// Check if the thread has appropriate permissions to use the already loaded metadata
            /// 
            internal void CheckFilePermission()
            { 
                Debug.Assert(_itemCollection != null, "Item collection must be present since we want to reuse the metadata");
                Debug.Assert(_entryTokenReference.IsAlive, "This entry must be in use"); 
                Debug.Assert(_markEntryForCleanup == false, "The entry must not marked for cleanup"); 
                Debug.Assert(_weakReferenceItemCollection.IsAlive, "Weak reference to item collection must be alive");
 
                // we will have an empty ItemCollection (no files were used to load it)
                if (_filePermissions != null)
                {
                    _filePermissions.Demand(); 
                }
            } 
 
            /// 
            /// Dispose the composite loader that encapsulates all artifacts 
            /// 
            internal virtual void Clear()
            {
            } 

            ///  
            /// This returns true if the entry is still in use - the entry can be use if the entry token is 
            /// still alive.If the entry token is still not alive, it means that no one is using this entry
            /// and its okay to remove it. Today there is no 
            /// 
            /// 
            internal bool IsEntryStillValid()
            { 
                return _entryTokenReference.IsAlive;
            } 
        } 

        ///  
        /// A metadata entry holding EdmItemCollection object for the cache
        /// 
        private class EdmMetadataEntry : MetadataEntry
        { 
            /// 
            /// Gets the EdmItemCollection for this entry 
            ///  
            internal EdmItemCollection EdmItemCollection
            { 
                get
                {
                    return (EdmItemCollection)this.ItemCollection;
                } 
            }
 
            ///  
            /// Just loads the edm item collection
            ///  
            /// 
            [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2103:ReviewImperativeSecurity")]
            internal void LoadEdmItemCollection(MetadataArtifactLoader loader)
            { 
                Debug.Assert(loader != null, "loader is null");
 
                List readers = loader.CreateReaders(DataSpace.CSpace); 
                try
                { 
                    EdmItemCollection itemCollection = new EdmItemCollection(
                                                           readers,
                                                           loader.GetPaths(DataSpace.CSpace)
                                                            ); 

                    List permissionPaths = new List(); 
                    loader.CollectFilePermissionPaths(permissionPaths, DataSpace.CSpace); 
                    FileIOPermission filePermissions = null;
                    if (permissionPaths.Count > 0) 
                    {
                        filePermissions = new FileIOPermission(FileIOPermissionAccess.Read, permissionPaths.ToArray());
                    }
 
                    UpdateMetadataEntry(itemCollection, filePermissions);
                } 
                finally 
                {
                    Helper.DisposeXmlReaders(readers); 
                }
            }
        }
 
        /// 
        /// A metadata entry holding a StoreItemCollection and a StorageMappingItemCollection objects for the cache 
        ///  
        private class StoreMetadataEntry : MetadataEntry
        { 
            private System.Data.Common.QueryCache.QueryCacheManager _queryCacheManager;

            /// 
            /// The constructor for constructing this entry with an StoreItemCollection and a StorageMappingItemCollection 
            /// 
            /// An instance of the composite MetadataArtifactLoader 
            internal StoreMetadataEntry() 
            {
            } 

            /// 
            /// Gets the StorageMappingItemCollection for this entry
            ///  
            internal StorageMappingItemCollection StorageMappingItemCollection
            { 
                get 
                {
                    return (StorageMappingItemCollection)this.ItemCollection; 
                }
            }

            ///  
            /// Load store specific metadata into the StoreItemCollection for this entry
            ///  
            /// The store-specific provider factory 
            /// edmItemCollection
            [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2103:ReviewImperativeSecurity")] 
            internal void LoadStoreCollection(EdmItemCollection edmItemCollection, MetadataArtifactLoader loader)
            {
                StoreItemCollection storeItemCollection = null;
                IEnumerable sSpaceXmlReaders = loader.CreateReaders(DataSpace.SSpace); 
                try
                { 
                    // Load the store side, however, only do so if we don't already have one 
                    storeItemCollection = new StoreItemCollection(
                                    sSpaceXmlReaders, 
                                    loader.GetPaths(DataSpace.SSpace));

                }
                finally 
                {
                    Helper.DisposeXmlReaders(sSpaceXmlReaders); 
                } 

                // If this entry is getting re-used, make sure that the previous query cache manager gets 
                // cleared up
                if (_queryCacheManager != null)
                {
                    _queryCacheManager.Clear(); 
                }
 
                // Update the query cache manager reference 
                _queryCacheManager = storeItemCollection.QueryCacheManager;
 
                // With the store metadata in place, we can then load the mappings, however, only use it
                // if we don't already have one
                //
                StorageMappingItemCollection storageMappingItemCollection = null; 
                IEnumerable csSpaceXmlReaders = loader.CreateReaders(DataSpace.CSSpace);
                try 
                { 
                    storageMappingItemCollection = new StorageMappingItemCollection(
                                                                        edmItemCollection, 
                                                                        storeItemCollection,
                                                                        csSpaceXmlReaders,
                                                                        loader.GetPaths(DataSpace.CSSpace));
                } 
                finally
                { 
                    Helper.DisposeXmlReaders(csSpaceXmlReaders); 
                }
 
                List permissionPaths = new List();
                loader.CollectFilePermissionPaths(permissionPaths, DataSpace.SSpace);
                loader.CollectFilePermissionPaths(permissionPaths, DataSpace.CSSpace);
                FileIOPermission filePermissions = null; 
                if (permissionPaths.Count > 0)
                { 
                    filePermissions = new FileIOPermission(FileIOPermissionAccess.Read, permissionPaths.ToArray()); 
                }
                this.UpdateMetadataEntry(storageMappingItemCollection, filePermissions); 

            }

            ///  
            /// Calls clear on query cache manager to make sure all the performance counters associated with the query
            /// cache are gone 
            ///  
            internal override void Clear()
            { 
                // there can be entries in cache for which the store item collection was never created. For e.g.
                // if you create a new entity connection, but never call open on it
                CleanupQueryCache();
                base.Clear(); 
            }
 
            ///  
            /// Cleans and Dispose query cache manager
            ///  
            internal void CleanupQueryCache()
            {
                if (null != _queryCacheManager)
                { 
                    _queryCacheManager.Dispose();
                    _queryCacheManager = null; 
                } 
            }
 
        }

        /// 
        /// Interface to construct the metadata entry so that code can be reused 
        /// 
        ///  
        interface IMetadataEntryConstructor 
        {
            T GetMetadataEntry(); 
        }

        /// 
        /// Struct for creating EdmMetadataEntry 
        /// 
        private struct EdmMetadataEntryConstructor : IMetadataEntryConstructor 
        { 
            public EdmMetadataEntry GetMetadataEntry()
            { 
                return new EdmMetadataEntry();
            }
        }
 
        /// 
        /// Struct for creating StoreMetadataEntry 
        ///  
        private struct StoreMetadataEntryConstructor : IMetadataEntryConstructor
        { 
            public StoreMetadataEntry GetMetadataEntry()
            {
                return new StoreMetadataEntry();
            } 
        }
 
        ///  
        /// Interface which constructs a new Item collection
        ///  
        /// 
        interface IItemCollectionLoader where T : MetadataEntry
        {
            void LoadItemCollection(T entry); 
        }
 
        private struct EdmItemCollectionLoader : IItemCollectionLoader 
        {
 
            private MetadataArtifactLoader _loader;

            public EdmItemCollectionLoader(MetadataArtifactLoader loader)
            { 
                Debug.Assert(loader != null, "loader must never be null");
                _loader = loader; 
            } 

            ///  
            /// Creates a new item collection and updates the entry with the item collection
            /// 
            /// 
            ///  
            public void LoadItemCollection(EdmMetadataEntry entry)
            { 
                entry.LoadEdmItemCollection(_loader); 
            }
        } 

        private struct StoreItemCollectionLoader : IItemCollectionLoader
        {
            private EdmItemCollection _edmItemCollection; 
            private MetadataArtifactLoader _loader;
 
            ///  
            /// Constructs a struct from which you can load edm item collection
            ///  
            /// 
            /// 
            internal StoreItemCollectionLoader(EdmItemCollection edmItemCollection, MetadataArtifactLoader loader)
            { 
                Debug.Assert(edmItemCollection != null, "EdmItemCollection must never be null");
                Debug.Assert(loader != null, "loader must never be null"); 
                //StoreItemCollection requires atleast one SSDL path. 
                if ((loader.GetPaths(DataSpace.SSpace) == null) || (loader.GetPaths(DataSpace.SSpace).Count == 0))
                { 
                    throw EntityUtil.Metadata(Strings.AtleastOneSSDLNeeded);
                }

                _edmItemCollection = edmItemCollection; 
                _loader = loader;
            } 
 
            public void LoadItemCollection(StoreMetadataEntry entry)
            { 
                entry.LoadStoreCollection(_edmItemCollection, _loader);
            }
        }
 
        #endregion
    } 
} 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.

                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK