1 //---------------------------------------------------------------------
2 // <copyright file="SourceInterpreter.cs" company="Microsoft">
3 //      Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 //
6 // @owner Microsoft
7 // @backupOwner Microsoft
8 //---------------------------------------------------------------------
9 
10 using System.Collections.Generic;
11 using System.Collections.ObjectModel;
12 using System.Diagnostics;
13 using System.Data.Objects;
14 using System.Data.Metadata.Edm;
15 namespace System.Data.Mapping.Update.Internal
16 {
17     /// <summary>
18     /// This class determines the state entries contributing to an expression
19     /// propagated through an update mapping view (values in propagated expressions
20     /// remember where they come from)
21     /// </summary>
22     internal class SourceInterpreter
23     {
SourceInterpreter(UpdateTranslator translator, EntitySet sourceTable)24         private SourceInterpreter(UpdateTranslator translator, EntitySet sourceTable)
25         {
26             m_stateEntries = new List<IEntityStateEntry>();
27             m_translator = translator;
28             m_sourceTable = sourceTable;
29         }
30 
31         private readonly List<IEntityStateEntry> m_stateEntries;
32         private readonly UpdateTranslator m_translator;
33         private readonly EntitySet m_sourceTable;
34 
35         /// <summary>
36         /// Finds all markup associated with the given source.
37         /// </summary>
38         /// <param name="source">Source expression. Must not be null.</param>
39         /// <param name="translator">Translator containing session information.</param>
40         /// <param name="sourceTable">Table from which the exception was thrown (must not be null).</param>
41         /// <returns>Markup.</returns>
GetAllStateEntries(PropagatorResult source, UpdateTranslator translator, EntitySet sourceTable)42         internal static ReadOnlyCollection<IEntityStateEntry> GetAllStateEntries(PropagatorResult source, UpdateTranslator translator,
43             EntitySet sourceTable)
44         {
45             Debug.Assert(null != source);
46             Debug.Assert(null != translator);
47             Debug.Assert(null != sourceTable);
48 
49             SourceInterpreter interpreter = new SourceInterpreter(translator, sourceTable);
50             interpreter.RetrieveResultMarkup(source);
51 
52             return new ReadOnlyCollection<IEntityStateEntry>(interpreter.m_stateEntries);
53         }
54 
RetrieveResultMarkup(PropagatorResult source)55         private void RetrieveResultMarkup(PropagatorResult source)
56         {
57             Debug.Assert(null != source);
58 
59             if (source.Identifier != PropagatorResult.NullIdentifier)
60             {
61                 // state entries travel with identifiers. several state entries may be merged
62                 // into a single identifier result via joins in the update mapping view
63                 do
64                 {
65                     if (null != source.StateEntry)
66                     {
67                         m_stateEntries.Add(source.StateEntry);
68                         if (source.Identifier != PropagatorResult.NullIdentifier)
69                         {
70                             // if this is an identifier, it may also be registered with an "owner".
71                             // Return the owner as well if the owner is also mapped to this table.
72                             PropagatorResult owner;
73                             if (m_translator.KeyManager.TryGetIdentifierOwner(source.Identifier, out owner) &&
74                                 null != owner.StateEntry &&
75                                 ExtentInScope(owner.StateEntry.EntitySet))
76                             {
77                                 m_stateEntries.Add(owner.StateEntry);
78                             }
79 
80                             // Check if are any referential constraints. If so, the entity key
81                             // implies that the dependent relationship instance is also being
82                             // handled in this result.
83                             foreach (IEntityStateEntry stateEntry in m_translator.KeyManager.GetDependentStateEntries(source.Identifier))
84                             {
85                                 m_stateEntries.Add(stateEntry);
86                             }
87                         }
88                     }
89                     source = source.Next;
90                 }
91                 while (null != source);
92             }
93             else if (!source.IsSimple && !source.IsNull)
94             {
95                 // walk children
96                 foreach (PropagatorResult child in source.GetMemberValues())
97                 {
98                     RetrieveResultMarkup(child);
99                 }
100             }
101         }
102 
103         // Determines whether the given table is in scope for the current source: if the source
104         // table does not map to the source table for this interpreter, it is not in scope
105         // for exceptions thrown from this table.
ExtentInScope(EntitySetBase extent)106         private bool ExtentInScope(EntitySetBase extent)
107         {
108             if (null == extent)
109             {
110                 return false;
111             }
112             // determine if the extent is mapped to this table
113             return m_translator.ViewLoader.GetAffectedTables(extent, m_translator.MetadataWorkspace).Contains(m_sourceTable);
114         }
115     }
116 }
117