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 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761
|
//---------------------------------------------------------------------
// <copyright file="ObjectItemAttributeAssemblyLoader.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
//
// @owner Microsoft
// @backupOwner Microsoft
//---------------------------------------------------------------------
namespace System.Data.Metadata.Edm
{
using System.Collections.Generic;
using System.Data.Entity;
using System.Data.Objects.DataClasses;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
/// <summary>
/// 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
/// </summary>
internal sealed class ObjectItemAttributeAssemblyLoader : ObjectItemAssemblyLoader
{
#region Fields
// list of unresolved navigation properties
private readonly List<Action> _unresolvedNavigationProperties = new List<Action>();
private new MutableAssemblyCacheEntry CacheEntry { get { return (MutableAssemblyCacheEntry)base.CacheEntry; } }
private List<Action> _referenceResolutions = new List<Action>();
#endregion
#region Constructor
internal ObjectItemAttributeAssemblyLoader(Assembly assembly, ObjectItemLoadingSessionData sessionData)
:base(assembly, new MutableAssemblyCacheEntry(), sessionData)
{
Debug.Assert(Create == sessionData.ObjectItemAssemblyLoaderFactory, "Why is there a different factory creating this class");
}
#endregion
#region Methods
internal override void OnLevel1SessionProcessing()
{
foreach (Action resolve in _referenceResolutions)
{
resolve();
}
}
internal override void OnLevel2SessionProcessing()
{
foreach (Action resolve in _unresolvedNavigationProperties)
{
resolve();
}
}
/// <summary>
/// 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
/// </summary>
/// <param name="context"></param>
/// <returns>true if the assembly was already loaded in the cache</returns>
internal override void Load()
{
Debug.Assert(IsSchemaAttributePresent(SourceAssembly), "LoadAssembly shouldn't be called with assembly having no schema attribute");
Debug.Assert(!SessionData.KnownAssemblies.Contains(SourceAssembly, SessionData.ObjectItemAssemblyLoaderFactory, SessionData.EdmItemCollection), "InternalLoadAssemblyFromCache: This assembly must not be present in the list of known assemblies");
base.Load();
}
protected override void AddToAssembliesLoaded()
{
SessionData.AssembliesLoaded.Add(SourceAssembly, CacheEntry);
}
/// <summary>
/// Check to see if the type is already loaded - either in the typesInLoading, or ObjectItemCollection or
/// in the global cache
/// </summary>
/// <param name="clrType"></param>
/// <param name="edmType"></param>
/// <returns></returns>
private bool TryGetLoadedType(Type clrType, out EdmType edmType)
{
if (SessionData.TypesInLoading.TryGetValue(clrType.FullName, out edmType) ||
TryGetCachedEdmType(clrType, out edmType))
{
// Check to make sure the CLR type we got is the same as the given one
if (edmType.ClrType != clrType)
{
SessionData.EdmItemErrors.Add(new EdmItemError(System.Data.Entity.Strings.NewTypeConflictsWithExistingType(
clrType.AssemblyQualifiedName, edmType.ClrType.AssemblyQualifiedName), edmType));
edmType = null;
return false;
}
return true;
}
// 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;
if (!TryGetLoadedType(clrType.GetGenericArguments()[0], out elementType))
return false;
if (typeof(System.Collections.IEnumerable).IsAssignableFrom(clrType))
{
EntityType entityType = elementType as EntityType;
if (entityType == null)
{
// return null and let the caller deal with the error handling
return false;
}
edmType = entityType.GetCollectionType();
}
else
{
edmType = elementType;
}
return true;
}
edmType = null;
return false;
}
private bool TryGetCachedEdmType(Type clrType, out EdmType edmType)
{
Debug.Assert(!SessionData.TypesInLoading.ContainsKey(clrType.FullName), "This should be called only after looking in typesInLoading");
Debug.Assert(SessionData.EdmItemErrors.Count > 0 || // had an error during loading
clrType.GetCustomAttributes(typeof(EdmTypeAttribute), false /*inherit*/).Length == 0 || // not a type we track
SourceAssembly != clrType.Assembly, // not from this assembly
"Given that we don't have any error, if the type is part of this assembly, it should not be loaded from the cache");
ImmutableAssemblyCacheEntry immutableCacheEntry;
if (SessionData.LockedAssemblyCache.TryGetValue(clrType.Assembly, out immutableCacheEntry))
{
Debug.Assert(SessionData.KnownAssemblies.Contains(clrType.Assembly, SessionData.LoaderCookie, SessionData.EdmItemCollection), "We should only be loading things directly from the cache if they are already in the collection");
return immutableCacheEntry.TryGetEdmType(clrType.FullName, out edmType);
}
edmType = null;
return false;
}
#endregion
/// <summary>
/// Loads the set of types from the given assembly and adds it to the given list of types
/// </summary>
/// <param name="context">context containing information for loading</param>
protected override void LoadTypesFromAssembly()
{
Debug.Assert(CacheEntry.TypesInAssembly.Count == 0);
LoadRelationshipTypes();
// Loop through each type in the assembly and process it
foreach (Type type in EntityUtil.GetTypesSpecial(SourceAssembly))
{
// 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(EdmTypeAttribute), false))
{
continue;
}
// Generic type is not supported, if the user attributed this generic type using EdmTypeAttribute,
// then the exception message can help them better understand what is going on instead of just
// failing at a much later point of OC type mapping lookup with a super generic error message
if (type.IsGenericType)
{
SessionData.EdmItemErrors.Add(new EdmItemError(System.Data.Entity.Strings.GenericTypeNotSupported(type.FullName), null));
continue;
}
// Load the metadata for this type
LoadType(type);
}
if (_referenceResolutions.Count != 0)
{
SessionData.RegisterForLevel1PostSessionProcessing(this);
}
if (_unresolvedNavigationProperties.Count != 0)
{
SessionData.RegisterForLevel2PostSessionProcessing(this);
}
}
/// <summary>
/// This method loads all the relationship type that this entity takes part in
/// </summary>
/// <param name="entityType"></param>
/// <param name="context"></param>
private void LoadRelationshipTypes()
{
foreach (EdmRelationshipAttribute roleAttribute in SourceAssembly.GetCustomAttributes(typeof(EdmRelationshipAttribute), false /*inherit*/))
{
// Check if there is an entry already with this name
if (TryFindNullParametersInRelationshipAttribute(roleAttribute))
{
// 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)
{
SessionData.EdmItemErrors.Add(new EdmItemError(System.Data.Entity.Strings.SameRoleNameOnRelationshipAttribute(roleAttribute.RelationshipName, roleAttribute.Role2Name),
null));
errorEncountered = true;
}
if (!errorEncountered)
{
AssociationType associationType = new AssociationType(roleAttribute.RelationshipName, roleAttribute.RelationshipNamespaceName, roleAttribute.IsForeignKey, DataSpace.OSpace);
SessionData.TypesInLoading.Add(associationType.FullName, associationType);
TrackClosure(roleAttribute.Role1Type);
TrackClosure(roleAttribute.Role2Type);
// prevent lifting of loop vars
string r1Name = roleAttribute.Role1Name;
Type r1Type = roleAttribute.Role1Type;
RelationshipMultiplicity r1Multiplicity = roleAttribute.Role1Multiplicity;
AddTypeResolver(() =>
ResolveAssociationEnd(associationType, r1Name, r1Type, r1Multiplicity));
// prevent lifting of loop vars
string r2Name = roleAttribute.Role2Name;
Type r2Type = roleAttribute.Role2Type;
RelationshipMultiplicity r2Multiplicity = roleAttribute.Role2Multiplicity;
AddTypeResolver(() =>
ResolveAssociationEnd(associationType, r2Name, r2Type, r2Multiplicity));
// get assembly entry and add association type to the list of types in the assembly
Debug.Assert(!CacheEntry.ContainsType(associationType.FullName), "Relationship type must not be present in the list of types");
CacheEntry.TypesInAssembly.Add(associationType);
}
}
}
private void ResolveAssociationEnd(AssociationType associationType, string roleName, Type clrType, RelationshipMultiplicity multiplicity)
{
EntityType entityType;
if (!TryGetRelationshipEndEntityType(clrType, out entityType))
{
SessionData.EdmItemErrors.Add(new EdmItemError(System.Data.Entity.Strings.RoleTypeInEdmRelationshipAttributeIsInvalidType(associationType.Name, roleName, clrType),
null));
return;
}
associationType.AddKeyMember(new AssociationEndMember(roleName, entityType.GetReferenceType(), multiplicity));
}
/// <summary>
/// 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
/// </summary>
/// <param name="clrType"></param>
/// <param name="context"></param>
/// <returns></returns>
private void LoadType(Type clrType)
{
Debug.Assert(clrType.Assembly == SourceAssembly, "Why are we loading a type that is not in our assembly?");
Debug.Assert(!SessionData.TypesInLoading.ContainsKey(clrType.FullName), "Trying to load a type that is already loaded???");
Debug.Assert(!clrType.IsGenericType, "Generic type is not supported");
EdmType edmType = null;
EdmTypeAttribute[] typeAttributes = (EdmTypeAttribute[])clrType.GetCustomAttributes(typeof(EdmTypeAttribute), false /*inherit*/);
// the CLR doesn't allow types to have duplicate/multiple attribute declarations
if (typeAttributes.Length != 0)
{
if (clrType.IsNested)
{
SessionData.EdmItemErrors.Add(new EdmItemError(System.Data.Entity.Strings.NestedClassNotSupported(clrType.FullName, clrType.Assembly.FullName), null));
return;
}
EdmTypeAttribute typeAttribute = typeAttributes[0];
string cspaceTypeName = String.IsNullOrEmpty(typeAttribute.Name) ? clrType.Name : typeAttribute.Name;
if (String.IsNullOrEmpty(typeAttribute.NamespaceName) && clrType.Namespace == null)
{
SessionData.EdmItemErrors.Add(new EdmItemError(Strings.Validator_TypeHasNoNamespace, edmType));
return;
}
string cspaceNamespaceName = String.IsNullOrEmpty(typeAttribute.NamespaceName) ? clrType.Namespace : typeAttribute.NamespaceName;
if (typeAttribute.GetType() == typeof(EdmEntityTypeAttribute))
{
edmType = new ClrEntityType(clrType, cspaceNamespaceName, cspaceTypeName);
}
else if(typeAttribute.GetType() == typeof(EdmComplexTypeAttribute))
{
edmType = new ClrComplexType(clrType, cspaceNamespaceName, cspaceTypeName);
}
else
{
Debug.Assert(typeAttribute is EdmEnumTypeAttribute, "Invalid type attribute encountered");
// Note that TryGetPrimitiveType() will return false not only for types that are not primitive
// but also for CLR primitive types that are valid underlying enum types in CLR but are not
// a valid Edm primitive types (e.g. ulong)
PrimitiveType underlyingEnumType;
if (!ClrProviderManifest.Instance.TryGetPrimitiveType(clrType.GetEnumUnderlyingType(), out underlyingEnumType))
{
SessionData.EdmItemErrors.Add(
new EdmItemError(
Strings.Validator_UnsupportedEnumUnderlyingType(clrType.GetEnumUnderlyingType().FullName),
edmType));
return;
}
edmType = new ClrEnumType(clrType, cspaceNamespaceName, cspaceTypeName);
}
}
else
{
// not a type we are interested
return;
}
Debug.Assert(!CacheEntry.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
CacheEntry.TypesInAssembly.Add(edmType);
// Add this to the known type map so we won't try to load it again
SessionData.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))
{
TrackClosure(clrType.BaseType);
AddTypeResolver(
() => edmType.BaseType = ResolveBaseType(clrType.BaseType));
}
// Load the properties for this type
LoadPropertiesFromType((StructuralType)edmType);
}
return;
}
private void AddTypeResolver(Action resolver)
{
_referenceResolutions.Add(resolver);
}
private EdmType ResolveBaseType(Type type)
{
EdmType edmType;
if (type.GetCustomAttributes(typeof(EdmEntityTypeAttribute), false).Length > 0 && TryGetLoadedType(type, out edmType))
{
return edmType;
}
return null;
}
private bool TryFindNullParametersInRelationshipAttribute(EdmRelationshipAttribute roleAttribute)
{
if (roleAttribute.RelationshipName == null)
{
SessionData.EdmItemErrors.Add(new EdmItemError(System.Data.Entity.Strings.NullRelationshipNameforEdmRelationshipAttribute(SourceAssembly.FullName), null));
return true;
}
bool nullsFound = false;
if (roleAttribute.RelationshipNamespaceName == null)
{
SessionData.EdmItemErrors.Add(new EdmItemError(System.Data.Entity.Strings.NullParameterForEdmRelationshipAttribute(
"RelationshipNamespaceName", roleAttribute.RelationshipName), null));
nullsFound = true;
}
if (roleAttribute.Role1Name == null)
{
SessionData.EdmItemErrors.Add(new EdmItemError(System.Data.Entity.Strings.NullParameterForEdmRelationshipAttribute(
"Role1Name", roleAttribute.RelationshipName), null));
nullsFound = true;
}
if (roleAttribute.Role1Type == null)
{
SessionData.EdmItemErrors.Add(new EdmItemError(System.Data.Entity.Strings.NullParameterForEdmRelationshipAttribute(
"Role1Type", roleAttribute.RelationshipName), null));
nullsFound = true;
}
if (roleAttribute.Role2Name == null)
{
SessionData.EdmItemErrors.Add(new EdmItemError(System.Data.Entity.Strings.NullParameterForEdmRelationshipAttribute(
"Role2Name", roleAttribute.RelationshipName), null));
nullsFound = true;
}
if (roleAttribute.Role2Type == null)
{
SessionData.EdmItemErrors.Add(new EdmItemError(System.Data.Entity.Strings.NullParameterForEdmRelationshipAttribute(
"Role2Type", roleAttribute.RelationshipName), null));
nullsFound = true;
}
return nullsFound;
}
private bool TryGetRelationshipEndEntityType(Type type, out EntityType entityType)
{
if (type == null)
{
entityType = null;
return false;
}
EdmType edmType;
if (!TryGetLoadedType(type, out edmType) || !Helper.IsEntityType(edmType))
{
entityType = null;
return false;
}
entityType = (EntityType)edmType;
return true;
}
/// <summary>
/// Load all the property metadata of the given type
/// </summary>
/// <param name="type">The CLR entity type</param>
/// <param name="structuralType">The type where properties are loaded</param>
/// <param name="context"></param>
private void LoadPropertiesFromType(StructuralType structuralType)
{
// 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(PropertyReflectionBindingFlags);
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(EdmRelationshipNavigationPropertyAttribute), false))
{
// keep the loop var from being lifted
PropertyInfo pi = property;
_unresolvedNavigationProperties.Add(() =>
ResolveNavigationProperty(structuralType, pi));
}
else if (property.IsDefined(typeof(EdmScalarPropertyAttribute), false))
{
if ((Nullable.GetUnderlyingType(property.PropertyType) ?? property.PropertyType).IsEnum)
{
TrackClosure(property.PropertyType);
PropertyInfo local = property;
AddTypeResolver(() => ResolveEnumTypeProperty(structuralType, local));
}
else
{
newMember = LoadScalarProperty(structuralType.ClrType, property, out isEntityKeyProperty);
}
}
else if (property.IsDefined(typeof(EdmComplexPropertyAttribute), false))
{
TrackClosure(property.PropertyType);
// keep loop var from being lifted
PropertyInfo local = property;
AddTypeResolver(() => ResolveComplexTypeProperty(structuralType, local));
}
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 (Helper.IsEntityType(structuralType) && isEntityKeyProperty)
{
((EntityType)structuralType).AddKeyMember(newMember);
}
}
}
internal void ResolveNavigationProperty(StructuralType declaringType, PropertyInfo propertyInfo)
{
Debug.Assert(propertyInfo.IsDefined(typeof(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 = propertyInfo.GetCustomAttributes(typeof(EdmRelationshipNavigationPropertyAttribute), false);
Debug.Assert(relationshipPropertyAttributes.Length == 1, "There should be exactly one property for every navigation property");
// The only valid return types from navigation properties are:
// (1) EntityType
// (2) CollectionType containing valid EntityType
// If TryGetLoadedType returned false, 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.
EdmType propertyType;
if (!TryGetLoadedType(propertyInfo.PropertyType, out propertyType) || !(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.
SessionData.EdmItemErrors.Add(new EdmItemError(System.Data.Entity.Strings.Validator_OSpace_InvalidNavPropReturnType(propertyInfo.Name, propertyInfo.DeclaringType.FullName, propertyInfo.PropertyType.FullName), null));
return;
}
// 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
EdmRelationshipNavigationPropertyAttribute attribute = (EdmRelationshipNavigationPropertyAttribute)relationshipPropertyAttributes[0];
EdmMember member = null;
EdmType type;
if (SessionData.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
{
SessionData.EdmItemErrors.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)
{
SessionData.EdmItemErrors.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
{
SessionData.EdmItemErrors.Add(new EdmItemError(System.Data.Entity.Strings.RelationshipNameInNavigationPropertyNotValid(
propertyInfo.Name, propertyInfo.DeclaringType.FullName, attribute.RelationshipName), declaringType));
}
if (member != null)
{
declaringType.AddMember(member);
}
}
/// <summary>
/// Load the property with scalar property attribute.
/// Note that we pass the CLR type in because in the case where the property is declared on a generic
/// base class the DeclaringType of propert won't work for us and we need the real entity type instead.
/// </summary>
/// <param name="type">The CLR type of the entity</param>
/// <param name="property">Metadata representing the property</param>
/// <param name="isEntityKeyProperty">True if the property forms part of the entity's key</param>
/// <returns></returns>
private EdmMember LoadScalarProperty(Type clrType, PropertyInfo property, out bool isEntityKeyProperty)
{
Debug.Assert(property.IsDefined(typeof(EdmScalarPropertyAttribute), false), "The property must have a scalar attribute");
EdmMember member = null;
isEntityKeyProperty = false;
// Load the property type and create a new property object
PrimitiveType primitiveType;
// 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 (!TryGetPrimitiveType(property.PropertyType, out 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
SessionData.EdmItemErrors.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(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 = ((EdmScalarPropertyAttribute)attrs[0]).EntityKeyProperty;
bool isNullable = ((EdmScalarPropertyAttribute)attrs[0]).IsNullable;
member = new EdmProperty(property.Name,
TypeUsage.Create(primitiveType, new FacetValues { Nullable = isNullable }),
property, clrType.TypeHandle);
}
return member;
}
/// <summary>
/// Resolves enum type property.
/// </summary>
/// <param name="declaringType">The type to add the declared property to.</param>
/// <param name="clrProperty">Property to resolve.</param>
private void ResolveEnumTypeProperty(StructuralType declaringType, PropertyInfo clrProperty)
{
Debug.Assert(declaringType != null, "type != null");
Debug.Assert(clrProperty != null, "clrProperty != null");
Debug.Assert(
(Nullable.GetUnderlyingType(clrProperty.PropertyType) ?? clrProperty.PropertyType).IsEnum,
"This method should be called for enums only");
EdmType propertyType;
if (!TryGetLoadedType(clrProperty.PropertyType, out propertyType) || !Helper.IsEnumType(propertyType))
{
SessionData.EdmItemErrors.Add(
new EdmItemError(
System.Data.Entity.Strings.Validator_OSpace_ScalarPropertyNotPrimitive(
clrProperty.Name,
clrProperty.DeclaringType.FullName,
clrProperty.PropertyType.FullName), null));
}
else
{
var edmScalarPropertyAttribute = (EdmScalarPropertyAttribute)clrProperty.GetCustomAttributes(typeof(EdmScalarPropertyAttribute), false).Single();
EdmProperty enumProperty = new EdmProperty(
clrProperty.Name,
TypeUsage.Create(propertyType, new FacetValues() { Nullable = edmScalarPropertyAttribute.IsNullable }),
clrProperty,
declaringType.ClrType.TypeHandle);
declaringType.AddMember(enumProperty);
if (declaringType.BuiltInTypeKind == BuiltInTypeKind.EntityType && edmScalarPropertyAttribute.EntityKeyProperty)
{
((EntityType)declaringType).AddKeyMember(enumProperty);
}
}
}
private void ResolveComplexTypeProperty(StructuralType type, PropertyInfo clrProperty)
{
// Load the property type and create a new property object
EdmType propertyType;
// 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 (!TryGetLoadedType(clrProperty.PropertyType, out propertyType) || 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
SessionData.EdmItemErrors.Add(new EdmItemError(System.Data.Entity.Strings.Validator_OSpace_ComplexPropertyNotComplex(clrProperty.Name, clrProperty.DeclaringType.FullName, clrProperty.PropertyType.FullName), null));
}
else
{
EdmProperty newProperty = new EdmProperty(clrProperty.Name,
TypeUsage.Create(propertyType, new FacetValues { Nullable = false }),
clrProperty, type.ClrType.TypeHandle);
type.AddMember(newProperty);
}
}
private void TrackClosure(Type type)
{
if (SourceAssembly != type.Assembly &&
!CacheEntry.ClosureAssemblies.Contains(type.Assembly) &&
IsSchemaAttributePresent(type.Assembly) &&
!(type.IsGenericType &&
(
EntityUtil.IsAnICollection(type) || // EntityCollection<>, List<>, ICollection<>
type.GetGenericTypeDefinition() == typeof(System.Data.Objects.DataClasses.EntityReference<>) ||
type.GetGenericTypeDefinition() == typeof(System.Nullable<>)
)
)
)
{
CacheEntry.ClosureAssemblies.Add(type.Assembly);
}
if (type.IsGenericType)
{
foreach (Type genericArgument in type.GetGenericArguments())
{
TrackClosure(genericArgument);
}
}
}
internal static bool IsSchemaAttributePresent(Assembly assembly)
{
return assembly.IsDefined(typeof(EdmSchemaAttribute), false /*inherit*/);
}
internal static ObjectItemAssemblyLoader Create(Assembly assembly, ObjectItemLoadingSessionData sessionData)
{
if (ObjectItemAttributeAssemblyLoader.IsSchemaAttributePresent(assembly))
{
return new ObjectItemAttributeAssemblyLoader(assembly, sessionData);
}
else
{
return new ObjectItemNoOpAssemblyLoader(assembly, sessionData);
}
}
}
}
|