//---------------------------------------------------------------------
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
//
// @owner Microsoft
// @backupOwner Microsoft
//---------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Xml;
using System.Data.Metadata.Edm;
using System.Diagnostics;
using System.Data.Mapping.ViewGeneration;
using System.Data.Common.Utils;
using System.Data.Mapping.ViewGeneration.Structures;
using System.Data.Mapping.ViewGeneration.Validation;
namespace System.Data.Mapping {
using CellGroup = Set;
///
/// Represents the Mapping metadata for the EntityContainer map in CS space.
/// Only one EntityContainerMapping element is allowed in the MSL file for CS mapping.
///
/// For Example if conceptually you could represent the CS MSL file as following
/// ---Mapping
/// --EntityContainerMapping ( CNorthwind-->SNorthwind )
/// --EntitySetMapping
/// --AssociationSetMapping
/// The type represents the metadata for EntityContainerMapping element in the above example.
/// The SetMapping elements that are children of the EntityContainerMapping element
/// can be accessed through the properties on this type.
///
///
/// We currently assume that an Entity Container on the C side
/// is mapped to a single Entity Container in the S - space.
///
///
internal class StorageEntityContainerMapping : Map
{
#region Constructors
///
/// Construct a new EntityContainer mapping object
/// passing in the C-space EntityContainer and
/// the s-space Entity container metadata objects.
///
/// Entity Continer type that is being mapped on the C-side
/// Entity Continer type that is being mapped on the S-side
internal StorageEntityContainerMapping(EntityContainer entityContainer, EntityContainer storageEntityContainer, StorageMappingItemCollection storageMappingItemCollection, bool validate, bool generateUpdateViews)
{
this.m_entityContainer = entityContainer;
this.m_storageEntityContainer = storageEntityContainer;
this.m_storageMappingItemCollection = storageMappingItemCollection;
this.m_memoizedCellGroupEvaluator = new Memoizer(ComputeCellGroups, new InputForComputingCellGroups());
this.identity = entityContainer.Identity;
this.m_validate = validate;
this.m_generateUpdateViews = generateUpdateViews;
}
#endregion
#region Fields
private string identity;
private bool m_validate;
private bool m_generateUpdateViews;
private EntityContainer m_entityContainer; //Entity Continer type that is being mapped on the C-side
private EntityContainer m_storageEntityContainer; //Entity Continer type that the C-space container is being mapped to
private Dictionary m_entitySetMappings = new Dictionary(StringComparer.Ordinal); //A collection of EntitySetMappings under this EntityContainer mapping
private Dictionary m_associationSetMappings = new Dictionary(StringComparer.Ordinal); //A collection of AssociationSetMappings under this EntityContainer mapping
private Dictionary m_functionImportMappings = new Dictionary();
private string m_sourceLocation; //Schema URI for the mapping
private int m_startLineNumber; //Line Number for EntityContainer Mapping element start tag
private int m_startLinePosition; //Line position for EntityContainer Mapping element start tag
private readonly StorageMappingItemCollection m_storageMappingItemCollection;
private readonly Memoizer m_memoizedCellGroupEvaluator;
#endregion
#region Properties
public StorageMappingItemCollection StorageMappingItemCollection
{
get
{
return m_storageMappingItemCollection;
}
}
///
/// Gets the type kind for this item
///
public override BuiltInTypeKind BuiltInTypeKind {
get { return BuiltInTypeKind.MetadataItem; }
}
///
/// The Entity Container Metadata object on the C-side
/// for which the mapping is being represented.
///
internal override MetadataItem EdmItem {
get {
return this.m_entityContainer;
}
}
internal override string Identity {
get {
return identity;
}
}
///
/// Indicates whether there are no Set mappings
/// in the container mapping.
///
internal bool IsEmpty
{
get
{
return ((m_entitySetMappings.Count == 0)
&& (m_associationSetMappings.Count == 0));
}
}
///
/// Determine whether the container includes any views.
/// Returns true if there is at least one query or update view specified by the mapping.
///
internal bool HasViews
{
get
{
return HasMappingFragments()
|| AllSetMaps.Any((StorageSetMapping setMap) => setMap.QueryView != null);
}
}
internal string SourceLocation {
get { return m_sourceLocation; }
set { m_sourceLocation = value; }
}
///
/// The Entity Container Metadata object on the C-side
/// for which the mapping is being represented.
///
internal EntityContainer EdmEntityContainer {
get {
return this.m_entityContainer;
}
}
///
/// The Entity Container Metadata object on the C-side
/// for which the mapping is being represented.
///
internal EntityContainer StorageEntityContainer {
get {
return this.m_storageEntityContainer;
}
}
///
/// a list of all the entity set maps under this
/// container. In CS mapping, the mapping is done
/// at the extent level as opposed to the type level.
///
internal ReadOnlyCollection EntitySetMaps {
get {
return new List(this.m_entitySetMappings.Values).AsReadOnly();
}
}
///
/// a list of all the entity set maps under this
/// container. In CS mapping, the mapping is done
/// at the extent level as opposed to the type level.
/// RelationshipSetMaps will be CompositionSetMaps and
/// AssociationSetMaps put together.
///
///
/// The reason we have RelationshipSetMaps is to be consistent with CDM metadata
/// which treats both associations and compositions as Relationships.
///
internal ReadOnlyCollection RelationshipSetMaps {
get {
return new List(this.m_associationSetMappings.Values).AsReadOnly();
}
}
///
/// a list of all the set maps under this
/// container.
///
internal IEnumerable AllSetMaps
{
get
{
return System.Linq.Enumerable.Concat(this.m_entitySetMappings.Values, this.m_associationSetMappings.Values);
}
}
///
/// Line Number in MSL file where the EntityContainer Mapping Element's Start Tag is present.
///
internal int StartLineNumber
{
get
{
return m_startLineNumber;
}
set
{
m_startLineNumber = value;
}
}
///
/// Line Position in MSL file where the EntityContainer Mapping Element's Start Tag is present.
///
internal int StartLinePosition
{
get
{
return m_startLinePosition;
}
set
{
m_startLinePosition = value;
}
}
///
/// Indicates whether to validate the mapping or not.
///
internal bool Validate
{
get
{
return m_validate;
}
}
///
/// Indicates whether to generate the update views or not.
///
internal bool GenerateUpdateViews
{
get
{
return m_generateUpdateViews;
}
}
#endregion
#region Methods
///
/// get an EntitySet mapping based upon the name of the entity set.
///
/// /// the name of the entity set
internal StorageSetMapping GetEntitySetMapping(String entitySetName) {
EntityUtil.CheckArgumentNull(entitySetName, "entitySetName");
//Key for EntitySetMapping should be EntitySet name and Entoty type name
StorageSetMapping setMapping = null;
m_entitySetMappings.TryGetValue(entitySetName, out setMapping);
return setMapping;
}
///
/// Get a RelationShip set mapping based upon the name of the relationship set
///
/// the name of the relationship set
/// the mapping for the entity set if it exists, null if it does not exist
internal StorageSetMapping GetRelationshipSetMapping(string relationshipSetName) {
EntityUtil.CheckArgumentNull(relationshipSetName, "relationshipSetName");
StorageSetMapping setMapping = null;
m_associationSetMappings.TryGetValue(relationshipSetName, out setMapping);
return setMapping;
}
///
/// Get a RelationShipSet mapping that has the passed in EntitySet as one of the ends and is mapped to the
/// table.
///
internal IEnumerable GetRelationshipSetMappingsFor(EntitySetBase edmEntitySet, EntitySetBase storeEntitySet )
{
//First select the association set maps that are mapped to this table
IEnumerable associationSetMappings = m_associationSetMappings.Values.Cast().Where(w => ((w.StoreEntitySet != null) && (w.StoreEntitySet == storeEntitySet)));
//From this again filter the ones that have the specified EntitySet on atleast one end
associationSetMappings = associationSetMappings.Where(associationSetMap => ((associationSetMap.Set as AssociationSet).AssociationSetEnds.Any(associationSetEnd => associationSetEnd.EntitySet == edmEntitySet)));
return associationSetMappings;
}
///
/// Get a set mapping based upon the name of the set
///
///
///
internal StorageSetMapping GetSetMapping(string setName)
{
StorageSetMapping setMap = GetEntitySetMapping(setName);
if (setMap == null)
{
setMap = GetRelationshipSetMapping(setName);
}
return setMap;
}
///
/// Adds an entity set mapping to the list of EntitySetMaps
/// under this entity container mapping. The method will be called
/// by the Mapping loader.
///
internal void AddEntitySetMapping(StorageSetMapping setMapping) {
if (!this.m_entitySetMappings.ContainsKey(setMapping.Set.Name))
this.m_entitySetMappings.Add(setMapping.Set.Name, setMapping);
}
///
/// Adds a association set mapping to the list of AssociationSetMaps
/// under this entity container mapping. The method will be called
/// by the Mapping loader.
///
internal void AddAssociationSetMapping(StorageSetMapping setMapping) {
this.m_associationSetMappings.Add(setMapping.Set.Name, setMapping);
}
///
/// check whether the EntityContainerMapping contains
/// the map for the given AssociationSet
///
///
///
internal bool ContainsAssociationSetMapping(AssociationSet associationSet) {
return this.m_associationSetMappings.ContainsKey(associationSet.Name);
}
///
/// Returns whether the Set Map for the given set has a query view or not
///
///
///
internal bool HasQueryViewForSetMap(string setName)
{
StorageSetMapping set = GetSetMapping(setName);
if (set != null)
{
return (set.QueryView != null);
}
return false;
}
internal bool HasMappingFragments()
{
foreach (var extentMap in this.AllSetMaps)
{
foreach (var typeMap in extentMap.TypeMappings)
{
if (typeMap.MappingFragments.Count > 0)
{
return true;
}
}
}
return false;
}
///
/// The method builds up the spaces required for pretty printing each
/// part of the mapping.
///
internal static string GetPrettyPrintString(ref int index) {
string spaces = "";
spaces = spaces.PadLeft(index, ' ');
Console.WriteLine(spaces + "|");
Console.WriteLine(spaces + "|");
index++;
spaces = spaces.PadLeft(index, ' ');
Console.Write(spaces + "-");
index++;
spaces = spaces.PadLeft(index, ' ');
Console.Write("-");
index++;
spaces = spaces.PadLeft(index, ' ');
return spaces;
}
///
/// This method is primarily for debugging purposes.
/// Will be removed shortly.
///
///
internal void Print(int index) {
string spaces = "";
StringBuilder sb = new StringBuilder();
sb.Append(spaces);
sb.Append("EntityContainerMapping");
sb.Append(" ");
sb.Append("Name:");
sb.Append(this.m_entityContainer.Name);
sb.Append(" ");
Console.WriteLine(sb.ToString());
foreach (StorageSetMapping extentMapping in m_entitySetMappings.Values) {
extentMapping.Print(index + 5);
}
foreach (StorageSetMapping extentMapping in m_associationSetMappings.Values) {
extentMapping.Print(index + 5);
}
}
// Methods to modify and access function imports, which association a "functionImport" declared
// in the model entity container with a targetFunction declared in the target
internal void AddFunctionImportMapping(EdmFunction functionImport, FunctionImportMapping mapping)
{
m_functionImportMappings.Add(functionImport, mapping);
}
internal bool TryGetFunctionImportMapping(EdmFunction functionImport, out FunctionImportMapping mapping)
{
return m_functionImportMappings.TryGetValue(functionImport, out mapping);
}
internal OutputFromComputeCellGroups GetCellgroups(InputForComputingCellGroups args)
{
Debug.Assert(object.ReferenceEquals(this, args.ContainerMapping));
return m_memoizedCellGroupEvaluator.Evaluate(args);
}
private OutputFromComputeCellGroups ComputeCellGroups(InputForComputingCellGroups args)
{
OutputFromComputeCellGroups result = new OutputFromComputeCellGroups();
result.Success = true;
CellCreator cellCreator = new CellCreator(args.ContainerMapping);
result.Cells = cellCreator.GenerateCells(args.Config);
result.Identifiers = cellCreator.Identifiers;
if (result.Cells.Count <= 0)
{
//When type-specific QVs are asked for but not defined in the MSL we should return without generating
// Query pipeline will handle this appropriately by asking for UNION ALL view.
result.Success = false;
return result;
}
result.ForeignKeyConstraints = ForeignConstraint.GetForeignConstraints(args.ContainerMapping.StorageEntityContainer);
// Go through each table and determine their foreign key constraints
CellPartitioner partitioner = new CellPartitioner(result.Cells, result.ForeignKeyConstraints);
List cellGroups = partitioner.GroupRelatedCells();
//Clone cell groups- i.e, List> - upto cell before storing it in the cache because viewgen modified the Cell structure
result.CellGroups = cellGroups.Select(setOfcells => new CellGroup(setOfcells.Select(cell => new Cell(cell)))).ToList();
return result;
}
#endregion
}
internal struct InputForComputingCellGroups : IEquatable, IEqualityComparer
{
internal readonly StorageEntityContainerMapping ContainerMapping;
internal readonly ConfigViewGenerator Config;
internal InputForComputingCellGroups(StorageEntityContainerMapping containerMapping, ConfigViewGenerator config)
{
this.ContainerMapping = containerMapping;
this.Config = config;
}
public bool Equals(InputForComputingCellGroups other)
{
// Isn't this funny? We are not using Memoizer for function memoization. Args Entity and Config don't matter!
// If I were to compare Entity this would not use the cache for cases when I supply different entity set. However,
// the cell groups belong to ALL entity sets.
return (this.ContainerMapping.Equals(other.ContainerMapping)
&& this.Config.Equals(other.Config));
}
public bool Equals(InputForComputingCellGroups one, InputForComputingCellGroups two)
{
if (object.ReferenceEquals(one, two))
{
return true;
}
if (object.ReferenceEquals(one, null) || object.ReferenceEquals(two, null))
{
return false;
}
return one.Equals(two);
}
public int GetHashCode(InputForComputingCellGroups value)
{
if (value == null)
{
return 0;
}
return value.GetHashCode();
}
public override int GetHashCode()
{
return this.ContainerMapping.GetHashCode();
}
public override bool Equals(object obj)
{
if (obj is InputForComputingCellGroups)
{
return Equals((InputForComputingCellGroups)obj);
}
else
{
return false;
}
}
public static bool operator ==(InputForComputingCellGroups input1, InputForComputingCellGroups input2)
{
if (object.ReferenceEquals(input1, input2))
{
return true;
}
return input1.Equals(input2);
}
public static bool operator !=(InputForComputingCellGroups input1, InputForComputingCellGroups input2)
{
return !(input1 == input2);
}
}
internal struct OutputFromComputeCellGroups
{
internal List Cells;
internal CqlIdentifiers Identifiers;
internal List CellGroups;
internal List ForeignKeyConstraints;
internal bool Success;
}
}
| |