Code:
/ Dotnetfx_Vista_SP2 / Dotnetfx_Vista_SP2 / 8.0.50727.4016 / DEVDIV / depot / DevDiv / releases / Orcas / QFE / ndp / fx / src / DataEntity / System / Data / Metadata / ObjectItemCollectionAssemblyCacheEntry.cs / 1 / ObjectItemCollectionAssemblyCacheEntry.cs
//----------------------------------------------------------------------
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
//
// @owner [....], [....]
//---------------------------------------------------------------------
using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using System.Xml.Serialization;
using System.Xml;
using System.Xml.Schema;
using System.Data.Common.Utils;
using System.Diagnostics;
using System.Collections.ObjectModel;
using System.Threading;
// Using an alias for this because a lot of names in this namespace conflicts with names in metadata
using DataClasses = System.Data.Objects.DataClasses;
using System.Globalization;
using System.Data.Entity;
using System.Data.Common;
namespace System.Data.Metadata.Edm
{
///
/// Class for representing a collection of items for the object layer.
/// Most of the implemetation for actual maintainance of the collection is
/// done by ItemCollection
///
public sealed partial class ObjectItemCollection : ItemCollection
{
#region PrivateNestedClass
private class AssemblyCacheEntry
{
#region Fields
private readonly List _typesInAssembly; // types in "this" assembly
private readonly List _referencedAssemblies; // other assemblies referenced by "this" assembly
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 s_viewGenAssemblies = new ThreadSafeList();
#endregion
#region Nested classes
private class LoadingContext
{
#region Fields
// all the types that we encountered while loading - this may contain types from various assemblies
private readonly Dictionary _typesInLoading;
// list of errors encountered during loading
private readonly List _errors;
// list of unresolved navigation properties
private readonly List _unresolvedNavigationProperties = new List();
// keep the list of new assemblies that got loaded in this load assembly call. The region why we need to keep a seperate
// assembly is that if we need to keep track of errors, and if there are no errors, then only add the list of assemblies
// to the global cache. Hence global cache is never polluted with invalid assemblies
private readonly Dictionary _listOfAssembliesLoaded = new Dictionary();
// Current assembly whose type we are loading
private Assembly _currentAssembly;
// Assembly Cache Entry corresponding to the current assembly
private AssemblyCacheEntry _currentCacheEntry;
// Indicates if this assembly is already loaded in the cache
private bool _isAssemblyLoadedFromGlobalCache;
// Global Assembly Cache
private readonly static Dictionary s_globalAssemblyCache = new Dictionary();
// List of known assemblies - this list is initially passed by the caller and we keep adding to it, as and when we load
// an assembly
private readonly Dictionary _knownAssemblies;
#endregion
#region Constructor
internal LoadingContext(Assembly assembly, Dictionary knownAssemblies)
{
_typesInLoading = new Dictionary(StringComparer.Ordinal);
_errors = new List();
_knownAssemblies = knownAssemblies;
UpdateCurrentAssembly(assembly, false/*mustAssemblyBeAlreadyLoaded*/);
}
#endregion
#region Properties
internal Dictionary TypesInLoading { get { return _typesInLoading; } }
internal List EdmItemError { get { return _errors; } }
internal List UnresolvedNavigationProperties { get { return _unresolvedNavigationProperties; } }
internal Assembly CurrentAssembly { get { return _currentAssembly; } }
internal AssemblyCacheEntry AssemblyCacheEntry { get { return _currentCacheEntry; } }
internal bool IsAssemblyAlreadyLoadedInCache { get { return _isAssemblyLoadedFromGlobalCache; } }
internal Dictionary KnownAssemblies { get { return _knownAssemblies; } }
#endregion
#region Methods
///
/// Check to see if the type is already loaded - either in the typesInLoading, or ObjectItemCollection or
/// in the global cache
///
///
///
///
internal bool IsTypeAlreadyLoaded(Type clrType, out EdmType edmType)
{
edmType = null;
bool isPresentInAssemblyCache = false;
bool isPresentInTypesInLoading = false;
if (!clrType.IsGenericType && ((isPresentInTypesInLoading = TypesInLoading.TryGetValue(clrType.FullName, out edmType)) ||
(isPresentInAssemblyCache = IsTypeAlreadyInCache(clrType, out edmType))))
{
// If the type is primitive type, just return the type
if (!Helper.IsPrimitiveType(edmType))
{
Debug.Assert(!ShouldFilterAssembly(clrType.Assembly.FullName), "Since the type is already loaded, the assembly must have a schema attribute");
Debug.Assert(ObjectItemCollection.IsSchemaAttributePresent(clrType.Assembly), "Since the type is already loaded, the assembly must have a schema attribute");
// If the type is not present in the current assembly, make sure you add the type's assembly
// as one of the referenced assemblies
if (clrType.Assembly != _currentAssembly)
{
if (!_currentCacheEntry._referencedAssemblies.Contains(clrType.Assembly))
{
_currentCacheEntry._referencedAssemblies.Add(clrType.Assembly);
}
}
// If the base type BT1 of a type T1 is present in another assembly, we just load the base type from that assembly and add
// that assembly to the list of referenced assemblies. When you come to loading the referenced assembly, the type BT1
// is already present in TypeInLoading, and hence we need to add it to the list of types in assembly
else if (isPresentInTypesInLoading && !_currentCacheEntry.ContainsType(edmType.Identity))
{
_currentCacheEntry._typesInAssembly.Add(edmType);
}
// If the type was loaded from the global cache, then we need to find if this assembly is already loaded. If yes,
// then we don't need to add that type in typesInLoading
if (isPresentInAssemblyCache && !KnownAssemblies.ContainsKey(clrType.Assembly))
{
TypesInLoading.Add(clrType.FullName, edmType);
}
}
}
return (edmType != null);
}
///
/// Returns if the types is already loaded in the cache
///
///
///
///
private bool IsTypeAlreadyInCache(Type clrType, out EdmType edmType)
{
AssemblyCacheEntry cacheEntry;
edmType = null;
Debug.Assert(!_typesInLoading.ContainsKey(clrType.FullName), "This should be called only after looking in typesInLoading");
Debug.Assert(clrType.Assembly != _currentAssembly || !_currentCacheEntry.ContainsType(clrType.FullName), "The type must never be present in the current assembly list");
if (clrType.Assembly == _currentAssembly)
{
return false;
}
if (s_globalAssemblyCache.TryGetValue(clrType.Assembly, out cacheEntry))
{
return cacheEntry.TryGetEdmType(clrType.FullName, out edmType);
}
else if (_listOfAssembliesLoaded.TryGetValue(clrType.Assembly, out cacheEntry))
{
return cacheEntry.TryGetEdmType(clrType.FullName, out edmType);
}
return false;
}
///
/// Update the current assembly for the loading context. If the second parameter is true, assert that
/// the assembly must be present in the global cache. this is to make sure if a assembly was already
/// present in the global cache, all its dependent assemblies must also be present in the global cache
/// This method checks if the given assembly is present in the global cache, if yes, its loads from there
/// otherwise creates a new AssemblyCacheEntry for this assembly.
/// Also it adds the earlier current assembly, into its local cache. The reason for doing this is that until
/// we have loaded all the assemblies for this context and made sure that there are no errors, then we need
/// to update the global cache
///
///
///
internal void UpdateCurrentAssembly(Assembly assembly, bool mustAssemblyBeAlreadyLoaded)
{
Debug.Assert(assembly != null, "Current Assembly can't be set to null");
Debug.Assert(!mustAssemblyBeAlreadyLoaded || s_globalAssemblyCache.ContainsKey(assembly), "The assembly must be loaded in the cache");
// Update the current assembly
_currentAssembly = assembly;
if (mustAssemblyBeAlreadyLoaded)
{
// check if the assembly is already loaded in the cache
_currentCacheEntry = s_globalAssemblyCache[assembly];
_isAssemblyLoadedFromGlobalCache = true;
}
else if (s_globalAssemblyCache.TryGetValue(assembly, out _currentCacheEntry))
{
_isAssemblyLoadedFromGlobalCache = true;
}
// If the assemblies have circular dependencies then the assembly might have been added in the local cache
else
{
if (!_listOfAssembliesLoaded.TryGetValue(assembly, out _currentCacheEntry))
{
_currentCacheEntry = new AssemblyCacheEntry();
_listOfAssembliesLoaded.Add(_currentAssembly, _currentCacheEntry);
}
_isAssemblyLoadedFromGlobalCache = false;
}
}
// Add all assemblies to the global cache if there are no errors
internal void UpdateCacheWithAssembliesLoaded()
{
if (_errors.Count == 0)
{
foreach (KeyValuePair entry in _listOfAssembliesLoaded)
{
// Add all the assemblies from the loading context to the global cache
s_globalAssemblyCache.Add(entry.Key, entry.Value);
}
// Remove all entries from transient cache
_listOfAssembliesLoaded.Clear();
}
}
#endregion
}
private class NavigationPropertyInfo
{
private StructuralType _declaringType;
private PropertyInfo _propertyInfo;
private EdmType _propertyType;
private DataClasses.EdmRelationshipNavigationPropertyAttribute _attribute;
internal NavigationPropertyInfo(
StructuralType declaringType,
PropertyInfo propertyInfo,
EdmType propertyType,
DataClasses.EdmRelationshipNavigationPropertyAttribute attribute)
{
_declaringType = declaringType;
_propertyInfo = propertyInfo;
_propertyType = propertyType;
_attribute = attribute;
}
internal void ResolveNavigationProperty(LoadingContext context)
{
EdmMember member = null;
EdmType type;
if (context.TypesInLoading.TryGetValue(_attribute.RelationshipNamespaceName + "." + _attribute.RelationshipName, out type) &&
Helper.IsAssociationType(type))
{
AssociationType relationshipType = (AssociationType)type;
if (relationshipType != null)
{
// The return value of this property has been verified, so create the property now
NavigationProperty navigationProperty = new NavigationProperty(_propertyInfo.Name, TypeUsage.Create(_propertyType), _propertyInfo);
navigationProperty.RelationshipType = relationshipType;
member = navigationProperty;
if (relationshipType.Members[0].Name == _attribute.TargetRoleName)
{
navigationProperty.ToEndMember = (RelationshipEndMember)relationshipType.Members[0];
navigationProperty.FromEndMember = (RelationshipEndMember)relationshipType.Members[1];
}
else if (relationshipType.Members[1].Name == _attribute.TargetRoleName)
{
navigationProperty.ToEndMember = (RelationshipEndMember)relationshipType.Members[1];
navigationProperty.FromEndMember = (RelationshipEndMember)relationshipType.Members[0];
}
else
{
context.EdmItemError.Add(new EdmItemError(System.Data.Entity.Strings.TargetRoleNameInNavigationPropertyNotValid(
_propertyInfo.Name, _propertyInfo.DeclaringType.FullName, _attribute.TargetRoleName, _attribute.RelationshipName), navigationProperty));
member = null;
}
if (member != null &&
((RefType)navigationProperty.FromEndMember.TypeUsage.EdmType).ElementType.ClrType != _declaringType.ClrType)
{
context.EdmItemError.Add(new EdmItemError(System.Data.Entity.Strings.NavigationPropertyRelationshipEndTypeMismatch(
_declaringType.FullName,
navigationProperty.Name,
relationshipType.FullName,
navigationProperty.FromEndMember.Name,
((RefType)navigationProperty.FromEndMember.TypeUsage.EdmType).ElementType.ClrType), navigationProperty));
member = null;
}
}
}
else
{
context.EdmItemError.Add(new EdmItemError(System.Data.Entity.Strings.RelationshipNameInNavigationPropertyNotValid(
_propertyInfo.Name, _propertyInfo.DeclaringType.FullName, _attribute.RelationshipName), _declaringType));
}
if (member != null)
{
_declaringType.AddMember(member);
}
}
}
#endregion
#region Constructor
public AssemblyCacheEntry()
{
_typesInAssembly = new List();
_referencedAssemblies = new List();
}
#endregion
#region Internal Methods (Entry points to the Cache)
internal static IList ViewGenerationAssemblies
{
get
{
return s_viewGenAssemblies;
}
}
internal static void LoadAssemblyFromCache(Assembly assembly, bool loadReferencedAssemblies,
Dictionary knownAssemblies, out Dictionary typesInLoading, out List errors)
{
Debug.Assert(!ShouldFilterAssembly(assembly.FullName), "LoadAssemblyFromCache should be called on assembly having non-reserved public key token");
typesInLoading = null;
errors = null;
lock (_assemblyCacheLock)
{
// This function loads all the types from the given assembly and the dependent assemblies. We only try to load assemblies
// that are requried for type closure. This is different from referenced asssemblies. If the assembly is already loaded,
// then the assembly is just copied from the cache
LoadingContext context = new LoadingContext(assembly, knownAssemblies);
// Loads the current assembly and all the dependent assemblies (required for type closure)
if (!context.KnownAssemblies.ContainsKey(context.CurrentAssembly) && IsSchemaAttributePresent(context.CurrentAssembly))
{
InternalLoadAssemblyFromCache(context);
}
if (loadReferencedAssemblies)
{
InternalLoadAllReferencedAssemblies(context);
}
// resolve navigation properties that showed up
// before the relationships that they use showed up
ResolveNavigationProperties(context);
// 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(context.TypesInLoading.Values, context.EdmItemError);
// Update the global cache if there are no errors
context.UpdateCacheWithAssembliesLoaded();
// Update the out parameters once you are done with loading
typesInLoading = context.TypesInLoading;
errors = context.EdmItemError;
if (typesInLoading != null && typesInLoading.Count > 0)
{
foreach (EdmType edmType in typesInLoading.Values)
{
edmType.SetReadOnly();
}
}
}
}
internal static Assembly SafeLoadReferencedAssembly(string assemblyFullName)
{
Assembly referencedAssembly = null;
try
{
referencedAssembly = Assembly.Load(assemblyFullName);
}
catch (System.IO.FileNotFoundException)
{
// See 552932: ObjectItemCollection: fails on referenced asseblies that are not available
}
return referencedAssembly;
}
#endregion
#region Private Methods
private bool TryGetEdmType(string typeName, out EdmType edmType)
{
edmType = null;
foreach (EdmType loadedEdmType in this._typesInAssembly)
{
if (loadedEdmType.Identity == typeName)
{
edmType = loadedEdmType;
break;
}
}
return (edmType != null);
}
private bool ContainsType(string typeName)
{
EdmType edmType = null;
return TryGetEdmType(typeName, out edmType);
}
private static void InternalLoadAllReferencedAssemblies(LoadingContext context)
{
// 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 (AssemblyName asmName in context.CurrentAssembly.GetReferencedAssemblies())
{
string assemblyFullName = asmName.FullName;
if (!ShouldFilterAssembly(assemblyFullName))
{
// filter out "known" assemblies to prevent unnecessary loading
EntityBid.Trace(" loadededAssembly='%ls'\n", assemblyFullName);
Assembly referencedAssembly = SafeLoadReferencedAssembly(assemblyFullName);
if (referencedAssembly == null)
{
continue;
}
// Mark the assembly as known assembly, and since we are loading all the referenced assemblies,
// mark the value to the true
context.UpdateCurrentAssembly(referencedAssembly, false/*mustAlreadyBeLoaded*/);
// Check if the assembly is already loaded
bool areReferencedAssembliesLoaded;
if (context.KnownAssemblies.TryGetValue(referencedAssembly, out areReferencedAssembliesLoaded))
{
// If all the referenced assemblies are already loaded, don't need to do anything
if (areReferencedAssembliesLoaded)
{
continue;
}
}
// Load this assembly if the schema attrbute is present
else if (ObjectItemCollection.IsSchemaAttributePresent(referencedAssembly))
{
InternalLoadAssemblyFromCache(context);
}
// We need to add this assembly to the list of known assemblies before we start
// analyzing the referenced assemblies, since there could be circular reference
// and we need to detect that and break the loop
context.KnownAssemblies[referencedAssembly] = true;
InternalLoadAllReferencedAssemblies(context);
}
}
}
///
/// Loads the given assembly and all the other referencd assemblies in the cache. If the assembly was already present
/// then it loads from the cache
///
///
/// true if the assembly was already loaded in the cache
private static bool InternalLoadAssemblyFromCache(LoadingContext context)
{
Debug.Assert(!ShouldFilterAssembly(context.CurrentAssembly.FullName), "LoadAssemblyFromCache should be called on assembly having non-reserved public key token");
Debug.Assert(IsSchemaAttributePresent(context.CurrentAssembly), "LoadAssembly shouldn't be called with assembly having no schema attribute");
Debug.Assert(!context.KnownAssemblies.ContainsKey(context.CurrentAssembly), "InternalLoadAssemblyFromCache: This assembly must not be present in the list of known assemblies");
bool areAssembliesLoadedFromCache = context.IsAssemblyAlreadyLoadedInCache;
// Check if the assembly has been loaded in the cache then:
// 1) Add EdmTypes described in ----semblyCacheEntry.TypesInAssembly
// 2) Add Assemblies described by AssemblyCacheEntry.ReferenceAssembly, check to make sure that it's not already loaded in ObjectItemCollection
if (context.IsAssemblyAlreadyLoadedInCache)
{
foreach (EdmType type in context.AssemblyCacheEntry._typesInAssembly)
{
if (!context.TypesInLoading.ContainsKey(type.Identity))
{
context.TypesInLoading.Add(type.Identity, type);
}
}
}
else
{
LoadTypesFromAssembly(context);
}
Debug.Assert(!context.KnownAssemblies.ContainsKey(context.CurrentAssembly), "This assembly must not be present in the list of known assemblies");
context.KnownAssemblies.Add(context.CurrentAssembly, false/*ReferencedAssembliesNotLoaded*/);
// When loading assembly from cache, the cache provide the implicit-dependency i.e. cross-reference by it's type to types in other assemblies.
// In the case where assembly is loaded for the first-time, the loading process ensures that implicitly-dependenent assemblies are loaded
foreach (Assembly referencedAssembly in context.AssemblyCacheEntry._referencedAssemblies)
{
if (!context.KnownAssemblies.ContainsKey(referencedAssembly))
{
// Update the current assembly that we are currently loading
context.UpdateCurrentAssembly(referencedAssembly, context.IsAssemblyAlreadyLoadedInCache);
areAssembliesLoadedFromCache |= InternalLoadAssemblyFromCache(context);
}
}
return areAssembliesLoadedFromCache;
}
///
/// Loads the set of types from the given assembly and adds it to the given list of types
///
/// context containing information for loading
private static void LoadTypesFromAssembly(LoadingContext context)
{
Debug.Assert(context.AssemblyCacheEntry._typesInAssembly.Count == 0);
LoadRelationshipTypes(context);
// Loop through each type in the assembly and process it
foreach (Type type in context.CurrentAssembly.GetTypes())
{
// If the type doesn't have the same EdmTypeAttribute defined, then it's not a special type
// that we care about, skip it.
if (!type.IsDefined(typeof(DataClasses.EdmTypeAttribute), false))
{
continue;
}
// Load the metadata for this type
LoadFromType(type, context);
}
}
///
/// Load metadata of the given type - when you call this method, you should check and make sure that the type has
/// edm attribute. If it doesn't,we won't load the type and it will be returned as null
///
///
///
///
private static EdmType LoadFromType(Type clrType, LoadingContext context)
{
EdmType edmType = null;
if (clrType.IsNested)
{
context.EdmItemError.Add(new EdmItemError(System.Data.Entity.Strings.NestedClassNotSupported(clrType.FullName, clrType.Assembly.FullName), null));
return null;
}
// Lookup for the specified type in the following structures:
// 1) typesInLoading - that describes the (*new*) types loaded so far by the current load-operation.
// 2) objectItemCollection.Items - describes the types that are already loaded in ObjectItemCollection
// 3) AssemblyCache - this describes a set of AssemblyCacheEntries containing 1) assembly types and 2) (implicitly) dependent assemblies
// iff type is present in assembly_cache, add it to typesInLoading.
if (!clrType.IsGenericType && context.IsTypeAlreadyLoaded(clrType, out edmType))
{
// Check to make sure the CLR type we got is the same as the given one
if (edmType.ClrType != clrType)
{
context.EdmItemError.Add(new EdmItemError(System.Data.Entity.Strings.NewTypeConflictsWithExistingType(
clrType.AssemblyQualifiedName, edmType.ClrType.AssemblyQualifiedName), edmType));
return null;
}
return edmType;
}
DataClasses.EdmTypeAttribute[] typeAttributes = (DataClasses.EdmTypeAttribute[])clrType.GetCustomAttributes(typeof(DataClasses.EdmTypeAttribute), false /*inherit*/);
// the CLR doesn't allow types to have duplicate/multiple attribute declarations
if (typeAttributes.Length != 0)
{
DataClasses.EdmTypeAttribute typeAttribute = typeAttributes[0];
string cspaceTypeName = String.IsNullOrEmpty(typeAttribute.Name) ? clrType.Name : typeAttribute.Name;
if(String.IsNullOrEmpty(typeAttribute.NamespaceName) && clrType.Namespace == null)
{
context.EdmItemError.Add(new EdmItemError(Strings.Validator_TypeHasNoNamespace, edmType));
return null;
}
string cspaceNamespaceName = String.IsNullOrEmpty(typeAttribute.NamespaceName) ? clrType.Namespace : typeAttribute.NamespaceName;
if (typeAttribute.GetType() == typeof(DataClasses.EdmEntityTypeAttribute))
{
edmType = new ClrEntityType(clrType, cspaceNamespaceName, cspaceTypeName);
}
else
{
Debug.Assert(typeAttribute.GetType() == typeof(DataClasses.EdmComplexTypeAttribute), "Invalid type attribute encountered");
edmType = new ClrComplexType(clrType, cspaceNamespaceName, cspaceTypeName);
}
}
else
{
return ResolveNonSchemaType(clrType, context);
}
// If type is not present in the current assembly, make sure you add the type's assembly in the list of referenced
// assembly for the current assembly. But we still have to load the type and all its dependent type
if (clrType.Assembly != context.CurrentAssembly)
{
// 1) Enqueue the "other" assembly for being loaded
// 2) Register the "other" assembly as a referenced-assembly of currentAssembly
// 3) and Load the specific type from "other" assembly for resolving the forward reference
if (ShouldFilterAssembly(clrType.Assembly.FullName) || !IsSchemaAttributePresent(clrType.Assembly))
{
context.EdmItemError.Add(new EdmItemError(System.Data.Entity.Strings.MissingAssemblyAttribute(
clrType.FullName, clrType.Assembly.FullName), edmType));
return null;
}
else
{
if (!context.AssemblyCacheEntry._referencedAssemblies.Contains(clrType.Assembly))
{
context.AssemblyCacheEntry._referencedAssemblies.Add(clrType.Assembly);
}
}
}
else
{
Debug.Assert(!context.AssemblyCacheEntry.ContainsType(edmType.Identity), "This type must not be already present in the list of types for this assembly");
// Also add this to the list of the types for this assembly
context.AssemblyCacheEntry._typesInAssembly.Add(edmType);
}
// Add this to the known type map so we won't try to load it again
context.TypesInLoading.Add(clrType.FullName, edmType);
// Load properties for structural type
if (Helper.IsStructuralType(edmType))
{
//Load base type only for entity type - not sure if we will allow complex type inheritance
if (Helper.IsEntityType(edmType))
{
edmType.BaseType = LoadFromType(clrType.BaseType, context);
}
// Load the properties for this type
LoadPropertiesFromType((StructuralType)edmType, context);
}
return edmType;
}
///
/// Load all the property metadata of the given type
///
/// The type where properties are loaded
///
private static void LoadPropertiesFromType(StructuralType structuralType, LoadingContext context)
{
// Look at both public, internal, and private instanced properties declared at this type, inherited members
// are not looked at. Internal and private properties are also looked at because they are also schematized fields
PropertyInfo[] properties = structuralType.ClrType.GetProperties(BindingFlags.DeclaredOnly |
BindingFlags.Instance |
BindingFlags.Public |
BindingFlags.NonPublic);
foreach (PropertyInfo property in properties)
{
EdmMember newMember = null;
bool isEntityKeyProperty = false; //used for EdmScalarProperties only
// EdmScalarPropertyAttribute, EdmComplexPropertyAttribute and EdmRelationshipNavigationPropertyAttribute
// are all EdmPropertyAttributes that we need to process. If the current property is not an EdmPropertyAttribute
// we will just ignore it and skip to the next property.
if (property.IsDefined(typeof(DataClasses.EdmRelationshipNavigationPropertyAttribute), false))
{
SaveNavigationProperty(structuralType, property, context);
}
else if (property.IsDefined(typeof(DataClasses.EdmScalarPropertyAttribute), false))
{
newMember = LoadScalarProperty(property, context, out isEntityKeyProperty);
}
else if (property.IsDefined(typeof(DataClasses.EdmComplexPropertyAttribute), false))
{
newMember = LoadComplexTypeProperty(property, context);
}
if (newMember == null)
{
// Property does not have one of the following attributes:
// EdmScalarPropertyAttribute, EdmComplexPropertyAttribute, EdmRelationshipNavigationPropertyAttribute
// This means its an unmapped property and can be ignored.
// Or there were error encountered while loading the properties
continue;
}
// Add the property object to the type
structuralType.AddMember(newMember);
// Add to the entity's collection of key members
// Do this here instead of in the if condition above for scalar properties because
// we want to make sure the AddMember call above did not fail before updating the key members
if (structuralType.BuiltInTypeKind == BuiltInTypeKind.EntityType && isEntityKeyProperty)
{
((EntityType)structuralType).AddKeyMember(newMember);
}
}
}
///
/// Loads metadata for the navigation properties
///
///
///
///
///
private static void SaveNavigationProperty(StructuralType declaringType, PropertyInfo property, LoadingContext context)
{
Debug.Assert(property.IsDefined(typeof(DataClasses.EdmRelationshipNavigationPropertyAttribute), false), "The property must have navigation property defined");
// EdmScalarPropertyAttribute, EdmComplexPropertyAttribute and EdmRelationshipNavigationPropertyAttribute
// are all EdmPropertyAttributes that we need to process. If the current property is not an EdmPropertyAttribute
// we will just ignore it and skip to the next property.
object[] relationshipPropertyAttributes = property.GetCustomAttributes(typeof(DataClasses.EdmRelationshipNavigationPropertyAttribute), false);
Debug.Assert(relationshipPropertyAttributes.Length == 1, "There should be exactly one property for every navigation property");
// Load the property type and create a new property object
EdmType propertyType = LoadFromType(property.PropertyType, context);
// The only valid return types from navigation properties are:
// (1) EntityType
// (2) CollectionType containing valid EntityType
// If LoadFromType returned null, it could mean that we couldn't validate any part of the type, or it could mean that it's a generic
// where the main generic type was validated, but the generic type parameter was not. We can't tell the difference, so just fail
// with the same error message in both cases. The user will have to figure out which part of the type is wrong.
// We can't just rely on checking for a generic because it can lead to a scenario where we report that the type parameter is invalid
// when really it's the main generic type. That is more confusing than reporting the full name and letting the user determine the problem.
if (propertyType == null || !(propertyType.BuiltInTypeKind == BuiltInTypeKind.EntityType || propertyType.BuiltInTypeKind == BuiltInTypeKind.CollectionType))
{
// Once an error is detected the property does not need to be validated further, just add to the errors
// collection and continue with the next property. The failure will cause an exception to be thrown later during validation of all of the types.
context.EdmItemError.Add(new EdmItemError(System.Data.Entity.Strings.Validator_OSpace_InvalidNavPropReturnType(property.Name, property.DeclaringType.FullName, property.PropertyType.FullName), null));
}
else
{
// else we have a valid EntityType or CollectionType that contains EntityType. ResolveNonSchemaType enforces that a collection type
// must contain an EntityType, and if it doesn't, propertyType will be null here. If propertyType is EntityType or CollectionType we know it is valid
// Expecting EdmRelationshipNavigationPropertyAttribute to have AllowMultiple=False, so only look at first element in the attribute array
DataClasses.EdmRelationshipNavigationPropertyAttribute attribute = (DataClasses.EdmRelationshipNavigationPropertyAttribute)relationshipPropertyAttributes[0];
context.UnresolvedNavigationProperties.Add(new NavigationPropertyInfo(declaringType, property, propertyType, attribute));
}
}
private static void ResolveNavigationProperties(LoadingContext context)
{
foreach (NavigationPropertyInfo info in context.UnresolvedNavigationProperties)
{
info.ResolveNavigationProperty(context);
}
}
///
/// Load the property with scalar property attribute
///
///
///
///
///
private static EdmMember LoadScalarProperty(PropertyInfo property, LoadingContext context, out bool isEntityKeyProperty)
{
Debug.Assert(property.IsDefined(typeof(DataClasses.EdmScalarPropertyAttribute), false), "The property must have a scalar attribute");
EdmMember member = null;
isEntityKeyProperty = false;
// Load the property type and create a new property object
EdmType propertyType = LoadFromType(property.PropertyType, context);
// If the type could not be loaded it's definitely not a primitive type, so that's an error
// If it could be loaded but is not a primitive that's an error as well
if (propertyType == null || propertyType.BuiltInTypeKind != BuiltInTypeKind.PrimitiveType)
{
// This property does not need to be validated further, just add to the errors collection and continue with the next property
// This failure will cause an exception to be thrown later during validation of all of the types
context.EdmItemError.Add(new EdmItemError(System.Data.Entity.Strings.Validator_OSpace_ScalarPropertyNotPrimitive(property.Name, property.DeclaringType.FullName, property.PropertyType.FullName), null));
}
else
{
object[] attrs = property.GetCustomAttributes(typeof(DataClasses.EdmScalarPropertyAttribute), false);
Debug.Assert(attrs.Length == 1, "Every property can exactly have one ScalarProperty Attribute");
// Expecting EdmScalarPropertyAttribute to have AllowMultiple=False, so only look at first element in the attribute array
isEntityKeyProperty = ((DataClasses.EdmScalarPropertyAttribute)attrs[0]).EntityKeyProperty;
bool isNullable = ((DataClasses.EdmScalarPropertyAttribute)attrs[0]).IsNullable;
member = new EdmProperty(property.Name,
TypeUsage.Create(propertyType, new FacetValues { Nullable = isNullable }),
property);
}
return member;
}
///
/// Load the property with complex type property attribute
///
///
///
///
private static EdmMember LoadComplexTypeProperty(PropertyInfo property, LoadingContext context)
{
// Load the property type and create a new property object
EdmType propertyType = LoadFromType(property.PropertyType, context);
// If the type could not be loaded it's definitely not a complex type, so that's an error
// If it could be loaded but is not a complex type that's an error as well
if (propertyType == null || propertyType.BuiltInTypeKind != BuiltInTypeKind.ComplexType)
{
// This property does not need to be validated further, just add to the errors collection and continue with the next property
// This failure will cause an exception to be thrown later during validation of all of the types
context.EdmItemError.Add(new EdmItemError(System.Data.Entity.Strings.Validator_OSpace_ComplexPropertyNotComplex(property.Name, property.DeclaringType.FullName, property.PropertyType.FullName), null));
}
else
{
EdmProperty newProperty = new EdmProperty(property.Name,
TypeUsage.Create(propertyType, new FacetValues { Nullable = false }),
property);
return newProperty;
}
return null;
}
///
/// Resolves the given non-schematized type by looking at what kind of type this is. Non-schematized types include
/// collection types, nullable types, reference types, and primitive types.
///
/// The non-schematized CLR type that is being resolved
/// A dictionary of types that we are currently loading
/// An EdmType object representing the type, null if we can't resolve it
private static EdmType ResolveNonSchemaType(Type clrType, LoadingContext context)
{
// Let's check to see if this type is a ref type, a nullable type, or a collection type, these are the types that
// we need to take special care of them
if (clrType.IsGenericType)
{
Type genericType = clrType.GetGenericTypeDefinition();
// Try to resolve the element type into a type object
EdmType elementType = LoadFromType(clrType.GetGenericArguments()[0], context);
if (elementType == null)
{
// return null and let the caller deal with the error handling
return null;
}
// Create the collection or reference type object or just a simple value that was wrapped inside a Nullable
if (genericType == typeof(Nullable<>))
{
// In here, the Nullable<> is unwrapped. In CDM, nullability is on a per property basis, there shouldn't be
// the notion of a nullable type
return elementType;
}
else if (genericType == typeof(DataClasses.EntityReference<>))
{
// EntityReference is IEnumerable, so we need to detect this condition and return here.
// We don't support EntityReference in this scenario, so just fail.
return null;
}
else if (typeof(IEnumerable).IsAssignableFrom(clrType))
{
EntityType entityType = elementType as EntityType;
if (entityType == null)
{
// return null and let the caller deal with the error handling
return null;
}
return entityType.GetCollectionType();
}
}
// For primitive types, look in clr provider manifest
PrimitiveType primitiveType;
if (ClrProviderManifest.Instance.TryGetPrimitiveType(clrType, out primitiveType))
{
return primitiveType;
}
return null;
}
///
/// This method loads all the relationship type that this entity takes part in
///
///
///
private static void LoadRelationshipTypes(LoadingContext context)
{
foreach (System.Data.Objects.DataClasses.EdmRelationshipAttribute roleAttribute in context.CurrentAssembly.GetCustomAttributes(typeof(System.Data.Objects.DataClasses.EdmRelationshipAttribute), false /*inherit*/))
{
// Check if there is an entry already with this name
if (TryFindNullParametersInRelationshipAttribute(roleAttribute, context))
{
// don't give more errors for these same bad parameters
continue;
}
bool errorEncountered = false;
// return error if the role names are the same
if (roleAttribute.Role1Name == roleAttribute.Role2Name)
{
context.EdmItemError.Add(new EdmItemError(System.Data.Entity.Strings.SameRoleNameOnRelationshipAttribute(roleAttribute.RelationshipName, roleAttribute.Role2Name),
null));
errorEncountered = true;
}
// Make sure the clr type specified are the same
EntityType type1;
if (!TryGetRelationshipEndEntityType(context, roleAttribute.Role1Type, out type1))
{
context.EdmItemError.Add(new EdmItemError(System.Data.Entity.Strings.RoleTypeInEdmRelationshipAttributeIsInvalidType(roleAttribute.RelationshipName, roleAttribute.Role1Name, roleAttribute.Role1Type),
null));
errorEncountered = true;
}
EntityType type2;
if (!TryGetRelationshipEndEntityType(context, roleAttribute.Role2Type, out type2))
{
context.EdmItemError.Add(new EdmItemError(System.Data.Entity.Strings.RoleTypeInEdmRelationshipAttributeIsInvalidType(roleAttribute.RelationshipName, roleAttribute.Role2Name, roleAttribute.Role2Type),
null));
errorEncountered = true;
}
if (!errorEncountered)
{
AssociationType associationType = new AssociationType(roleAttribute.RelationshipName, roleAttribute.RelationshipNamespaceName, DataSpace.OSpace);
associationType.AddKeyMember(new AssociationEndMember(roleAttribute.Role1Name, type1.GetReferenceType(), roleAttribute.Role1Multiplicity));
associationType.AddKeyMember(new AssociationEndMember(roleAttribute.Role2Name, type2.GetReferenceType(), roleAttribute.Role2Multiplicity));
context.TypesInLoading.Add(associationType.FullName, associationType);
// get assembly entry and add association type to the list of types in the assembly
Debug.Assert(!context.AssemblyCacheEntry.ContainsType(associationType.FullName), "Relationship type must not be present in the list of types");
context.AssemblyCacheEntry._typesInAssembly.Add(associationType);
}
}
}
private static bool TryFindNullParametersInRelationshipAttribute(System.Data.Objects.DataClasses.EdmRelationshipAttribute roleAttribute, LoadingContext context)
{
if (roleAttribute.RelationshipName == null)
{
context.EdmItemError.Add(new EdmItemError(System.Data.Entity.Strings.NullRelationshipNameforEdmRelationshipAttribute(context.CurrentAssembly.FullName), null));
return true;
}
bool nullsFound = false;
if (roleAttribute.RelationshipNamespaceName == null)
{
context.EdmItemError.Add(new EdmItemError(System.Data.Entity.Strings.NullParameterForEdmRelationshipAttribute(
"RelationshipNamespaceName", roleAttribute.RelationshipName), null));
nullsFound = true;
}
if (roleAttribute.Role1Name == null)
{
context.EdmItemError.Add(new EdmItemError(System.Data.Entity.Strings.NullParameterForEdmRelationshipAttribute(
"Role1Name", roleAttribute.RelationshipName), null));
nullsFound = true;
}
if (roleAttribute.Role1Type == null)
{
context.EdmItemError.Add(new EdmItemError(System.Data.Entity.Strings.NullParameterForEdmRelationshipAttribute(
"Role1Type", roleAttribute.RelationshipName), null));
nullsFound = true;
}
if (roleAttribute.Role2Name == null)
{
context.EdmItemError.Add(new EdmItemError(System.Data.Entity.Strings.NullParameterForEdmRelationshipAttribute(
"Role2Name", roleAttribute.RelationshipName), null));
nullsFound = true;
}
if (roleAttribute.Role2Type == null)
{
context.EdmItemError.Add(new EdmItemError(System.Data.Entity.Strings.NullParameterForEdmRelationshipAttribute(
"Role2Type", roleAttribute.RelationshipName), null));
nullsFound = true;
}
return nullsFound;
}
private static bool TryGetRelationshipEndEntityType(LoadingContext context, Type type, out EntityType entityType)
{
if (type == null)
{
entityType = null;
return false;
}
EdmType edmType = LoadFromType(type, context);
if (edmType == null || !Helper.IsEntityType(edmType))
{
entityType = null;
return false;
}
entityType = (EntityType)edmType;
return true;
}
#endregion
}
#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;
using System.Collections.Generic;
using System.Reflection;
using System.Xml.Serialization;
using System.Xml;
using System.Xml.Schema;
using System.Data.Common.Utils;
using System.Diagnostics;
using System.Collections.ObjectModel;
using System.Threading;
// Using an alias for this because a lot of names in this namespace conflicts with names in metadata
using DataClasses = System.Data.Objects.DataClasses;
using System.Globalization;
using System.Data.Entity;
using System.Data.Common;
namespace System.Data.Metadata.Edm
{
///
/// Class for representing a collection of items for the object layer.
/// Most of the implemetation for actual maintainance of the collection is
/// done by ItemCollection
///
public sealed partial class ObjectItemCollection : ItemCollection
{
#region PrivateNestedClass
private class AssemblyCacheEntry
{
#region Fields
private readonly List _typesInAssembly; // types in "this" assembly
private readonly List _referencedAssemblies; // other assemblies referenced by "this" assembly
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 s_viewGenAssemblies = new ThreadSafeList();
#endregion
#region Nested classes
private class LoadingContext
{
#region Fields
// all the types that we encountered while loading - this may contain types from various assemblies
private readonly Dictionary _typesInLoading;
// list of errors encountered during loading
private readonly List _errors;
// list of unresolved navigation properties
private readonly List _unresolvedNavigationProperties = new List();
// keep the list of new assemblies that got loaded in this load assembly call. The region why we need to keep a seperate
// assembly is that if we need to keep track of errors, and if there are no errors, then only add the list of assemblies
// to the global cache. Hence global cache is never polluted with invalid assemblies
private readonly Dictionary _listOfAssembliesLoaded = new Dictionary();
// Current assembly whose type we are loading
private Assembly _currentAssembly;
// Assembly Cache Entry corresponding to the current assembly
private AssemblyCacheEntry _currentCacheEntry;
// Indicates if this assembly is already loaded in the cache
private bool _isAssemblyLoadedFromGlobalCache;
// Global Assembly Cache
private readonly static Dictionary s_globalAssemblyCache = new Dictionary();
// List of known assemblies - this list is initially passed by the caller and we keep adding to it, as and when we load
// an assembly
private readonly Dictionary _knownAssemblies;
#endregion
#region Constructor
internal LoadingContext(Assembly assembly, Dictionary knownAssemblies)
{
_typesInLoading = new Dictionary(StringComparer.Ordinal);
_errors = new List();
_knownAssemblies = knownAssemblies;
UpdateCurrentAssembly(assembly, false/*mustAssemblyBeAlreadyLoaded*/);
}
#endregion
#region Properties
internal Dictionary TypesInLoading { get { return _typesInLoading; } }
internal List EdmItemError { get { return _errors; } }
internal List UnresolvedNavigationProperties { get { return _unresolvedNavigationProperties; } }
internal Assembly CurrentAssembly { get { return _currentAssembly; } }
internal AssemblyCacheEntry AssemblyCacheEntry { get { return _currentCacheEntry; } }
internal bool IsAssemblyAlreadyLoadedInCache { get { return _isAssemblyLoadedFromGlobalCache; } }
internal Dictionary KnownAssemblies { get { return _knownAssemblies; } }
#endregion
#region Methods
///
/// Check to see if the type is already loaded - either in the typesInLoading, or ObjectItemCollection or
/// in the global cache
///
///
///
///
internal bool IsTypeAlreadyLoaded(Type clrType, out EdmType edmType)
{
edmType = null;
bool isPresentInAssemblyCache = false;
bool isPresentInTypesInLoading = false;
if (!clrType.IsGenericType && ((isPresentInTypesInLoading = TypesInLoading.TryGetValue(clrType.FullName, out edmType)) ||
(isPresentInAssemblyCache = IsTypeAlreadyInCache(clrType, out edmType))))
{
// If the type is primitive type, just return the type
if (!Helper.IsPrimitiveType(edmType))
{
Debug.Assert(!ShouldFilterAssembly(clrType.Assembly.FullName), "Since the type is already loaded, the assembly must have a schema attribute");
Debug.Assert(ObjectItemCollection.IsSchemaAttributePresent(clrType.Assembly), "Since the type is already loaded, the assembly must have a schema attribute");
// If the type is not present in the current assembly, make sure you add the type's assembly
// as one of the referenced assemblies
if (clrType.Assembly != _currentAssembly)
{
if (!_currentCacheEntry._referencedAssemblies.Contains(clrType.Assembly))
{
_currentCacheEntry._referencedAssemblies.Add(clrType.Assembly);
}
}
// If the base type BT1 of a type T1 is present in another assembly, we just load the base type from that assembly and add
// that assembly to the list of referenced assemblies. When you come to loading the referenced assembly, the type BT1
// is already present in TypeInLoading, and hence we need to add it to the list of types in assembly
else if (isPresentInTypesInLoading && !_currentCacheEntry.ContainsType(edmType.Identity))
{
_currentCacheEntry._typesInAssembly.Add(edmType);
}
// If the type was loaded from the global cache, then we need to find if this assembly is already loaded. If yes,
// then we don't need to add that type in typesInLoading
if (isPresentInAssemblyCache && !KnownAssemblies.ContainsKey(clrType.Assembly))
{
TypesInLoading.Add(clrType.FullName, edmType);
}
}
}
return (edmType != null);
}
///
/// Returns if the types is already loaded in the cache
///
///
///
///
private bool IsTypeAlreadyInCache(Type clrType, out EdmType edmType)
{
AssemblyCacheEntry cacheEntry;
edmType = null;
Debug.Assert(!_typesInLoading.ContainsKey(clrType.FullName), "This should be called only after looking in typesInLoading");
Debug.Assert(clrType.Assembly != _currentAssembly || !_currentCacheEntry.ContainsType(clrType.FullName), "The type must never be present in the current assembly list");
if (clrType.Assembly == _currentAssembly)
{
return false;
}
if (s_globalAssemblyCache.TryGetValue(clrType.Assembly, out cacheEntry))
{
return cacheEntry.TryGetEdmType(clrType.FullName, out edmType);
}
else if (_listOfAssembliesLoaded.TryGetValue(clrType.Assembly, out cacheEntry))
{
return cacheEntry.TryGetEdmType(clrType.FullName, out edmType);
}
return false;
}
///
/// Update the current assembly for the loading context. If the second parameter is true, assert that
/// the assembly must be present in the global cache. this is to make sure if a assembly was already
/// present in the global cache, all its dependent assemblies must also be present in the global cache
/// This method checks if the given assembly is present in the global cache, if yes, its loads from there
/// otherwise creates a new AssemblyCacheEntry for this assembly.
/// Also it adds the earlier current assembly, into its local cache. The reason for doing this is that until
/// we have loaded all the assemblies for this context and made sure that there are no errors, then we need
/// to update the global cache
///
///
///
internal void UpdateCurrentAssembly(Assembly assembly, bool mustAssemblyBeAlreadyLoaded)
{
Debug.Assert(assembly != null, "Current Assembly can't be set to null");
Debug.Assert(!mustAssemblyBeAlreadyLoaded || s_globalAssemblyCache.ContainsKey(assembly), "The assembly must be loaded in the cache");
// Update the current assembly
_currentAssembly = assembly;
if (mustAssemblyBeAlreadyLoaded)
{
// check if the assembly is already loaded in the cache
_currentCacheEntry = s_globalAssemblyCache[assembly];
_isAssemblyLoadedFromGlobalCache = true;
}
else if (s_globalAssemblyCache.TryGetValue(assembly, out _currentCacheEntry))
{
_isAssemblyLoadedFromGlobalCache = true;
}
// If the assemblies have circular dependencies then the assembly might have been added in the local cache
else
{
if (!_listOfAssembliesLoaded.TryGetValue(assembly, out _currentCacheEntry))
{
_currentCacheEntry = new AssemblyCacheEntry();
_listOfAssembliesLoaded.Add(_currentAssembly, _currentCacheEntry);
}
_isAssemblyLoadedFromGlobalCache = false;
}
}
// Add all assemblies to the global cache if there are no errors
internal void UpdateCacheWithAssembliesLoaded()
{
if (_errors.Count == 0)
{
foreach (KeyValuePair entry in _listOfAssembliesLoaded)
{
// Add all the assemblies from the loading context to the global cache
s_globalAssemblyCache.Add(entry.Key, entry.Value);
}
// Remove all entries from transient cache
_listOfAssembliesLoaded.Clear();
}
}
#endregion
}
private class NavigationPropertyInfo
{
private StructuralType _declaringType;
private PropertyInfo _propertyInfo;
private EdmType _propertyType;
private DataClasses.EdmRelationshipNavigationPropertyAttribute _attribute;
internal NavigationPropertyInfo(
StructuralType declaringType,
PropertyInfo propertyInfo,
EdmType propertyType,
DataClasses.EdmRelationshipNavigationPropertyAttribute attribute)
{
_declaringType = declaringType;
_propertyInfo = propertyInfo;
_propertyType = propertyType;
_attribute = attribute;
}
internal void ResolveNavigationProperty(LoadingContext context)
{
EdmMember member = null;
EdmType type;
if (context.TypesInLoading.TryGetValue(_attribute.RelationshipNamespaceName + "." + _attribute.RelationshipName, out type) &&
Helper.IsAssociationType(type))
{
AssociationType relationshipType = (AssociationType)type;
if (relationshipType != null)
{
// The return value of this property has been verified, so create the property now
NavigationProperty navigationProperty = new NavigationProperty(_propertyInfo.Name, TypeUsage.Create(_propertyType), _propertyInfo);
navigationProperty.RelationshipType = relationshipType;
member = navigationProperty;
if (relationshipType.Members[0].Name == _attribute.TargetRoleName)
{
navigationProperty.ToEndMember = (RelationshipEndMember)relationshipType.Members[0];
navigationProperty.FromEndMember = (RelationshipEndMember)relationshipType.Members[1];
}
else if (relationshipType.Members[1].Name == _attribute.TargetRoleName)
{
navigationProperty.ToEndMember = (RelationshipEndMember)relationshipType.Members[1];
navigationProperty.FromEndMember = (RelationshipEndMember)relationshipType.Members[0];
}
else
{
context.EdmItemError.Add(new EdmItemError(System.Data.Entity.Strings.TargetRoleNameInNavigationPropertyNotValid(
_propertyInfo.Name, _propertyInfo.DeclaringType.FullName, _attribute.TargetRoleName, _attribute.RelationshipName), navigationProperty));
member = null;
}
if (member != null &&
((RefType)navigationProperty.FromEndMember.TypeUsage.EdmType).ElementType.ClrType != _declaringType.ClrType)
{
context.EdmItemError.Add(new EdmItemError(System.Data.Entity.Strings.NavigationPropertyRelationshipEndTypeMismatch(
_declaringType.FullName,
navigationProperty.Name,
relationshipType.FullName,
navigationProperty.FromEndMember.Name,
((RefType)navigationProperty.FromEndMember.TypeUsage.EdmType).ElementType.ClrType), navigationProperty));
member = null;
}
}
}
else
{
context.EdmItemError.Add(new EdmItemError(System.Data.Entity.Strings.RelationshipNameInNavigationPropertyNotValid(
_propertyInfo.Name, _propertyInfo.DeclaringType.FullName, _attribute.RelationshipName), _declaringType));
}
if (member != null)
{
_declaringType.AddMember(member);
}
}
}
#endregion
#region Constructor
public AssemblyCacheEntry()
{
_typesInAssembly = new List();
_referencedAssemblies = new List();
}
#endregion
#region Internal Methods (Entry points to the Cache)
internal static IList ViewGenerationAssemblies
{
get
{
return s_viewGenAssemblies;
}
}
internal static void LoadAssemblyFromCache(Assembly assembly, bool loadReferencedAssemblies,
Dictionary knownAssemblies, out Dictionary typesInLoading, out List errors)
{
Debug.Assert(!ShouldFilterAssembly(assembly.FullName), "LoadAssemblyFromCache should be called on assembly having non-reserved public key token");
typesInLoading = null;
errors = null;
lock (_assemblyCacheLock)
{
// This function loads all the types from the given assembly and the dependent assemblies. We only try to load assemblies
// that are requried for type closure. This is different from referenced asssemblies. If the assembly is already loaded,
// then the assembly is just copied from the cache
LoadingContext context = new LoadingContext(assembly, knownAssemblies);
// Loads the current assembly and all the dependent assemblies (required for type closure)
if (!context.KnownAssemblies.ContainsKey(context.CurrentAssembly) && IsSchemaAttributePresent(context.CurrentAssembly))
{
InternalLoadAssemblyFromCache(context);
}
if (loadReferencedAssemblies)
{
InternalLoadAllReferencedAssemblies(context);
}
// resolve navigation properties that showed up
// before the relationships that they use showed up
ResolveNavigationProperties(context);
// 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(context.TypesInLoading.Values, context.EdmItemError);
// Update the global cache if there are no errors
context.UpdateCacheWithAssembliesLoaded();
// Update the out parameters once you are done with loading
typesInLoading = context.TypesInLoading;
errors = context.EdmItemError;
if (typesInLoading != null && typesInLoading.Count > 0)
{
foreach (EdmType edmType in typesInLoading.Values)
{
edmType.SetReadOnly();
}
}
}
}
internal static Assembly SafeLoadReferencedAssembly(string assemblyFullName)
{
Assembly referencedAssembly = null;
try
{
referencedAssembly = Assembly.Load(assemblyFullName);
}
catch (System.IO.FileNotFoundException)
{
// See 552932: ObjectItemCollection: fails on referenced asseblies that are not available
}
return referencedAssembly;
}
#endregion
#region Private Methods
private bool TryGetEdmType(string typeName, out EdmType edmType)
{
edmType = null;
foreach (EdmType loadedEdmType in this._typesInAssembly)
{
if (loadedEdmType.Identity == typeName)
{
edmType = loadedEdmType;
break;
}
}
return (edmType != null);
}
private bool ContainsType(string typeName)
{
EdmType edmType = null;
return TryGetEdmType(typeName, out edmType);
}
private static void InternalLoadAllReferencedAssemblies(LoadingContext context)
{
// 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 (AssemblyName asmName in context.CurrentAssembly.GetReferencedAssemblies())
{
string assemblyFullName = asmName.FullName;
if (!ShouldFilterAssembly(assemblyFullName))
{
// filter out "known" assemblies to prevent unnecessary loading
EntityBid.Trace(" loadededAssembly='%ls'\n", assemblyFullName);
Assembly referencedAssembly = SafeLoadReferencedAssembly(assemblyFullName);
if (referencedAssembly == null)
{
continue;
}
// Mark the assembly as known assembly, and since we are loading all the referenced assemblies,
// mark the value to the true
context.UpdateCurrentAssembly(referencedAssembly, false/*mustAlreadyBeLoaded*/);
// Check if the assembly is already loaded
bool areReferencedAssembliesLoaded;
if (context.KnownAssemblies.TryGetValue(referencedAssembly, out areReferencedAssembliesLoaded))
{
// If all the referenced assemblies are already loaded, don't need to do anything
if (areReferencedAssembliesLoaded)
{
continue;
}
}
// Load this assembly if the schema attrbute is present
else if (ObjectItemCollection.IsSchemaAttributePresent(referencedAssembly))
{
InternalLoadAssemblyFromCache(context);
}
// We need to add this assembly to the list of known assemblies before we start
// analyzing the referenced assemblies, since there could be circular reference
// and we need to detect that and break the loop
context.KnownAssemblies[referencedAssembly] = true;
InternalLoadAllReferencedAssemblies(context);
}
}
}
///
/// Loads the given assembly and all the other referencd assemblies in the cache. If the assembly was already present
/// then it loads from the cache
///
///
/// true if the assembly was already loaded in the cache
private static bool InternalLoadAssemblyFromCache(LoadingContext context)
{
Debug.Assert(!ShouldFilterAssembly(context.CurrentAssembly.FullName), "LoadAssemblyFromCache should be called on assembly having non-reserved public key token");
Debug.Assert(IsSchemaAttributePresent(context.CurrentAssembly), "LoadAssembly shouldn't be called with assembly having no schema attribute");
Debug.Assert(!context.KnownAssemblies.ContainsKey(context.CurrentAssembly), "InternalLoadAssemblyFromCache: This assembly must not be present in the list of known assemblies");
bool areAssembliesLoadedFromCache = context.IsAssemblyAlreadyLoadedInCache;
// Check if the assembly has been loaded in the cache then:
// 1) Add EdmTypes described in ----semblyCacheEntry.TypesInAssembly
// 2) Add Assemblies described by AssemblyCacheEntry.ReferenceAssembly, check to make sure that it's not already loaded in ObjectItemCollection
if (context.IsAssemblyAlreadyLoadedInCache)
{
foreach (EdmType type in context.AssemblyCacheEntry._typesInAssembly)
{
if (!context.TypesInLoading.ContainsKey(type.Identity))
{
context.TypesInLoading.Add(type.Identity, type);
}
}
}
else
{
LoadTypesFromAssembly(context);
}
Debug.Assert(!context.KnownAssemblies.ContainsKey(context.CurrentAssembly), "This assembly must not be present in the list of known assemblies");
context.KnownAssemblies.Add(context.CurrentAssembly, false/*ReferencedAssembliesNotLoaded*/);
// When loading assembly from cache, the cache provide the implicit-dependency i.e. cross-reference by it's type to types in other assemblies.
// In the case where assembly is loaded for the first-time, the loading process ensures that implicitly-dependenent assemblies are loaded
foreach (Assembly referencedAssembly in context.AssemblyCacheEntry._referencedAssemblies)
{
if (!context.KnownAssemblies.ContainsKey(referencedAssembly))
{
// Update the current assembly that we are currently loading
context.UpdateCurrentAssembly(referencedAssembly, context.IsAssemblyAlreadyLoadedInCache);
areAssembliesLoadedFromCache |= InternalLoadAssemblyFromCache(context);
}
}
return areAssembliesLoadedFromCache;
}
///
/// Loads the set of types from the given assembly and adds it to the given list of types
///
/// context containing information for loading
private static void LoadTypesFromAssembly(LoadingContext context)
{
Debug.Assert(context.AssemblyCacheEntry._typesInAssembly.Count == 0);
LoadRelationshipTypes(context);
// Loop through each type in the assembly and process it
foreach (Type type in context.CurrentAssembly.GetTypes())
{
// If the type doesn't have the same EdmTypeAttribute defined, then it's not a special type
// that we care about, skip it.
if (!type.IsDefined(typeof(DataClasses.EdmTypeAttribute), false))
{
continue;
}
// Load the metadata for this type
LoadFromType(type, context);
}
}
///
/// Load metadata of the given type - when you call this method, you should check and make sure that the type has
/// edm attribute. If it doesn't,we won't load the type and it will be returned as null
///
///
///
///
private static EdmType LoadFromType(Type clrType, LoadingContext context)
{
EdmType edmType = null;
if (clrType.IsNested)
{
context.EdmItemError.Add(new EdmItemError(System.Data.Entity.Strings.NestedClassNotSupported(clrType.FullName, clrType.Assembly.FullName), null));
return null;
}
// Lookup for the specified type in the following structures:
// 1) typesInLoading - that describes the (*new*) types loaded so far by the current load-operation.
// 2) objectItemCollection.Items - describes the types that are already loaded in ObjectItemCollection
// 3) AssemblyCache - this describes a set of AssemblyCacheEntries containing 1) assembly types and 2) (implicitly) dependent assemblies
// iff type is present in assembly_cache, add it to typesInLoading.
if (!clrType.IsGenericType && context.IsTypeAlreadyLoaded(clrType, out edmType))
{
// Check to make sure the CLR type we got is the same as the given one
if (edmType.ClrType != clrType)
{
context.EdmItemError.Add(new EdmItemError(System.Data.Entity.Strings.NewTypeConflictsWithExistingType(
clrType.AssemblyQualifiedName, edmType.ClrType.AssemblyQualifiedName), edmType));
return null;
}
return edmType;
}
DataClasses.EdmTypeAttribute[] typeAttributes = (DataClasses.EdmTypeAttribute[])clrType.GetCustomAttributes(typeof(DataClasses.EdmTypeAttribute), false /*inherit*/);
// the CLR doesn't allow types to have duplicate/multiple attribute declarations
if (typeAttributes.Length != 0)
{
DataClasses.EdmTypeAttribute typeAttribute = typeAttributes[0];
string cspaceTypeName = String.IsNullOrEmpty(typeAttribute.Name) ? clrType.Name : typeAttribute.Name;
if(String.IsNullOrEmpty(typeAttribute.NamespaceName) && clrType.Namespace == null)
{
context.EdmItemError.Add(new EdmItemError(Strings.Validator_TypeHasNoNamespace, edmType));
return null;
}
string cspaceNamespaceName = String.IsNullOrEmpty(typeAttribute.NamespaceName) ? clrType.Namespace : typeAttribute.NamespaceName;
if (typeAttribute.GetType() == typeof(DataClasses.EdmEntityTypeAttribute))
{
edmType = new ClrEntityType(clrType, cspaceNamespaceName, cspaceTypeName);
}
else
{
Debug.Assert(typeAttribute.GetType() == typeof(DataClasses.EdmComplexTypeAttribute), "Invalid type attribute encountered");
edmType = new ClrComplexType(clrType, cspaceNamespaceName, cspaceTypeName);
}
}
else
{
return ResolveNonSchemaType(clrType, context);
}
// If type is not present in the current assembly, make sure you add the type's assembly in the list of referenced
// assembly for the current assembly. But we still have to load the type and all its dependent type
if (clrType.Assembly != context.CurrentAssembly)
{
// 1) Enqueue the "other" assembly for being loaded
// 2) Register the "other" assembly as a referenced-assembly of currentAssembly
// 3) and Load the specific type from "other" assembly for resolving the forward reference
if (ShouldFilterAssembly(clrType.Assembly.FullName) || !IsSchemaAttributePresent(clrType.Assembly))
{
context.EdmItemError.Add(new EdmItemError(System.Data.Entity.Strings.MissingAssemblyAttribute(
clrType.FullName, clrType.Assembly.FullName), edmType));
return null;
}
else
{
if (!context.AssemblyCacheEntry._referencedAssemblies.Contains(clrType.Assembly))
{
context.AssemblyCacheEntry._referencedAssemblies.Add(clrType.Assembly);
}
}
}
else
{
Debug.Assert(!context.AssemblyCacheEntry.ContainsType(edmType.Identity), "This type must not be already present in the list of types for this assembly");
// Also add this to the list of the types for this assembly
context.AssemblyCacheEntry._typesInAssembly.Add(edmType);
}
// Add this to the known type map so we won't try to load it again
context.TypesInLoading.Add(clrType.FullName, edmType);
// Load properties for structural type
if (Helper.IsStructuralType(edmType))
{
//Load base type only for entity type - not sure if we will allow complex type inheritance
if (Helper.IsEntityType(edmType))
{
edmType.BaseType = LoadFromType(clrType.BaseType, context);
}
// Load the properties for this type
LoadPropertiesFromType((StructuralType)edmType, context);
}
return edmType;
}
///
/// Load all the property metadata of the given type
///
/// The type where properties are loaded
///
private static void LoadPropertiesFromType(StructuralType structuralType, LoadingContext context)
{
// Look at both public, internal, and private instanced properties declared at this type, inherited members
// are not looked at. Internal and private properties are also looked at because they are also schematized fields
PropertyInfo[] properties = structuralType.ClrType.GetProperties(BindingFlags.DeclaredOnly |
BindingFlags.Instance |
BindingFlags.Public |
BindingFlags.NonPublic);
foreach (PropertyInfo property in properties)
{
EdmMember newMember = null;
bool isEntityKeyProperty = false; //used for EdmScalarProperties only
// EdmScalarPropertyAttribute, EdmComplexPropertyAttribute and EdmRelationshipNavigationPropertyAttribute
// are all EdmPropertyAttributes that we need to process. If the current property is not an EdmPropertyAttribute
// we will just ignore it and skip to the next property.
if (property.IsDefined(typeof(DataClasses.EdmRelationshipNavigationPropertyAttribute), false))
{
SaveNavigationProperty(structuralType, property, context);
}
else if (property.IsDefined(typeof(DataClasses.EdmScalarPropertyAttribute), false))
{
newMember = LoadScalarProperty(property, context, out isEntityKeyProperty);
}
else if (property.IsDefined(typeof(DataClasses.EdmComplexPropertyAttribute), false))
{
newMember = LoadComplexTypeProperty(property, context);
}
if (newMember == null)
{
// Property does not have one of the following attributes:
// EdmScalarPropertyAttribute, EdmComplexPropertyAttribute, EdmRelationshipNavigationPropertyAttribute
// This means its an unmapped property and can be ignored.
// Or there were error encountered while loading the properties
continue;
}
// Add the property object to the type
structuralType.AddMember(newMember);
// Add to the entity's collection of key members
// Do this here instead of in the if condition above for scalar properties because
// we want to make sure the AddMember call above did not fail before updating the key members
if (structuralType.BuiltInTypeKind == BuiltInTypeKind.EntityType && isEntityKeyProperty)
{
((EntityType)structuralType).AddKeyMember(newMember);
}
}
}
///
/// Loads metadata for the navigation properties
///
///
///
///
///
private static void SaveNavigationProperty(StructuralType declaringType, PropertyInfo property, LoadingContext context)
{
Debug.Assert(property.IsDefined(typeof(DataClasses.EdmRelationshipNavigationPropertyAttribute), false), "The property must have navigation property defined");
// EdmScalarPropertyAttribute, EdmComplexPropertyAttribute and EdmRelationshipNavigationPropertyAttribute
// are all EdmPropertyAttributes that we need to process. If the current property is not an EdmPropertyAttribute
// we will just ignore it and skip to the next property.
object[] relationshipPropertyAttributes = property.GetCustomAttributes(typeof(DataClasses.EdmRelationshipNavigationPropertyAttribute), false);
Debug.Assert(relationshipPropertyAttributes.Length == 1, "There should be exactly one property for every navigation property");
// Load the property type and create a new property object
EdmType propertyType = LoadFromType(property.PropertyType, context);
// The only valid return types from navigation properties are:
// (1) EntityType
// (2) CollectionType containing valid EntityType
// If LoadFromType returned null, it could mean that we couldn't validate any part of the type, or it could mean that it's a generic
// where the main generic type was validated, but the generic type parameter was not. We can't tell the difference, so just fail
// with the same error message in both cases. The user will have to figure out which part of the type is wrong.
// We can't just rely on checking for a generic because it can lead to a scenario where we report that the type parameter is invalid
// when really it's the main generic type. That is more confusing than reporting the full name and letting the user determine the problem.
if (propertyType == null || !(propertyType.BuiltInTypeKind == BuiltInTypeKind.EntityType || propertyType.BuiltInTypeKind == BuiltInTypeKind.CollectionType))
{
// Once an error is detected the property does not need to be validated further, just add to the errors
// collection and continue with the next property. The failure will cause an exception to be thrown later during validation of all of the types.
context.EdmItemError.Add(new EdmItemError(System.Data.Entity.Strings.Validator_OSpace_InvalidNavPropReturnType(property.Name, property.DeclaringType.FullName, property.PropertyType.FullName), null));
}
else
{
// else we have a valid EntityType or CollectionType that contains EntityType. ResolveNonSchemaType enforces that a collection type
// must contain an EntityType, and if it doesn't, propertyType will be null here. If propertyType is EntityType or CollectionType we know it is valid
// Expecting EdmRelationshipNavigationPropertyAttribute to have AllowMultiple=False, so only look at first element in the attribute array
DataClasses.EdmRelationshipNavigationPropertyAttribute attribute = (DataClasses.EdmRelationshipNavigationPropertyAttribute)relationshipPropertyAttributes[0];
context.UnresolvedNavigationProperties.Add(new NavigationPropertyInfo(declaringType, property, propertyType, attribute));
}
}
private static void ResolveNavigationProperties(LoadingContext context)
{
foreach (NavigationPropertyInfo info in context.UnresolvedNavigationProperties)
{
info.ResolveNavigationProperty(context);
}
}
///
/// Load the property with scalar property attribute
///
///
///
///
///
private static EdmMember LoadScalarProperty(PropertyInfo property, LoadingContext context, out bool isEntityKeyProperty)
{
Debug.Assert(property.IsDefined(typeof(DataClasses.EdmScalarPropertyAttribute), false), "The property must have a scalar attribute");
EdmMember member = null;
isEntityKeyProperty = false;
// Load the property type and create a new property object
EdmType propertyType = LoadFromType(property.PropertyType, context);
// If the type could not be loaded it's definitely not a primitive type, so that's an error
// If it could be loaded but is not a primitive that's an error as well
if (propertyType == null || propertyType.BuiltInTypeKind != BuiltInTypeKind.PrimitiveType)
{
// This property does not need to be validated further, just add to the errors collection and continue with the next property
// This failure will cause an exception to be thrown later during validation of all of the types
context.EdmItemError.Add(new EdmItemError(System.Data.Entity.Strings.Validator_OSpace_ScalarPropertyNotPrimitive(property.Name, property.DeclaringType.FullName, property.PropertyType.FullName), null));
}
else
{
object[] attrs = property.GetCustomAttributes(typeof(DataClasses.EdmScalarPropertyAttribute), false);
Debug.Assert(attrs.Length == 1, "Every property can exactly have one ScalarProperty Attribute");
// Expecting EdmScalarPropertyAttribute to have AllowMultiple=False, so only look at first element in the attribute array
isEntityKeyProperty = ((DataClasses.EdmScalarPropertyAttribute)attrs[0]).EntityKeyProperty;
bool isNullable = ((DataClasses.EdmScalarPropertyAttribute)attrs[0]).IsNullable;
member = new EdmProperty(property.Name,
TypeUsage.Create(propertyType, new FacetValues { Nullable = isNullable }),
property);
}
return member;
}
///
/// Load the property with complex type property attribute
///
///
///
///
private static EdmMember LoadComplexTypeProperty(PropertyInfo property, LoadingContext context)
{
// Load the property type and create a new property object
EdmType propertyType = LoadFromType(property.PropertyType, context);
// If the type could not be loaded it's definitely not a complex type, so that's an error
// If it could be loaded but is not a complex type that's an error as well
if (propertyType == null || propertyType.BuiltInTypeKind != BuiltInTypeKind.ComplexType)
{
// This property does not need to be validated further, just add to the errors collection and continue with the next property
// This failure will cause an exception to be thrown later during validation of all of the types
context.EdmItemError.Add(new EdmItemError(System.Data.Entity.Strings.Validator_OSpace_ComplexPropertyNotComplex(property.Name, property.DeclaringType.FullName, property.PropertyType.FullName), null));
}
else
{
EdmProperty newProperty = new EdmProperty(property.Name,
TypeUsage.Create(propertyType, new FacetValues { Nullable = false }),
property);
return newProperty;
}
return null;
}
///
/// Resolves the given non-schematized type by looking at what kind of type this is. Non-schematized types include
/// collection types, nullable types, reference types, and primitive types.
///
/// The non-schematized CLR type that is being resolved
/// A dictionary of types that we are currently loading
/// An EdmType object representing the type, null if we can't resolve it
private static EdmType ResolveNonSchemaType(Type clrType, LoadingContext context)
{
// Let's check to see if this type is a ref type, a nullable type, or a collection type, these are the types that
// we need to take special care of them
if (clrType.IsGenericType)
{
Type genericType = clrType.GetGenericTypeDefinition();
// Try to resolve the element type into a type object
EdmType elementType = LoadFromType(clrType.GetGenericArguments()[0], context);
if (elementType == null)
{
// return null and let the caller deal with the error handling
return null;
}
// Create the collection or reference type object or just a simple value that was wrapped inside a Nullable
if (genericType == typeof(Nullable<>))
{
// In here, the Nullable<> is unwrapped. In CDM, nullability is on a per property basis, there shouldn't be
// the notion of a nullable type
return elementType;
}
else if (genericType == typeof(DataClasses.EntityReference<>))
{
// EntityReference is IEnumerable, so we need to detect this condition and return here.
// We don't support EntityReference in this scenario, so just fail.
return null;
}
else if (typeof(IEnumerable).IsAssignableFrom(clrType))
{
EntityType entityType = elementType as EntityType;
if (entityType == null)
{
// return null and let the caller deal with the error handling
return null;
}
return entityType.GetCollectionType();
}
}
// For primitive types, look in clr provider manifest
PrimitiveType primitiveType;
if (ClrProviderManifest.Instance.TryGetPrimitiveType(clrType, out primitiveType))
{
return primitiveType;
}
return null;
}
///
/// This method loads all the relationship type that this entity takes part in
///
///
///
private static void LoadRelationshipTypes(LoadingContext context)
{
foreach (System.Data.Objects.DataClasses.EdmRelationshipAttribute roleAttribute in context.CurrentAssembly.GetCustomAttributes(typeof(System.Data.Objects.DataClasses.EdmRelationshipAttribute), false /*inherit*/))
{
// Check if there is an entry already with this name
if (TryFindNullParametersInRelationshipAttribute(roleAttribute, context))
{
// don't give more errors for these same bad parameters
continue;
}
bool errorEncountered = false;
// return error if the role names are the same
if (roleAttribute.Role1Name == roleAttribute.Role2Name)
{
context.EdmItemError.Add(new EdmItemError(System.Data.Entity.Strings.SameRoleNameOnRelationshipAttribute(roleAttribute.RelationshipName, roleAttribute.Role2Name),
null));
errorEncountered = true;
}
// Make sure the clr type specified are the same
EntityType type1;
if (!TryGetRelationshipEndEntityType(context, roleAttribute.Role1Type, out type1))
{
context.EdmItemError.Add(new EdmItemError(System.Data.Entity.Strings.RoleTypeInEdmRelationshipAttributeIsInvalidType(roleAttribute.RelationshipName, roleAttribute.Role1Name, roleAttribute.Role1Type),
null));
errorEncountered = true;
}
EntityType type2;
if (!TryGetRelationshipEndEntityType(context, roleAttribute.Role2Type, out type2))
{
context.EdmItemError.Add(new EdmItemError(System.Data.Entity.Strings.RoleTypeInEdmRelationshipAttributeIsInvalidType(roleAttribute.RelationshipName, roleAttribute.Role2Name, roleAttribute.Role2Type),
null));
errorEncountered = true;
}
if (!errorEncountered)
{
AssociationType associationType = new AssociationType(roleAttribute.RelationshipName, roleAttribute.RelationshipNamespaceName, DataSpace.OSpace);
associationType.AddKeyMember(new AssociationEndMember(roleAttribute.Role1Name, type1.GetReferenceType(), roleAttribute.Role1Multiplicity));
associationType.AddKeyMember(new AssociationEndMember(roleAttribute.Role2Name, type2.GetReferenceType(), roleAttribute.Role2Multiplicity));
context.TypesInLoading.Add(associationType.FullName, associationType);
// get assembly entry and add association type to the list of types in the assembly
Debug.Assert(!context.AssemblyCacheEntry.ContainsType(associationType.FullName), "Relationship type must not be present in the list of types");
context.AssemblyCacheEntry._typesInAssembly.Add(associationType);
}
}
}
private static bool TryFindNullParametersInRelationshipAttribute(System.Data.Objects.DataClasses.EdmRelationshipAttribute roleAttribute, LoadingContext context)
{
if (roleAttribute.RelationshipName == null)
{
context.EdmItemError.Add(new EdmItemError(System.Data.Entity.Strings.NullRelationshipNameforEdmRelationshipAttribute(context.CurrentAssembly.FullName), null));
return true;
}
bool nullsFound = false;
if (roleAttribute.RelationshipNamespaceName == null)
{
context.EdmItemError.Add(new EdmItemError(System.Data.Entity.Strings.NullParameterForEdmRelationshipAttribute(
"RelationshipNamespaceName", roleAttribute.RelationshipName), null));
nullsFound = true;
}
if (roleAttribute.Role1Name == null)
{
context.EdmItemError.Add(new EdmItemError(System.Data.Entity.Strings.NullParameterForEdmRelationshipAttribute(
"Role1Name", roleAttribute.RelationshipName), null));
nullsFound = true;
}
if (roleAttribute.Role1Type == null)
{
context.EdmItemError.Add(new EdmItemError(System.Data.Entity.Strings.NullParameterForEdmRelationshipAttribute(
"Role1Type", roleAttribute.RelationshipName), null));
nullsFound = true;
}
if (roleAttribute.Role2Name == null)
{
context.EdmItemError.Add(new EdmItemError(System.Data.Entity.Strings.NullParameterForEdmRelationshipAttribute(
"Role2Name", roleAttribute.RelationshipName), null));
nullsFound = true;
}
if (roleAttribute.Role2Type == null)
{
context.EdmItemError.Add(new EdmItemError(System.Data.Entity.Strings.NullParameterForEdmRelationshipAttribute(
"Role2Type", roleAttribute.RelationshipName), null));
nullsFound = true;
}
return nullsFound;
}
private static bool TryGetRelationshipEndEntityType(LoadingContext context, Type type, out EntityType entityType)
{
if (type == null)
{
entityType = null;
return false;
}
EdmType edmType = LoadFromType(type, context);
if (edmType == null || !Helper.IsEntityType(edmType))
{
entityType = null;
return false;
}
entityType = (EntityType)edmType;
return true;
}
#endregion
}
#endregion
}
}
// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
Link Menu

This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- Serializer.cs
- FixUp.cs
- DbBuffer.cs
- WindowsListViewGroup.cs
- PagesSection.cs
- MenuItemBinding.cs
- XmlStreamStore.cs
- DSASignatureDeformatter.cs
- Int64Converter.cs
- AppLevelCompilationSectionCache.cs
- EditorAttribute.cs
- AccessViolationException.cs
- NativeCppClassAttribute.cs
- Stack.cs
- EntityFunctions.cs
- SqlCacheDependencyDatabase.cs
- QueryStringHandler.cs
- OutputCacheSection.cs
- Logging.cs
- MessageTraceRecord.cs
- IApplicationTrustManager.cs
- XmlAttributes.cs
- TimeSpanConverter.cs
- baseaxisquery.cs
- ListViewInsertionMark.cs
- SqlDataSourceConfigureSelectPanel.cs
- Dictionary.cs
- ElementNotEnabledException.cs
- TextComposition.cs
- FrameworkName.cs
- NetPeerTcpBinding.cs
- PathFigureCollection.cs
- Console.cs
- TreeSet.cs
- SecurityChannel.cs
- WebServiceEnumData.cs
- MenuItemStyle.cs
- RegexStringValidatorAttribute.cs
- EmissiveMaterial.cs
- List.cs
- TextMetrics.cs
- xmlfixedPageInfo.cs
- ListBindableAttribute.cs
- XmlSchemaProviderAttribute.cs
- DynamicILGenerator.cs
- documentsequencetextpointer.cs
- AssociatedControlConverter.cs
- XmlNotation.cs
- SelectorItemAutomationPeer.cs
- Expressions.cs
- ConfigurationException.cs
- COMException.cs
- ScrollItemPattern.cs
- BitmapCache.cs
- TagNameToTypeMapper.cs
- Stackframe.cs
- FuncCompletionCallbackWrapper.cs
- InheritanceContextChangedEventManager.cs
- OracleInternalConnection.cs
- sitestring.cs
- ImageAttributes.cs
- TimerEventSubscription.cs
- StyleTypedPropertyAttribute.cs
- TimeSpanSecondsOrInfiniteConverter.cs
- StaticFileHandler.cs
- CodeDesigner.cs
- CroppedBitmap.cs
- StyleConverter.cs
- TextChangedEventArgs.cs
- KeyedCollection.cs
- AffineTransform3D.cs
- Registry.cs
- InfiniteTimeSpanConverter.cs
- SecurityTokenContainer.cs
- DataGridTextBoxColumn.cs
- WebSysDisplayNameAttribute.cs
- ProcessActivityTreeOptions.cs
- SQLByteStorage.cs
- GridItemPattern.cs
- TableRowGroup.cs
- ComAwareEventInfo.cs
- DesigntimeLicenseContextSerializer.cs
- SessionEndingCancelEventArgs.cs
- UnaryNode.cs
- ACE.cs
- SiteMapDesignerDataSourceView.cs
- BoolLiteral.cs
- CodeDOMProvider.cs
- AddInToken.cs
- DeobfuscatingStream.cs
- WinInet.cs
- TagPrefixAttribute.cs
- DataGridLinkButton.cs
- EntityDataSourceColumn.cs
- AssemblyResourceLoader.cs
- ReadOnlyPropertyMetadata.cs
- SqlParameterizer.cs
- ObjectConverter.cs
- PkcsUtils.cs
- ObjectAnimationUsingKeyFrames.cs