|
//---------------------------------------------------------------------
// <copyright file="StorageEntityContainerMapping.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
//
// @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<Cell>;
/// <summary>
/// Represents the Mapping metadata for the EntityContainer map in CS space.
/// Only one EntityContainerMapping element is allowed in the MSL file for CS mapping.
/// <example>
/// 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.
/// </example>
/// <remarks>
/// We currently assume that an Entity Container on the C side
/// is mapped to a single Entity Container in the S - space.
/// </remarks>
/// </summary>
internal class StorageEntityContainerMapping : Map
{
#region Constructors
/// <summary>
/// Construct a new EntityContainer mapping object
/// passing in the C-space EntityContainer and
/// the s-space Entity container metadata objects.
/// </summary>
/// <param name="entityContainer">Entity Continer type that is being mapped on the C-side</param>
/// <param name="storageEntityContainer">Entity Continer type that is being mapped on the S-side</param>
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<InputForComputingCellGroups, OutputFromComputeCellGroups>(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<string, StorageSetMapping> m_entitySetMappings = new Dictionary<string, StorageSetMapping>(StringComparer.Ordinal); //A collection of EntitySetMappings under this EntityContainer mapping
private Dictionary<string, StorageSetMapping> m_associationSetMappings = new Dictionary<string, StorageSetMapping>(StringComparer.Ordinal); //A collection of AssociationSetMappings under this EntityContainer mapping
private Dictionary<EdmFunction, FunctionImportMapping> m_functionImportMappings = new Dictionary<EdmFunction, FunctionImportMapping>();
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<InputForComputingCellGroups, OutputFromComputeCellGroups> m_memoizedCellGroupEvaluator;
#endregion
#region Properties
public StorageMappingItemCollection StorageMappingItemCollection
{
get
{
return m_storageMappingItemCollection;
}
}
/// <summary>
/// Gets the type kind for this item
/// </summary>
public override BuiltInTypeKind BuiltInTypeKind {
get { return BuiltInTypeKind.MetadataItem; }
}
/// <summary>
/// The Entity Container Metadata object on the C-side
/// for which the mapping is being represented.
/// </summary>
internal override MetadataItem EdmItem {
get {
return this.m_entityContainer;
}
}
internal override string Identity {
get {
return identity;
}
}
/// <summary>
/// Indicates whether there are no Set mappings
/// in the container mapping.
/// </summary>
internal bool IsEmpty
{
get
{
return ((m_entitySetMappings.Count == 0)
&& (m_associationSetMappings.Count == 0));
}
}
/// <summary>
/// Determine whether the container includes any views.
/// Returns true if there is at least one query or update view specified by the mapping.
/// </summary>
internal bool HasViews
{
get
{
return HasMappingFragments()
|| AllSetMaps.Any((StorageSetMapping setMap) => setMap.QueryView != null);
}
}
internal string SourceLocation {
get { return m_sourceLocation; }
set { m_sourceLocation = value; }
}
/// <summary>
/// The Entity Container Metadata object on the C-side
/// for which the mapping is being represented.
/// </summary>
internal EntityContainer EdmEntityContainer {
get {
return this.m_entityContainer;
}
}
/// <summary>
/// The Entity Container Metadata object on the C-side
/// for which the mapping is being represented.
/// </summary>
internal EntityContainer StorageEntityContainer {
get {
return this.m_storageEntityContainer;
}
}
/// <summary>
/// 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.
/// </summary>
internal ReadOnlyCollection<StorageSetMapping> EntitySetMaps {
get {
return new List<StorageSetMapping>(this.m_entitySetMappings.Values).AsReadOnly();
}
}
/// <summary>
/// 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.
/// </summary>
/// <remarks>
/// The reason we have RelationshipSetMaps is to be consistent with CDM metadata
/// which treats both associations and compositions as Relationships.
/// </remarks>
internal ReadOnlyCollection<StorageSetMapping> RelationshipSetMaps {
get {
return new List<StorageSetMapping>(this.m_associationSetMappings.Values).AsReadOnly();
}
}
/// <summary>
/// a list of all the set maps under this
/// container.
/// </summary>
internal IEnumerable<StorageSetMapping> AllSetMaps
{
get
{
return System.Linq.Enumerable.Concat(this.m_entitySetMappings.Values, this.m_associationSetMappings.Values);
}
}
/// <summary>
/// Line Number in MSL file where the EntityContainer Mapping Element's Start Tag is present.
/// </summary>
internal int StartLineNumber
{
get
{
return m_startLineNumber;
}
set
{
m_startLineNumber = value;
}
}
/// <summary>
/// Line Position in MSL file where the EntityContainer Mapping Element's Start Tag is present.
/// </summary>
internal int StartLinePosition
{
get
{
return m_startLinePosition;
}
set
{
m_startLinePosition = value;
}
}
/// <summary>
/// Indicates whether to validate the mapping or not.
/// </summary>
internal bool Validate
{
get
{
return m_validate;
}
}
/// <summary>
/// Indicates whether to generate the update views or not.
/// </summary>
internal bool GenerateUpdateViews
{
get
{
return m_generateUpdateViews;
}
}
#endregion
#region Methods
/// <summary>
/// get an EntitySet mapping based upon the name of the entity set.
/// </summary>
/// /// <param name="entitySetName">the name of the entity set</param>
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;
}
/// <summary>
/// Get a RelationShip set mapping based upon the name of the relationship set
/// </summary>
/// <param name="relationshipSetName">the name of the relationship set</param>
/// <returns>the mapping for the entity set if it exists, null if it does not exist</returns>
internal StorageSetMapping GetRelationshipSetMapping(string relationshipSetName) {
EntityUtil.CheckArgumentNull(relationshipSetName, "relationshipSetName");
StorageSetMapping setMapping = null;
m_associationSetMappings.TryGetValue(relationshipSetName, out setMapping);
return setMapping;
}
/// <summary>
/// Get a RelationShipSet mapping that has the passed in EntitySet as one of the ends and is mapped to the
/// table.
/// </summary>
internal IEnumerable<StorageAssociationSetMapping> GetRelationshipSetMappingsFor(EntitySetBase edmEntitySet, EntitySetBase storeEntitySet )
{
//First select the association set maps that are mapped to this table
IEnumerable<StorageAssociationSetMapping> associationSetMappings = m_associationSetMappings.Values.Cast<StorageAssociationSetMapping>().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;
}
/// <summary>
/// Get a set mapping based upon the name of the set
/// </summary>
/// <param name="setName"></param>
/// <returns></returns>
internal StorageSetMapping GetSetMapping(string setName)
{
StorageSetMapping setMap = GetEntitySetMapping(setName);
if (setMap == null)
{
setMap = GetRelationshipSetMapping(setName);
}
return setMap;
}
/// <summary>
/// Adds an entity set mapping to the list of EntitySetMaps
/// under this entity container mapping. The method will be called
/// by the Mapping loader.
/// </summary>
internal void AddEntitySetMapping(StorageSetMapping setMapping) {
if (!this.m_entitySetMappings.ContainsKey(setMapping.Set.Name))
this.m_entitySetMappings.Add(setMapping.Set.Name, setMapping);
}
/// <summary>
/// Adds a association set mapping to the list of AssociationSetMaps
/// under this entity container mapping. The method will be called
/// by the Mapping loader.
/// </summary>
internal void AddAssociationSetMapping(StorageSetMapping setMapping) {
this.m_associationSetMappings.Add(setMapping.Set.Name, setMapping);
}
/// <summary>
/// check whether the EntityContainerMapping contains
/// the map for the given AssociationSet
/// </summary>
/// <param name="associationSet"></param>
/// <returns></returns>
internal bool ContainsAssociationSetMapping(AssociationSet associationSet) {
return this.m_associationSetMappings.ContainsKey(associationSet.Name);
}
/// <summary>
/// Returns whether the Set Map for the given set has a query view or not
/// </summary>
/// <param name="setName"></param>
/// <returns></returns>
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;
}
///<summary>
/// The method builds up the spaces required for pretty printing each
/// part of the mapping.
///</summary>
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;
}
/// <summary>
/// This method is primarily for debugging purposes.
/// Will be removed shortly.
/// </summary>
/// <param name="index"></param>
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<CellGroup> cellGroups = partitioner.GroupRelatedCells();
//Clone cell groups- i.e, List<Set<Cell>> - 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<InputForComputingCellGroups>, IEqualityComparer<InputForComputingCellGroups>
{
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<Cell> Cells;
internal CqlIdentifiers Identifiers;
internal List<CellGroup> CellGroups;
internal List<ForeignConstraint> ForeignKeyConstraints;
internal bool Success;
}
}
|