MetadataCache.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ 4.0 / 4.0 / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / fx / src / DataEntity / System / Data / Metadata / MetadataCache.cs / 1305376 / MetadataCache.cs

                            //---------------------------------------------------------------------- 
// 
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// 
// 
// @owner       [....]
// @backupOwner [....] 
//--------------------------------------------------------------------- 

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;
using System.Data.Common.Utils;
using System.Data.EntityClient; 
using System.Runtime.Versioning;
 
 
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 
 
        private const string s_dataDirectory = "|datadirectory|";
        private const string s_metadataPathSeparator = "|"; 

        // 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();
 
        private static Memoizer> _artifactLoaderCache = new Memoizer>(MetadataCache.SplitPaths, null);
 
        ///  
        /// 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);
        }

        ///  
        /// A helper function for splitting up a string that is a concatenation of strings delimited by the metadata
        /// path separator into a string list. The resulting list is NOT sorted. 
        ///  
        /// The paths to split
        /// An array of strings 
        [ResourceExposure(ResourceScope.Machine)] //Exposes the file name which is a Machine resource
        [ResourceConsumption(ResourceScope.Machine)] //For MetadataArtifactLoader.Create method call. But the path is not created in this method.
        internal static List SplitPaths(string paths)
        { 
            Debug.Assert(!string.IsNullOrEmpty(paths), "paths cannot be empty or null");
 
            string[] results; 

            // This is the registry of all URIs in the global collection. 
            HashSet uriRegistry = new HashSet(StringComparer.OrdinalIgnoreCase);

            List loaders = new List();
 
            // If the argument contains one or more occurrences of the macro '|DataDirectory|', we
            // pull those paths out so that we don't lose them in the string-splitting logic below. 
            // Note that the macro '|DataDirectory|' cannot have any whitespace between the pipe 
            // symbols and the macro name. Also note that the macro must appear at the beginning of
            // a path (else we will eventually fail with an invalid path exception, because in that 
            // case the macro is not expanded). If a real/physical folder named 'DataDirectory' needs
            // to be included in the metadata path, whitespace should be used on either or both sides
            // of the name.
            // 
            List dataDirPaths = new List();
 
            int indexStart = paths.IndexOf(MetadataCache.s_dataDirectory, StringComparison.OrdinalIgnoreCase); 
            while (indexStart != -1)
            { 
                int prevSeparatorIndex = indexStart == 0 ? -1 : paths.LastIndexOf(
                                                                MetadataCache.s_metadataPathSeparator,
                                                                indexStart - 1, // start looking here
                                                                StringComparison.Ordinal 
                                                            );
 
                int macroPathBeginIndex = prevSeparatorIndex + 1; 

                // The '|DataDirectory|' macro is composable, so identify the complete path, like 
                // '|DataDirectory|\foo\bar'. If the macro appears anywhere other than at the
                // beginning, splice out the entire path, e.g. 'C:\foo\|DataDirectory|\bar'. In this
                // latter case the macro will not be expanded, and downstream code will throw an exception.
                // 
                int indexEnd = paths.IndexOf(MetadataCache.s_metadataPathSeparator,
                                             indexStart + MetadataCache.s_dataDirectory.Length, 
                                             StringComparison.Ordinal); 
                if (indexEnd == -1)
                { 
                    dataDirPaths.Add(paths.Substring(macroPathBeginIndex));
                    paths = paths.Remove(macroPathBeginIndex);   // update the concatenated list of paths
                    break;
                } 

                dataDirPaths.Add(paths.Substring(macroPathBeginIndex, indexEnd - macroPathBeginIndex)); 
 
                // Update the concatenated list of paths by removing the one containing the macro.
                // 
                paths = paths.Remove(macroPathBeginIndex, indexEnd - macroPathBeginIndex);
                indexStart = paths.IndexOf(MetadataCache.s_dataDirectory, StringComparison.OrdinalIgnoreCase);
            }
 
            // Split the string on the separator and remove all spaces around each parameter value
            results = paths.Split(new string[] { MetadataCache.s_metadataPathSeparator }, StringSplitOptions.RemoveEmptyEntries); 
 
            // Now that the non-macro paths have been identified, merge the paths containing the macro
            // into the complete list. 
            //
            if (dataDirPaths.Count > 0)
            {
                dataDirPaths.AddRange(results); 
                results = dataDirPaths.ToArray();
            } 
 
            for (int i = 0; i < results.Length; i++)
            { 
                // Trim out all the spaces for this parameter and add it only if it's not blank
                results[i] = results[i].Trim();
                if (results[i].Length > 0)
                { 
                    loaders.Add(MetadataArtifactLoader.Create(
                                    results[i], 
                                    MetadataArtifactLoader.ExtensionCheck.All,  // validate the extension against all acceptable values 
                                    null,
                                    uriRegistry 
                                ));
                }
            }
 
            return loaders;
        } 
 

        ///  
        /// 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;
        }

        internal static List GetOrCreateMetdataArtifactLoader(string paths) 
        {
            return _artifactLoaderCache.Evaluate(paths); 
        } 

        ///  
        /// 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();
            } 

            Memoizer> artifactLoaderCacheTemp = 
                new Memoizer>(MetadataCache.SplitPaths, null); 

            Interlocked.CompareExchange(ref _artifactLoaderCache, artifactLoaderCacheTemp, _artifactLoaderCache); 
        }

        #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       [....]
// @backupOwner [....] 
//--------------------------------------------------------------------- 

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;
using System.Data.Common.Utils;
using System.Data.EntityClient; 
using System.Runtime.Versioning;
 
 
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 
 
        private const string s_dataDirectory = "|datadirectory|";
        private const string s_metadataPathSeparator = "|"; 

        // 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();
 
        private static Memoizer> _artifactLoaderCache = new Memoizer>(MetadataCache.SplitPaths, null);
 
        ///  
        /// 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);
        }

        ///  
        /// A helper function for splitting up a string that is a concatenation of strings delimited by the metadata
        /// path separator into a string list. The resulting list is NOT sorted. 
        ///  
        /// The paths to split
        /// An array of strings 
        [ResourceExposure(ResourceScope.Machine)] //Exposes the file name which is a Machine resource
        [ResourceConsumption(ResourceScope.Machine)] //For MetadataArtifactLoader.Create method call. But the path is not created in this method.
        internal static List SplitPaths(string paths)
        { 
            Debug.Assert(!string.IsNullOrEmpty(paths), "paths cannot be empty or null");
 
            string[] results; 

            // This is the registry of all URIs in the global collection. 
            HashSet uriRegistry = new HashSet(StringComparer.OrdinalIgnoreCase);

            List loaders = new List();
 
            // If the argument contains one or more occurrences of the macro '|DataDirectory|', we
            // pull those paths out so that we don't lose them in the string-splitting logic below. 
            // Note that the macro '|DataDirectory|' cannot have any whitespace between the pipe 
            // symbols and the macro name. Also note that the macro must appear at the beginning of
            // a path (else we will eventually fail with an invalid path exception, because in that 
            // case the macro is not expanded). If a real/physical folder named 'DataDirectory' needs
            // to be included in the metadata path, whitespace should be used on either or both sides
            // of the name.
            // 
            List dataDirPaths = new List();
 
            int indexStart = paths.IndexOf(MetadataCache.s_dataDirectory, StringComparison.OrdinalIgnoreCase); 
            while (indexStart != -1)
            { 
                int prevSeparatorIndex = indexStart == 0 ? -1 : paths.LastIndexOf(
                                                                MetadataCache.s_metadataPathSeparator,
                                                                indexStart - 1, // start looking here
                                                                StringComparison.Ordinal 
                                                            );
 
                int macroPathBeginIndex = prevSeparatorIndex + 1; 

                // The '|DataDirectory|' macro is composable, so identify the complete path, like 
                // '|DataDirectory|\foo\bar'. If the macro appears anywhere other than at the
                // beginning, splice out the entire path, e.g. 'C:\foo\|DataDirectory|\bar'. In this
                // latter case the macro will not be expanded, and downstream code will throw an exception.
                // 
                int indexEnd = paths.IndexOf(MetadataCache.s_metadataPathSeparator,
                                             indexStart + MetadataCache.s_dataDirectory.Length, 
                                             StringComparison.Ordinal); 
                if (indexEnd == -1)
                { 
                    dataDirPaths.Add(paths.Substring(macroPathBeginIndex));
                    paths = paths.Remove(macroPathBeginIndex);   // update the concatenated list of paths
                    break;
                } 

                dataDirPaths.Add(paths.Substring(macroPathBeginIndex, indexEnd - macroPathBeginIndex)); 
 
                // Update the concatenated list of paths by removing the one containing the macro.
                // 
                paths = paths.Remove(macroPathBeginIndex, indexEnd - macroPathBeginIndex);
                indexStart = paths.IndexOf(MetadataCache.s_dataDirectory, StringComparison.OrdinalIgnoreCase);
            }
 
            // Split the string on the separator and remove all spaces around each parameter value
            results = paths.Split(new string[] { MetadataCache.s_metadataPathSeparator }, StringSplitOptions.RemoveEmptyEntries); 
 
            // Now that the non-macro paths have been identified, merge the paths containing the macro
            // into the complete list. 
            //
            if (dataDirPaths.Count > 0)
            {
                dataDirPaths.AddRange(results); 
                results = dataDirPaths.ToArray();
            } 
 
            for (int i = 0; i < results.Length; i++)
            { 
                // Trim out all the spaces for this parameter and add it only if it's not blank
                results[i] = results[i].Trim();
                if (results[i].Length > 0)
                { 
                    loaders.Add(MetadataArtifactLoader.Create(
                                    results[i], 
                                    MetadataArtifactLoader.ExtensionCheck.All,  // validate the extension against all acceptable values 
                                    null,
                                    uriRegistry 
                                ));
                }
            }
 
            return loaders;
        } 
 

        ///  
        /// 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;
        }

        internal static List GetOrCreateMetdataArtifactLoader(string paths) 
        {
            return _artifactLoaderCache.Evaluate(paths); 
        } 

        ///  
        /// 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();
            } 

            Memoizer> artifactLoaderCacheTemp = 
                new Memoizer>(MetadataCache.SplitPaths, null); 

            Interlocked.CompareExchange(ref _artifactLoaderCache, artifactLoaderCacheTemp, _artifactLoaderCache); 
        }

        #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