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 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907
|
//---------------------------------------------------------------------
// <copyright file="StorageMappingItemCollection.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
//
// @owner Microsoft
// @backupOwner Microsoft
//---------------------------------------------------------------------
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Data.Common.Utils;
using System.Data.Entity;
using System.Data.Mapping.Update.Internal;
using System.Data.Mapping.ViewGeneration;
using System.Data.Metadata.Edm;
using System.Diagnostics;
using System.Linq;
using System.Runtime.Versioning;
using System.Xml;
using som = System.Data.EntityModel.SchemaObjectModel;
namespace System.Data.Mapping
{
using OfTypeQVCacheKey = Pair<EntitySetBase, Pair<EntityTypeBase, bool>>;
/// <summary>
/// Class for representing a collection of items in Storage Mapping( CS Mapping) space.
/// </summary>
[CLSCompliant(false)]
public partial class StorageMappingItemCollection : MappingItemCollection
{
#region Fields
//EdmItemCollection that is associated with the MSL Loader.
private EdmItemCollection m_edmCollection;
//StoreItemCollection that is associated with the MSL Loader.
private StoreItemCollection m_storeItemCollection;
private ViewDictionary m_viewDictionary;
private double m_mappingVersion = XmlConstants.UndefinedVersion;
private MetadataWorkspace m_workspace;
// In this version, we won't allow same types in CSpace to map to different types in store. If the same type
// need to be reused, the store type must be the same. To keep track of this, we need to keep track of the member
// mapping across maps to make sure they are mapped to the same store side.
// The first TypeUsage in the KeyValuePair stores the store equivalent type for the cspace member type and the second
// one store the actual store type to which the member is mapped to.
// For e.g. If the CSpace member of type Edm.Int32 maps to a sspace member of type SqlServer.bigint, then the KeyValuePair
// for the cspace member will contain SqlServer.int (store equivalent for Edm.Int32) and SqlServer.bigint (Actual store type
// to which the member was mapped to)
private Dictionary<EdmMember, KeyValuePair<TypeUsage, TypeUsage>> m_memberMappings = new Dictionary<EdmMember, KeyValuePair<TypeUsage, TypeUsage>>();
private ViewLoader _viewLoader;
internal enum InterestingMembersKind
{
RequiredOriginalValueMembers, // legacy - used by the obsolete GetRequiredOriginalValueMembers
FullUpdate, // Interesting members in case of full update scenario
PartialUpdate // Interesting members in case of partial update scenario
};
private ConcurrentDictionary<Tuple<EntitySetBase, EntityTypeBase, InterestingMembersKind>, ReadOnlyCollection<EdmMember>> _cachedInterestingMembers =
new ConcurrentDictionary<Tuple<EntitySetBase, EntityTypeBase, InterestingMembersKind>, ReadOnlyCollection<EdmMember>>();
#endregion
#region Constructors
/// <summary>
/// constructor that takes in a list of folder or files or a mix of both and
/// creates metadata for mapping in all the files.
/// </summary>
/// <param name="edmCollection"></param>
/// <param name="storeCollection"></param>
/// <param name="filePaths"></param>
[ResourceExposure(ResourceScope.Machine)] //Exposes the file path names which are a Machine resource
[ResourceConsumption(ResourceScope.Machine)] //For MetadataArtifactLoader.CreateCompositeFromFilePaths method call but we do not create the file paths in this method
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors"), System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "edm")]
public StorageMappingItemCollection(EdmItemCollection edmCollection, StoreItemCollection storeCollection,
params string[] filePaths)
: base(DataSpace.CSSpace)
{
EntityUtil.CheckArgumentNull(edmCollection, "edmCollection");
EntityUtil.CheckArgumentNull(storeCollection, "storeCollection");
EntityUtil.CheckArgumentNull(filePaths, "filePaths");
this.m_edmCollection = edmCollection;
this.m_storeItemCollection = storeCollection;
// Wrap the file paths in instances of the MetadataArtifactLoader class, which provides
// an abstraction and a uniform interface over a diverse set of metadata artifacts.
//
MetadataArtifactLoader composite = null;
List<XmlReader> readers = null;
try
{
composite = MetadataArtifactLoader.CreateCompositeFromFilePaths(filePaths, XmlConstants.CSSpaceSchemaExtension);
readers = composite.CreateReaders(DataSpace.CSSpace);
this.Init(edmCollection, storeCollection, readers,
composite.GetPaths(DataSpace.CSSpace), true /*throwOnError*/);
}
finally
{
if (readers != null)
{
Helper.DisposeXmlReaders(readers);
}
}
}
/// <summary>
/// constructor that takes in a list of XmlReaders and creates metadata for mapping
/// in all the files.
/// </summary>
/// <param name="edmCollection">The edm metadata collection that this mapping is to use</param>
/// <param name="storeCollection">The store metadata collection that this mapping is to use</param>
/// <param name="xmlReaders">The XmlReaders to load mapping from</param>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors"), System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "edm")]
public StorageMappingItemCollection(EdmItemCollection edmCollection,
StoreItemCollection storeCollection,
IEnumerable<XmlReader> xmlReaders)
: base(DataSpace.CSSpace)
{
EntityUtil.CheckArgumentNull(xmlReaders, "xmlReaders");
MetadataArtifactLoader composite = MetadataArtifactLoader.CreateCompositeFromXmlReaders(xmlReaders);
this.Init(edmCollection,
storeCollection,
composite.GetReaders(), // filter out duplicates
composite.GetPaths(),
true /* throwOnError*/);
}
/// <summary>
/// constructor that takes in a list of XmlReaders and creates metadata for mapping
/// in all the files.
/// </summary>
/// <param name="edmCollection">The edm metadata collection that this mapping is to use</param>
/// <param name="storeCollection">The store metadata collection that this mapping is to use</param>
/// <param name="filePaths">Mapping URIs</param>
/// <param name="xmlReaders">The XmlReaders to load mapping from</param>
/// <param name="errors">a list of errors for each file loaded</param>
// referenced by System.Data.Entity.Design.dll
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
internal StorageMappingItemCollection(EdmItemCollection edmCollection,
StoreItemCollection storeCollection,
IEnumerable<XmlReader> xmlReaders,
List<string> filePaths,
out IList<EdmSchemaError> errors)
: base(DataSpace.CSSpace)
{
// we will check the parameters for this internal ctor becuase
// it is pretty much publicly exposed through the MetadataItemCollectionFactory
// in System.Data.Entity.Design
EntityUtil.CheckArgumentNull(xmlReaders, "xmlReaders");
EntityUtil.CheckArgumentContainsNull(ref xmlReaders, "xmlReaders");
// filePaths is allowed to be null
errors = this.Init(edmCollection, storeCollection, xmlReaders, filePaths, false /*throwOnError*/);
}
/// <summary>
/// constructor that takes in a list of XmlReaders and creates metadata for mapping
/// in all the files.
/// </summary>
/// <param name="edmCollection">The edm metadata collection that this mapping is to use</param>
/// <param name="storeCollection">The store metadata collection that this mapping is to use</param>
/// <param name="filePaths">Mapping URIs</param>
/// <param name="xmlReaders">The XmlReaders to load mapping from</param>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
internal StorageMappingItemCollection(EdmItemCollection edmCollection,
StoreItemCollection storeCollection,
IEnumerable<XmlReader> xmlReaders,
List<string> filePaths)
: base(DataSpace.CSSpace)
{
this.Init(edmCollection, storeCollection, xmlReaders, filePaths, true /*throwOnError*/);
}
/// <summary>
/// Initializer that takes in a list of XmlReaders and creates metadata for mapping
/// in all the files.
/// </summary>
/// <param name="edmCollection">The edm metadata collection that this mapping is to use</param>
/// <param name="storeCollection">The store metadata collection that this mapping is to use</param>
/// <param name="filePaths">Mapping URIs</param>
/// <param name="xmlReaders">The XmlReaders to load mapping from</param>
/// <param name="errors">a list of errors for each file loaded</param>
private IList<EdmSchemaError> Init(EdmItemCollection edmCollection,
StoreItemCollection storeCollection,
IEnumerable<XmlReader> xmlReaders,
List<string> filePaths,
bool throwOnError)
{
EntityUtil.CheckArgumentNull(xmlReaders, "xmlReaders");
EntityUtil.CheckArgumentNull(edmCollection, "edmCollection");
EntityUtil.CheckArgumentNull(storeCollection, "storeCollection");
this.m_edmCollection = edmCollection;
this.m_storeItemCollection = storeCollection;
Dictionary<EntitySetBase, GeneratedView> userDefinedQueryViewsDict;
Dictionary<OfTypeQVCacheKey, GeneratedView> userDefinedQueryViewsOfTypeDict;
this.m_viewDictionary = new ViewDictionary(this, out userDefinedQueryViewsDict, out userDefinedQueryViewsOfTypeDict);
List<EdmSchemaError> errors = new List<EdmSchemaError>();
if(this.m_edmCollection.EdmVersion != XmlConstants.UndefinedVersion &&
this.m_storeItemCollection.StoreSchemaVersion != XmlConstants.UndefinedVersion &&
this.m_edmCollection.EdmVersion != this.m_storeItemCollection.StoreSchemaVersion)
{
errors.Add(
new EdmSchemaError(
Strings.Mapping_DifferentEdmStoreVersion,
(int)StorageMappingErrorCode.MappingDifferentEdmStoreVersion, EdmSchemaErrorSeverity.Error));
}
else
{
double expectedVersion = this.m_edmCollection.EdmVersion != XmlConstants.UndefinedVersion
? this.m_edmCollection.EdmVersion
: this.m_storeItemCollection.StoreSchemaVersion;
errors.AddRange(LoadItems(xmlReaders, filePaths, userDefinedQueryViewsDict, userDefinedQueryViewsOfTypeDict, expectedVersion));
}
Debug.Assert(errors != null);
if (errors.Count > 0 && throwOnError)
{
if (!System.Data.Common.Utils.MetadataHelper.CheckIfAllErrorsAreWarnings(errors))
{
// NOTE: not using Strings.InvalidSchemaEncountered because it will truncate the errors list.
throw new MappingException(
String.Format(System.Globalization.CultureInfo.CurrentCulture,
EntityRes.GetString(EntityRes.InvalidSchemaEncountered),
Helper.CombineErrorMessage(errors)));
}
}
return errors;
}
#endregion Constructors
internal MetadataWorkspace Workspace
{
get
{
if (m_workspace == null)
{
m_workspace = new MetadataWorkspace();
m_workspace.RegisterItemCollection(m_edmCollection);
m_workspace.RegisterItemCollection(m_storeItemCollection);
m_workspace.RegisterItemCollection(this);
}
return m_workspace;
}
}
/// <summary>
/// Return the EdmItemCollection associated with the Mapping Collection
/// </summary>
internal EdmItemCollection EdmItemCollection
{
get
{
return this.m_edmCollection;
}
}
/// <summary>
/// Version of this StorageMappingItemCollection represents.
/// </summary>
public double MappingVersion
{
get
{
return this.m_mappingVersion;
}
}
/// <summary>
/// Return the StoreItemCollection associated with the Mapping Collection
/// </summary>
internal StoreItemCollection StoreItemCollection
{
get
{
return this.m_storeItemCollection;
}
}
/// <summary>
/// Search for a Mapping metadata with the specified type key.
/// </summary>
/// <param name="identity">identity of the type</param>
/// <param name="typeSpace">The dataspace that the type for which map needs to be returned belongs to</param>
/// <param name="ignoreCase">true for case-insensitive lookup</param>
/// <exception cref="ArgumentException"> Thrown if mapping space is not valid</exception>
internal override Map GetMap(string identity, DataSpace typeSpace, bool ignoreCase)
{
EntityUtil.CheckArgumentNull(identity, "identity");
if (typeSpace != DataSpace.CSpace)
{
throw EntityUtil.InvalidOperation(System.Data.Entity.Strings.Mapping_Storage_InvalidSpace(typeSpace));
}
return GetItem<Map>(identity, ignoreCase);
}
/// <summary>
/// Search for a Mapping metadata with the specified type key.
/// </summary>
/// <param name="identity">identity of the type</param>
/// <param name="typeSpace">The dataspace that the type for which map needs to be returned belongs to</param>
/// <param name="ignoreCase">true for case-insensitive lookup</param>
/// <param name="map"></param>
/// <returns>Returns false if no match found.</returns>
internal override bool TryGetMap(string identity, DataSpace typeSpace, bool ignoreCase, out Map map)
{
if (typeSpace != DataSpace.CSpace)
{
throw EntityUtil.InvalidOperation(System.Data.Entity.Strings.Mapping_Storage_InvalidSpace(typeSpace));
}
return TryGetItem<Map>(identity, ignoreCase, out map);
}
/// <summary>
/// Search for a Mapping metadata with the specified type key.
/// </summary>
/// <param name="identity">identity of the type</param>
/// <param name="typeSpace">The dataspace that the type for which map needs to be returned belongs to</param>
/// <exception cref="ArgumentException"> Thrown if mapping space is not valid</exception>
internal override Map GetMap(string identity, DataSpace typeSpace)
{
return this.GetMap(identity, typeSpace, false /*ignoreCase*/);
}
/// <summary>
/// Search for a Mapping metadata with the specified type key.
/// </summary>
/// <param name="identity">identity of the type</param>
/// <param name="typeSpace">The dataspace that the type for which map needs to be returned belongs to</param>
/// <param name="map"></param>
/// <returns>Returns false if no match found.</returns>
internal override bool TryGetMap(string identity, DataSpace typeSpace, out Map map)
{
return this.TryGetMap(identity, typeSpace, false /*ignoreCase*/, out map);
}
/// <summary>
/// Search for a Mapping metadata with the specified type key.
/// </summary>
/// <param name="item"></param>
internal override Map GetMap(GlobalItem item)
{
EntityUtil.CheckArgumentNull(item, "item");
DataSpace typeSpace = item.DataSpace;
if (typeSpace != DataSpace.CSpace)
{
throw EntityUtil.InvalidOperation(System.Data.Entity.Strings.Mapping_Storage_InvalidSpace(typeSpace));
}
return this.GetMap(item.Identity, typeSpace);
}
/// <summary>
/// Search for a Mapping metadata with the specified type key.
/// </summary>
/// <param name="item"></param>
/// <param name="map"></param>
/// <returns>Returns false if no match found.</returns>
internal override bool TryGetMap(GlobalItem item, out Map map)
{
if (item == null)
{
map = null;
return false;
}
DataSpace typeSpace = item.DataSpace;
if (typeSpace != DataSpace.CSpace)
{
map = null;
return false;
}
return this.TryGetMap(item.Identity, typeSpace, out map);
}
/// <summary>
/// This method
/// - generates views from the mapping elements in the collection;
/// - does not process user defined views - these are processed during mapping collection loading;
/// - does not cache generated views in the mapping collection.
/// The main purpose is design-time view validation and generation.
/// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] // referenced by System.Data.Entity.Design.dll
internal Dictionary<EntitySetBase, string> GenerateEntitySetViews(out IList<EdmSchemaError> errors)
{
Dictionary<EntitySetBase, string> esqlViews = new Dictionary<EntitySetBase, string>();
errors = new List<EdmSchemaError>();
foreach (var mapping in GetItems<Map>())
{
var entityContainerMapping = mapping as StorageEntityContainerMapping;
if (entityContainerMapping != null)
{
// If there are no entity set maps, don't call the view generation process.
if (!entityContainerMapping.HasViews)
{
return esqlViews;
}
// If entityContainerMapping contains only query views, then add a warning to the errors and continue to next mapping.
if (!entityContainerMapping.HasMappingFragments())
{
Debug.Assert(2088 == (int)StorageMappingErrorCode.MappingAllQueryViewAtCompileTime, "Please change the ERRORCODE_MAPPINGALLQUERYVIEWATCOMPILETIME value as well");
errors.Add(new EdmSchemaError(
Strings.Mapping_AllQueryViewAtCompileTime(entityContainerMapping.Identity),
(int)StorageMappingErrorCode.MappingAllQueryViewAtCompileTime,
EdmSchemaErrorSeverity.Warning));
}
else
{
ViewGenResults viewGenResults = ViewgenGatekeeper.GenerateViewsFromMapping(entityContainerMapping, new ConfigViewGenerator() { GenerateEsql = true });
if (viewGenResults.HasErrors)
{
((List<EdmSchemaError>)errors).AddRange(viewGenResults.Errors);
}
KeyToListMap<EntitySetBase, GeneratedView> extentMappingViews = viewGenResults.Views;
foreach (KeyValuePair<EntitySetBase, List<GeneratedView>> extentViewPair in extentMappingViews.KeyValuePairs)
{
List<GeneratedView> generatedViews = extentViewPair.Value;
// Multiple Views are returned for an extent but the first view
// is the only one that we will use for now. In the future,
// we might start using the other views which are per type within an extent.
esqlViews.Add(extentViewPair.Key, generatedViews[0].eSQL);
}
}
}
}
return esqlViews;
}
#region Get interesting members
/// <summary>
/// Return members for MetdataWorkspace.GetRequiredOriginalValueMembers() and MetdataWorkspace.GetRelevantMembersForUpdate() methods.
/// </summary>
/// <param name="entitySet">An EntitySet belonging to the C-Space. Must not be null.</param>
/// <param name="entityType">An EntityType that participates in the given EntitySet. Must not be null.</param>
/// <param name="interestingMembersKind">Scenario the members should be returned for.</param>
/// <returns>ReadOnlyCollection of interesting members for the requested scenario (<paramref name="interestingMembersKind"/>).</returns>
internal ReadOnlyCollection<EdmMember> GetInterestingMembers(EntitySetBase entitySet, EntityTypeBase entityType, InterestingMembersKind interestingMembersKind)
{
Debug.Assert(entitySet != null, "entitySet != null");
Debug.Assert(entityType != null, "entityType != null");
var key = new Tuple<EntitySetBase, EntityTypeBase, InterestingMembersKind>(entitySet, entityType, interestingMembersKind);
return _cachedInterestingMembers.GetOrAdd(key, FindInterestingMembers(entitySet, entityType, interestingMembersKind));
}
/// <summary>
/// Finds interesting members for MetdataWorkspace.GetRequiredOriginalValueMembers() and MetdataWorkspace.GetRelevantMembersForUpdate() methods
/// for the given <paramref name="entitySet"/> and <paramref name="entityType"/>.
/// </summary>
/// <param name="entitySet">An EntitySet belonging to the C-Space. Must not be null.</param>
/// <param name="entityType">An EntityType that participates in the given EntitySet. Must not be null.</param>
/// <param name="interestingMembersKind">Scenario the members should be returned for.</param>
/// <returns>ReadOnlyCollection of interesting members for the requested scenario (<paramref name="interestingMembersKind"/>).</returns>
private ReadOnlyCollection<EdmMember> FindInterestingMembers(EntitySetBase entitySet, EntityTypeBase entityType, InterestingMembersKind interestingMembersKind)
{
Debug.Assert(entitySet != null, "entitySet != null");
Debug.Assert(entityType != null, "entityType != null");
var interestingMembers = new List<EdmMember>();
foreach (var storageTypeMapping in MappingMetadataHelper.GetMappingsForEntitySetAndSuperTypes(this, entitySet.EntityContainer, entitySet, entityType))
{
StorageAssociationTypeMapping associationTypeMapping = storageTypeMapping as StorageAssociationTypeMapping;
if (associationTypeMapping != null)
{
FindInterestingAssociationMappingMembers(associationTypeMapping, interestingMembers);
}
else
{
Debug.Assert(storageTypeMapping is StorageEntityTypeMapping, "StorageEntityTypeMapping expected.");
FindInterestingEntityMappingMembers((StorageEntityTypeMapping)storageTypeMapping, interestingMembersKind, interestingMembers);
}
}
// For backwards compatibility we don't return foreign keys from the obsolete MetadataWorkspace.GetRequiredOriginalValueMembers() method
if (interestingMembersKind != InterestingMembersKind.RequiredOriginalValueMembers)
{
FindForeignKeyProperties(entitySet, entityType, interestingMembers);
}
foreach (var functionMappings in MappingMetadataHelper
.GetModificationFunctionMappingsForEntitySetAndType(this, entitySet.EntityContainer, entitySet, entityType)
.Where(functionMappings => functionMappings.UpdateFunctionMapping != null))
{
FindInterestingFunctionMappingMembers(functionMappings, interestingMembersKind, ref interestingMembers);
}
Debug.Assert(interestingMembers != null, "interestingMembers must never be null.");
return new ReadOnlyCollection<EdmMember>(interestingMembers.Distinct().ToList());
}
/// <summary>
/// Finds members participating in the assocciation and adds them to the <paramref name="interestingMembers"/>.
/// </summary>
/// <param name="associationTypeMapping">Association type mapping. Must not be null.</param>
/// <param name="interestingMembers">The list the interesting members (if any) will be added to. Must not be null.</param>
private static void FindInterestingAssociationMappingMembers(StorageAssociationTypeMapping associationTypeMapping, List<EdmMember> interestingMembers)
{
Debug.Assert(associationTypeMapping != null, "entityTypeMapping != null");
Debug.Assert(interestingMembers != null, "interestingMembers != null");
//(2) Ends participating in association are "interesting"
interestingMembers.AddRange(
associationTypeMapping
.MappingFragments
.SelectMany(m => m.AllProperties)
.OfType<StorageEndPropertyMapping>()
.Select(epm => epm.EndMember));
}
/// <summary>
/// Finds interesting entity properties - primary keys (if requested), properties (including complex properties and nested properties)
/// with concurrency mode set to fixed and C-Side condition members and adds them to the <paramref name="interestingMembers"/>.
/// </summary>
/// <param name="entityTypeMapping">Entity type mapping. Must not be null.</param>
/// <param name="interestingMembersKind">Scenario the members should be returned for.</param>
/// <param name="interestingMembers">The list the interesting members (if any) will be added to. Must not be null.</param>
private static void FindInterestingEntityMappingMembers(StorageEntityTypeMapping entityTypeMapping, InterestingMembersKind interestingMembersKind, List<EdmMember> interestingMembers)
{
Debug.Assert(entityTypeMapping != null, "entityTypeMapping != null");
Debug.Assert(interestingMembers != null, "interestingMembers != null");
foreach (var propertyMapping in entityTypeMapping.MappingFragments.SelectMany(mf => mf.AllProperties))
{
StorageScalarPropertyMapping scalarPropMapping = propertyMapping as StorageScalarPropertyMapping;
StorageComplexPropertyMapping complexPropMapping = propertyMapping as StorageComplexPropertyMapping;
StorageConditionPropertyMapping conditionMapping = propertyMapping as StorageConditionPropertyMapping;
Debug.Assert(!(propertyMapping is StorageEndPropertyMapping), "association mapping properties should be handled elsewhere.");
Debug.Assert(scalarPropMapping != null ||
complexPropMapping != null ||
conditionMapping != null, "Unimplemented property mapping");
//scalar property
if (scalarPropMapping != null && scalarPropMapping.EdmProperty != null)
{
// (0) if a member is part of the key it is interesting
if (MetadataHelper.IsPartOfEntityTypeKey(scalarPropMapping.EdmProperty))
{
// For backwards compatibility we do return primary keys from the obsolete MetadataWorkspace.GetRequiredOriginalValueMembers() method
if (interestingMembersKind == InterestingMembersKind.RequiredOriginalValueMembers)
{
interestingMembers.Add(scalarPropMapping.EdmProperty);
}
}
//(3) if a scalar property has Fixed concurrency mode then it is "interesting"
else if (MetadataHelper.GetConcurrencyMode(scalarPropMapping.EdmProperty) == ConcurrencyMode.Fixed)
{
interestingMembers.Add(scalarPropMapping.EdmProperty);
}
}
else if (complexPropMapping != null)
{
// (7) All complex members - partial update scenarios only
// (3.1) The complex property or its one of its children has fixed concurrency mode
if (interestingMembersKind == InterestingMembersKind.PartialUpdate ||
MetadataHelper.GetConcurrencyMode(complexPropMapping.EdmProperty) == ConcurrencyMode.Fixed || HasFixedConcurrencyModeInAnyChildProperty(complexPropMapping))
{
interestingMembers.Add(complexPropMapping.EdmProperty);
}
}
else if (conditionMapping != null)
{
//(1) C-Side condition members are 'interesting'
if (conditionMapping.EdmProperty != null)
{
interestingMembers.Add(conditionMapping.EdmProperty);
}
}
}
}
/// <summary>
/// Recurses down the complex property to find whether any of the nseted properties has concurrency mode set to "Fixed"
/// </summary>
/// <param name="complexMapping">Complex property mapping. Must not be null.</param>
/// <returns><c>true</c> if any of the descendant properties has concurrency mode set to "Fixed". Otherwise <c>false</c>.</returns>
private static bool HasFixedConcurrencyModeInAnyChildProperty(StorageComplexPropertyMapping complexMapping)
{
Debug.Assert(complexMapping != null, "complexMapping != null");
foreach (StoragePropertyMapping propertyMapping in complexMapping.TypeMappings.SelectMany(m => m.AllProperties))
{
StorageScalarPropertyMapping childScalarPropertyMapping = propertyMapping as StorageScalarPropertyMapping;
StorageComplexPropertyMapping childComplexPropertyMapping = propertyMapping as StorageComplexPropertyMapping;
Debug.Assert(childScalarPropertyMapping != null ||
childComplexPropertyMapping != null, "Unimplemented property mapping for complex property");
//scalar property and has Fixed CC mode
if (childScalarPropertyMapping != null && MetadataHelper.GetConcurrencyMode(childScalarPropertyMapping.EdmProperty) == ConcurrencyMode.Fixed)
{
return true;
}
// Complex Prop and sub-properties or itself has fixed CC mode
else if (childComplexPropertyMapping != null &&
(MetadataHelper.GetConcurrencyMode(childComplexPropertyMapping.EdmProperty) == ConcurrencyMode.Fixed
|| HasFixedConcurrencyModeInAnyChildProperty(childComplexPropertyMapping)))
{
return true;
}
}
return false;
}
/// <summary>
/// Finds foreign key properties and adds them to the <paramref name="interestingMembers"/>.
/// </summary>
/// <param name="entitySetBase">Entity set <paramref name="entityType"/> relates to. Must not be null.</param>
/// <param name="entityType">Entity type for which to find foreign key properties. Must not be null.</param>
/// <param name="interestingMembers">The list the interesting members (if any) will be added to. Must not be null.</param>
private void FindForeignKeyProperties(EntitySetBase entitySetBase, EntityTypeBase entityType, List<EdmMember> interestingMembers)
{
var entitySet = entitySetBase as EntitySet;
if (entitySet != null && entitySet.HasForeignKeyRelationships)
{
// (6) Foreign keys
// select all foreign key properties defined on the entityType and all its ancestors
interestingMembers.AddRange(
MetadataHelper.GetTypeAndParentTypesOf(entityType, this.m_edmCollection, true)
.SelectMany(e => ((EntityType)e).Properties)
.Where(p => entitySet.ForeignKeyDependents.SelectMany(fk => fk.Item2.ToProperties).Contains(p)));
}
}
/// <summary>
/// Finds interesting members for modification functions mapped to stored procedures and adds them to the <paramref name="interestingMembers"/>.
/// </summary>
/// <param name="functionMappings">Modification function mapping. Must not be null.</param>
/// <param name="interestingMembersKind">Update scenario the members will be used in (in general - partial update vs. full update).</param>
/// <param name="interestingMembers"></param>
private static void FindInterestingFunctionMappingMembers(StorageEntityTypeModificationFunctionMapping functionMappings, InterestingMembersKind interestingMembersKind, ref List<EdmMember> interestingMembers)
{
Debug.Assert(functionMappings != null && functionMappings.UpdateFunctionMapping != null, "Expected function mapping fragment with non-null update function mapping");
Debug.Assert(interestingMembers != null, "interestingMembers != null");
// for partial update scenarios (e.g. EntityDataSourceControl) all members are interesting otherwise the data may be corrupt.
// See bugs #272992 and #124460 in DevDiv database for more details. For full update scenarios and the obsolete
// MetadataWorkspace.GetRequiredOriginalValueMembers() metod we return only members with Version set to "Original".
if (interestingMembersKind == InterestingMembersKind.PartialUpdate)
{
// (5) Members included in Update ModificationFunction
interestingMembers.AddRange(functionMappings.UpdateFunctionMapping.ParameterBindings.Select(p => p.MemberPath.Members.Last()));
}
else
{
//(4) Members in update ModificationFunction with Version="Original" are "interesting"
// This also works when you have complex-types (4.1)
Debug.Assert(
interestingMembersKind == InterestingMembersKind.FullUpdate || interestingMembersKind == InterestingMembersKind.RequiredOriginalValueMembers,
"Unexpected kind of interesting members - if you changed the InterestingMembersKind enum type update this code accordingly");
foreach (var parameterBinding in functionMappings.UpdateFunctionMapping.ParameterBindings.Where(p => !p.IsCurrent))
{
//Last is the root element (with respect to the Entity)
//For example, Entity1={
// S1,
// C1{S2,
// C2{ S3, S4 }
// },
// S5}
// if S4 matches (i.e. C1.C2.S4), then it returns C1
//because internally the list is [S4][C2][C1]
interestingMembers.Add(parameterBinding.MemberPath.Members.Last());
}
}
}
#endregion
/// <summary>
/// Calls the view dictionary to load the view, see detailed comments in the view dictionary class.
/// </summary>
internal GeneratedView GetGeneratedView(EntitySetBase extent, MetadataWorkspace workspace)
{
return this.m_viewDictionary.GetGeneratedView(extent, workspace, this);
}
// Add to the cache. If it is already present, then throw an exception
private void AddInternal(Map storageMap)
{
storageMap.DataSpace = DataSpace.CSSpace;
try
{
base.AddInternal(storageMap);
}
catch (ArgumentException e)
{
throw new MappingException(System.Data.Entity.Strings.Mapping_Duplicate_Type(storageMap.EdmItem.Identity), e);
}
}
// Contains whether the given StorageEntityContainerName
internal bool ContainsStorageEntityContainer(string storageEntityContainerName)
{
ReadOnlyCollection<StorageEntityContainerMapping> entityContainerMaps =
this.GetItems<StorageEntityContainerMapping>();
return entityContainerMaps.Any(map => map.StorageEntityContainer.Name.Equals(storageEntityContainerName, StringComparison.Ordinal));
}
/// <summary>
/// This helper method loads items based on contents of in-memory XmlReader instances.
/// Assumption: This method is called only from the constructor because m_extentMappingViews is not thread safe.
/// </summary>
/// <param name="xmlReaders">A list of XmlReader instances</param>
/// <param name="mappingSchemaUris">A list of URIs</param>
/// <returns>A list of schema errors</returns>
private List<EdmSchemaError> LoadItems(IEnumerable<XmlReader> xmlReaders,
List<string> mappingSchemaUris,
Dictionary<EntitySetBase, GeneratedView> userDefinedQueryViewsDict,
Dictionary<OfTypeQVCacheKey, GeneratedView> userDefinedQueryViewsOfTypeDict,
double expectedVersion)
{
Debug.Assert(m_memberMappings.Count == 0, "Assumption: This method is called only once, and from the constructor because m_extentMappingViews is not thread safe.");
List<EdmSchemaError> errors = new List<EdmSchemaError>();
int index = -1;
foreach (XmlReader xmlReader in xmlReaders)
{
index++;
string location = null;
if (mappingSchemaUris == null)
{
som.SchemaManager.TryGetBaseUri(xmlReader, out location);
}
else
{
location = mappingSchemaUris[index];
}
StorageMappingItemLoader mapLoader = new StorageMappingItemLoader(
xmlReader,
this,
location, // ASSUMPTION: location is only used for generating error-messages
m_memberMappings);
errors.AddRange(mapLoader.ParsingErrors);
CheckIsSameVersion(expectedVersion, mapLoader.MappingVersion, errors);
// Process container mapping.
StorageEntityContainerMapping containerMapping = mapLoader.ContainerMapping;
if (mapLoader.HasQueryViews && containerMapping != null)
{
// Compile the query views so that we can report the errors in the user specified views.
CompileUserDefinedQueryViews(containerMapping, userDefinedQueryViewsDict, userDefinedQueryViewsOfTypeDict, errors);
}
// Add container mapping if there are no errors and entity container mapping is not already present.
if (MetadataHelper.CheckIfAllErrorsAreWarnings(errors) && !this.Contains(containerMapping))
{
AddInternal(containerMapping);
}
}
CheckForDuplicateItems(EdmItemCollection, StoreItemCollection, errors);
return errors;
}
/// <summary>
/// This method compiles all the user defined query views in the <paramref name="entityContainerMapping"/>.
/// </summary>
private static void CompileUserDefinedQueryViews(StorageEntityContainerMapping entityContainerMapping,
Dictionary<EntitySetBase, GeneratedView> userDefinedQueryViewsDict,
Dictionary<OfTypeQVCacheKey, GeneratedView> userDefinedQueryViewsOfTypeDict,
IList<EdmSchemaError> errors)
{
ConfigViewGenerator config = new ConfigViewGenerator();
foreach (StorageSetMapping setMapping in entityContainerMapping.AllSetMaps)
{
if (setMapping.QueryView != null)
{
GeneratedView generatedView;
if (!userDefinedQueryViewsDict.TryGetValue(setMapping.Set, out generatedView))
{
// Parse the view so that we will get back any errors in the view.
if (GeneratedView.TryParseUserSpecifiedView(setMapping,
setMapping.Set.ElementType,
setMapping.QueryView,
true, // includeSubtypes
entityContainerMapping.StorageMappingItemCollection,
config,
/*out*/ errors,
out generatedView))
{
// Add first QueryView
userDefinedQueryViewsDict.Add(setMapping.Set, generatedView);
}
// Add all type-specific QueryViews
foreach (OfTypeQVCacheKey key in setMapping.GetTypeSpecificQVKeys())
{
Debug.Assert(key.First.Equals(setMapping.Set));
if (GeneratedView.TryParseUserSpecifiedView(setMapping,
key.Second.First, // type
setMapping.GetTypeSpecificQueryView(key),
key.Second.Second, // includeSubtypes
entityContainerMapping.StorageMappingItemCollection,
config,
/*out*/ errors,
out generatedView))
{
userDefinedQueryViewsOfTypeDict.Add(key, generatedView);
}
}
}
}
}
}
private void CheckIsSameVersion(double expectedVersion, double currentLoaderVersion, IList<EdmSchemaError> errors)
{
if (m_mappingVersion == XmlConstants.UndefinedVersion)
{
m_mappingVersion = currentLoaderVersion;
}
if (expectedVersion != XmlConstants.UndefinedVersion && currentLoaderVersion != XmlConstants.UndefinedVersion && currentLoaderVersion != expectedVersion)
{
// Check that the mapping version is the same as the storage and model version
errors.Add(
new EdmSchemaError(
Strings.Mapping_DifferentMappingEdmStoreVersion,
(int)StorageMappingErrorCode.MappingDifferentMappingEdmStoreVersion, EdmSchemaErrorSeverity.Error));
}
if (currentLoaderVersion != m_mappingVersion && currentLoaderVersion != XmlConstants.UndefinedVersion)
{
// Check that the mapping versions are all consistent with each other
errors.Add(
new EdmSchemaError(
Strings.CannotLoadDifferentVersionOfSchemaInTheSameItemCollection,
(int)StorageMappingErrorCode.CannotLoadDifferentVersionOfSchemaInTheSameItemCollection,
EdmSchemaErrorSeverity.Error));
}
}
/// <summary>
/// Return the update view loader
/// </summary>
/// <returns></returns>
internal ViewLoader GetUpdateViewLoader()
{
if (_viewLoader == null)
{
_viewLoader = new ViewLoader(this);
}
return _viewLoader;
}
/// <summary>
/// this method will be called in metadatworkspace, the signature is the same as the one in ViewDictionary
/// </summary>
/// <param name="workspace"></param>
/// <param name="entity"></param>
/// <param name="type"></param>
/// <param name="includeSubtypes"></param>
/// <param name="generatedView"></param>
/// <returns></returns>
internal bool TryGetGeneratedViewOfType(MetadataWorkspace workspace, EntitySetBase entity, EntityTypeBase type, bool includeSubtypes, out GeneratedView generatedView)
{
return this.m_viewDictionary.TryGetGeneratedViewOfType(workspace, entity, type, includeSubtypes, out generatedView);
}
// Check for duplicate items (items with same name) in edm item collection and store item collection. Mapping is the only logical place to do this.
// The only other place is workspace, but that is at the time of registering item collections (only when the second one gets registered) and we
// will have to throw exceptions at that time. If we do this check in mapping, we might throw error in a more consistent way (by adding it to error
// collection). Also if someone is just creating item collection, and not registering it with workspace (tools), doing it in mapping makes more sense
private static void CheckForDuplicateItems(EdmItemCollection edmItemCollection, StoreItemCollection storeItemCollection, List<EdmSchemaError> errorCollection)
{
Debug.Assert(edmItemCollection != null && storeItemCollection != null && errorCollection != null, "The parameters must not be null in CheckForDuplicateItems");
foreach (GlobalItem item in edmItemCollection)
{
if (storeItemCollection.Contains(item.Identity))
{
errorCollection.Add(new EdmSchemaError(Strings.Mapping_ItemWithSameNameExistsBothInCSpaceAndSSpace(item.Identity),
(int)StorageMappingErrorCode.ItemWithSameNameExistsBothInCSpaceAndSSpace, EdmSchemaErrorSeverity.Error));
}
}
}
}//---- ItemCollection
}//----
|