1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164
|
//---------------------------------------------------------------------
// <copyright file="AssemblyCache.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
//
// @owner Microsoft
// @backupOwner Microsoft
//---------------------------------------------------------------------
using System.Collections.Generic;
using System.Data.Common.Utils;
using System.Diagnostics;
using System.Reflection;
namespace System.Data.Metadata.Edm
{
internal static class AssemblyCache
{
// Global Assembly Cache
private readonly static Dictionary<Assembly, ImmutableAssemblyCacheEntry> s_globalAssemblyCache = new Dictionary<Assembly, ImmutableAssemblyCacheEntry>();
private static object _assemblyCacheLock = new object();
//List of assemblies having view gen attribute. We cache these things if we discover
//these assemblies while looking for O-space metadata.
private static IList<Assembly> s_viewGenAssemblies = new ThreadSafeList<Assembly>();
internal static LockedAssemblyCache AquireLockedAssemblyCache()
{
return new LockedAssemblyCache(_assemblyCacheLock, s_globalAssemblyCache);
}
internal static void LoadAssembly(Assembly assembly, bool loadReferencedAssemblies,
KnownAssembliesSet knownAssemblies, out Dictionary<string, EdmType> typesInLoading, out List<EdmItemError> errors)
{
object loaderCookie = null;
LoadAssembly(assembly, loadReferencedAssemblies, knownAssemblies, null, null, ref loaderCookie, out typesInLoading, out errors);
}
internal static void LoadAssembly(Assembly assembly, bool loadReferencedAssemblies,
KnownAssembliesSet knownAssemblies, EdmItemCollection edmItemCollection, Action<String> logLoadMessage, ref object loaderCookie, out Dictionary<string, EdmType> typesInLoading, out List<EdmItemError> errors)
{
Debug.Assert(loaderCookie == null || loaderCookie is Func<Assembly, ObjectItemLoadingSessionData, ObjectItemAssemblyLoader>, "This is a bad loader cookie");
typesInLoading = null;
errors = null;
using (LockedAssemblyCache lockedAssemblyCache = AssemblyCache.AquireLockedAssemblyCache())
{
ObjectItemLoadingSessionData loadingData = new ObjectItemLoadingSessionData(knownAssemblies, lockedAssemblyCache, edmItemCollection, logLoadMessage, loaderCookie);
LoadAssembly(assembly, loadReferencedAssemblies, loadingData);
loaderCookie = loadingData.LoaderCookie;
// resolve references to top level types (base types, navigation properties returns and associations, and complex type properties)
loadingData.CompleteSession();
if (loadingData.EdmItemErrors.Count == 0)
{
// do the validation for the all the new types
// Now, perform validation on all the new types
EdmValidator validator = new EdmValidator();
validator.SkipReadOnlyItems = true;
validator.Validate(loadingData.TypesInLoading.Values, loadingData.EdmItemErrors);
// Update the global cache if there are no errors
if (loadingData.EdmItemErrors.Count == 0)
{
if (ObjectItemAssemblyLoader.IsAttributeLoader(loadingData.ObjectItemAssemblyLoaderFactory))
{
// we only cache items from the attribute loader globally, the
// items loaded by convention will change depending on the cspace
// provided. cspace will have a cache of it's own for assemblies
UpdateCache(lockedAssemblyCache, loadingData.AssembliesLoaded);
}
else if (loadingData.EdmItemCollection != null &&
ObjectItemAssemblyLoader.IsConventionLoader(loadingData.ObjectItemAssemblyLoaderFactory))
{
UpdateCache(loadingData.EdmItemCollection, loadingData.AssembliesLoaded);
}
}
}
if (loadingData.TypesInLoading.Count > 0)
{
foreach (EdmType edmType in loadingData.TypesInLoading.Values)
{
edmType.SetReadOnly();
}
}
// Update the out parameters once you are done with loading
typesInLoading = loadingData.TypesInLoading;
errors = loadingData.EdmItemErrors;
}
}
private static void LoadAssembly(Assembly assembly, bool loadReferencedAssemblies, ObjectItemLoadingSessionData loadingData)
{
// Check if the assembly is already loaded
KnownAssemblyEntry entry;
bool shouldLoadReferences = false;
if (loadingData.KnownAssemblies.TryGetKnownAssembly(assembly, loadingData.ObjectItemAssemblyLoaderFactory, loadingData.EdmItemCollection, out entry))
{
shouldLoadReferences = !entry.ReferencedAssembliesAreLoaded && loadReferencedAssemblies;
}
else
{
ObjectItemAssemblyLoader loader = ObjectItemAssemblyLoader.CreateLoader(assembly, loadingData);
loader.Load();
shouldLoadReferences = loadReferencedAssemblies;
}
if (shouldLoadReferences)
{
if (entry == null && loadingData.KnownAssemblies.TryGetKnownAssembly(assembly, loadingData.ObjectItemAssemblyLoaderFactory, loadingData.EdmItemCollection, out entry) ||
entry != null)
{
entry.ReferencedAssembliesAreLoaded = true;
}
Debug.Assert(entry != null, "we should always have an entry, why don't we?");
// We will traverse through all the statically linked assemblies and their dependencies.
// Only assemblies with the EdmSchemaAttribute will be loaded and rest will be ignored
// Even if the schema attribute is missing, we should still check all the dependent assemblies
// any of the dependent assemblies can have the schema attribute
// After the given assembly has been loaded, check on the flag in _knownAssemblies to see if it has already
// been recursively loaded. The flag can be true if it was already loaded before this function was called
foreach (Assembly referencedAssembly in MetadataAssemblyHelper.GetNonSystemReferencedAssemblies(assembly))
{
// filter out "known" assemblies to prevent unnecessary loading
// recursive call
LoadAssembly(referencedAssembly, loadReferencedAssemblies, loadingData);
}
}
}
private static void UpdateCache(EdmItemCollection edmItemCollection, Dictionary<Assembly, MutableAssemblyCacheEntry> assemblies)
{
foreach (var entry in assemblies)
{
edmItemCollection.ConventionalOcCache.AddAssemblyToOcCacheFromAssemblyCache(
entry.Key, new ImmutableAssemblyCacheEntry(entry.Value));
}
}
private static void UpdateCache(LockedAssemblyCache lockedAssemblyCache, Dictionary<Assembly, MutableAssemblyCacheEntry> assemblies)
{
foreach (KeyValuePair<Assembly, MutableAssemblyCacheEntry> entry in assemblies)
{
// Add all the assemblies from the loading context to the global cache
lockedAssemblyCache.Add(entry.Key, new ImmutableAssemblyCacheEntry(entry.Value));
}
}
internal static IList<Assembly> ViewGenerationAssemblies
{
get
{
return s_viewGenAssemblies;
}
}
}
}
|