1 //---------------------------------------------------------------------
2 // <copyright file="ViewgenGatekeeper.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
7 // @backupOwner Microsoft
8 //---------------------------------------------------------------------
10 using System.Collections.Generic;
11 using System.Data.Common.Utils;
12 using System.Data.Common.Utils.Boolean;
13 using System.Data.Mapping.ViewGeneration.Structures;
14 using System.Data.Mapping.ViewGeneration.Utils;
15 using System.Data.Mapping.ViewGeneration.Validation;
16 using System.Data.Metadata.Edm;
17 using System.Diagnostics;
21 namespace System.Data.Mapping.ViewGeneration
23 using CellGroup = Set<Cell>;
25 abstract internal class ViewgenGatekeeper : InternalBase
28 /// Entry point for View Generation
30 /// <param name="containerMapping"></param>
31 /// <param name="workSpace"></param>
32 /// <param name="config"></param>
33 /// <returns>Generated Views for EntitySets</returns>
34 internal static ViewGenResults GenerateViewsFromMapping(StorageEntityContainerMapping containerMapping, ConfigViewGenerator config)
36 EntityUtil.CheckArgumentNull(containerMapping, "containerMapping");
37 EntityUtil.CheckArgumentNull(config, "config");
38 Debug.Assert(containerMapping.HasViews, "Precondition Violated: No mapping exists to generate views for!");
40 if (config.IsNormalTracing)
42 containerMapping.Print(0);
45 //Create Cells from StorageEntityContainerMapping
46 CellCreator cellCreator = new CellCreator(containerMapping);
47 List<Cell> cells = cellCreator.GenerateCells(config);
48 CqlIdentifiers identifiers = cellCreator.Identifiers;
50 return GenerateViewsFromCells(cells, config, identifiers, containerMapping);
54 /// Entry point for Type specific generation of Query Views
56 internal static ViewGenResults GenerateTypeSpecificQueryView(StorageEntityContainerMapping containerMapping,
57 ConfigViewGenerator config,
63 EntityUtil.CheckArgumentNull(containerMapping, "containerMapping");
64 EntityUtil.CheckArgumentNull(config, "config");
65 EntityUtil.CheckArgumentNull(entity, "entity");
66 EntityUtil.CheckArgumentNull(type, "type");
67 Debug.Assert(!type.Abstract, "Can not generate OfType/OfTypeOnly query view for and abstract type");
69 if (config.IsNormalTracing)
71 Helpers.StringTraceLine("");
72 Helpers.StringTraceLine("<<<<<<<< Generating Query View for Entity [" + entity.Name + "] OfType" + (includeSubtypes ? "" : "Only") + "(" + type.Name + ") >>>>>>>");
75 if (containerMapping.GetEntitySetMapping(entity.Name).QueryView != null)
77 //Type-specific QV does not exist in the cache, but
78 // there is a EntitySet QV. So we can't generate the view (no mapping exists for this EntitySet)
79 // and we rely on Query to call us again to get the EntitySet View.
84 //Compute Cell Groups or get it from Memoizer
85 InputForComputingCellGroups args = new InputForComputingCellGroups(containerMapping, config);
86 OutputFromComputeCellGroups result = containerMapping.GetCellgroups(args);
87 success = result.Success;
94 List<ForeignConstraint> foreignKeyConstraints = result.ForeignKeyConstraints;
95 // Get a Clone of cell groups from cache since cells are modified during viewgen, and we dont want the cached copy to change
96 List<CellGroup> cellGroups = cellGroups = result.CellGroups.Select(setOfcells => new CellGroup(setOfcells.Select(cell => new Cell(cell)))).ToList();
97 List<Cell> cells = result.Cells;
98 CqlIdentifiers identifiers = result.Identifiers;
101 ViewGenResults viewGenResults = new ViewGenResults();
102 ErrorLog tmpLog = EnsureAllCSpaceContainerSetsAreMapped(cells, config, containerMapping);
103 if (tmpLog.Count > 0)
105 viewGenResults.AddErrors(tmpLog);
106 Helpers.StringTraceLine(viewGenResults.ErrorsToString());
107 success = true; //atleast we tried successfully
108 return viewGenResults;
111 foreach (CellGroup cellGroup in cellGroups)
113 if (!DoesCellGroupContainEntitySet(cellGroup, entity))
118 ViewGenerator viewGenerator = null;
119 ErrorLog groupErrorLog = new ErrorLog();
122 viewGenerator = new ViewGenerator(cellGroup, config, foreignKeyConstraints, containerMapping);
124 catch (InternalMappingException exception)
126 // All exceptions have mapping errors in them
127 Debug.Assert(exception.ErrorLog.Count > 0, "Incorrectly created mapping exception");
128 groupErrorLog = exception.ErrorLog;
131 if (groupErrorLog.Count > 0)
135 Debug.Assert(viewGenerator != null); //make sure there is no exception thrown that does not add error to log
137 ViewGenMode mode = includeSubtypes ? ViewGenMode.OfTypeViews : ViewGenMode.OfTypeOnlyViews;
139 groupErrorLog = viewGenerator.GenerateQueryViewForSingleExtent(viewGenResults.Views, identifiers, entity, type, mode);
141 if (groupErrorLog.Count != 0)
143 viewGenResults.AddErrors(groupErrorLog);
148 return viewGenResults;
152 // effects: Given a list of cells in the schema, generates the query and
153 // update mapping views for OFTYPE(Extent, Type) combinations in this schema
154 // container. Returns a list of generated query and update views.
155 // If it is false and some columns in a table are unmapped, an
156 // exception is raised
157 private static ViewGenResults GenerateViewsFromCells(List<Cell> cells, ConfigViewGenerator config,
158 CqlIdentifiers identifiers,
159 StorageEntityContainerMapping containerMapping)
161 EntityUtil.CheckArgumentNull(cells, "cells");
162 EntityUtil.CheckArgumentNull(config, "config");
163 Debug.Assert(cells.Count > 0, "There must be at least one cell in the container mapping");
166 // Go through each table and determine their foreign key constraints
167 EntityContainer container = containerMapping.StorageEntityContainer;
168 Debug.Assert(container != null);
170 ViewGenResults viewGenResults = new ViewGenResults();
171 ErrorLog tmpLog = EnsureAllCSpaceContainerSetsAreMapped(cells, config, containerMapping);
172 if (tmpLog.Count > 0)
174 viewGenResults.AddErrors(tmpLog);
175 Helpers.StringTraceLine(viewGenResults.ErrorsToString());
176 return viewGenResults;
179 List<ForeignConstraint> foreignKeyConstraints = ForeignConstraint.GetForeignConstraints(container);
181 CellPartitioner partitioner = new CellPartitioner(cells, foreignKeyConstraints);
182 List<CellGroup> cellGroups = partitioner.GroupRelatedCells();
183 foreach (CellGroup cellGroup in cellGroups)
185 ViewGenerator viewGenerator = null;
186 ErrorLog groupErrorLog = new ErrorLog();
189 viewGenerator = new ViewGenerator(cellGroup, config, foreignKeyConstraints, containerMapping);
191 catch (InternalMappingException exception)
193 // All exceptions have mapping errors in them
194 Debug.Assert(exception.ErrorLog.Count > 0, "Incorrectly created mapping exception");
195 groupErrorLog = exception.ErrorLog;
198 if (groupErrorLog.Count == 0)
200 Debug.Assert(viewGenerator != null);
201 groupErrorLog = viewGenerator.GenerateAllBidirectionalViews(viewGenResults.Views, identifiers);
204 if (groupErrorLog.Count != 0)
206 viewGenResults.AddErrors(groupErrorLog);
209 // We used to print the errors here. Now we trace them as they are being thrown
210 //if (viewGenResults.HasErrors && config.IsViewTracing) {
211 // Helpers.StringTraceLine(viewGenResults.ErrorsToString());
213 return viewGenResults;
218 // effects: Given a container, ensures that all entity/association
219 // sets in container on the C-side have been mapped
220 private static ErrorLog EnsureAllCSpaceContainerSetsAreMapped(IEnumerable<Cell> cells,
221 ConfigViewGenerator config,
222 StorageEntityContainerMapping containerMapping)
225 Set<EntitySetBase> mappedExtents = new Set<EntitySetBase>();
226 string mslFileLocation = null;
227 EntityContainer container = null;
228 // Determine the container and name of the file while determining
229 // the set of mapped extents in the cells
230 foreach (Cell cell in cells)
232 mappedExtents.Add(cell.CQuery.Extent);
233 mslFileLocation = cell.CellLabel.SourceLocation;
234 // All cells are from the same container
235 container = cell.CQuery.Extent.EntityContainer;
237 Debug.Assert(container != null);
239 List<EntitySetBase> missingExtents = new List<EntitySetBase>();
240 // Go through all the extents in the container and determine
241 // extents that are missing
242 foreach (EntitySetBase extent in container.BaseEntitySets)
244 if (mappedExtents.Contains(extent) == false
245 && !(containerMapping.HasQueryViewForSetMap(extent.Name)))
247 AssociationSet associationSet = extent as AssociationSet;
248 if (associationSet==null || !associationSet.ElementType.IsForeignKey)
250 missingExtents.Add(extent);
254 ErrorLog errorLog = new ErrorLog();
255 // If any extent is not mapped, add an error
256 if (missingExtents.Count > 0)
258 StringBuilder extentBuilder = new StringBuilder();
260 foreach (EntitySetBase extent in missingExtents)
262 if (isFirst == false)
264 extentBuilder.Append(", ");
267 extentBuilder.Append(extent.Name);
269 string message = System.Data.Entity.Strings.ViewGen_Missing_Set_Mapping(extentBuilder);
270 // Find the cell with smallest line number - so that we can
271 // point to the beginning of the file
272 int lowestLineNum = -1;
273 Cell smallestCell = null;
274 foreach (Cell cell in cells)
276 if (lowestLineNum == -1 || cell.CellLabel.StartLineNumber < lowestLineNum)
279 lowestLineNum = cell.CellLabel.StartLineNumber;
282 Debug.Assert(smallestCell != null && lowestLineNum >= 0);
283 EdmSchemaError edmSchemaError = new EdmSchemaError(message, (int)ViewGenErrorCode.MissingExtentMapping,
284 EdmSchemaErrorSeverity.Error, containerMapping.SourceLocation, containerMapping.StartLineNumber,
285 containerMapping.StartLinePosition, null);
286 ErrorLog.Record record = new ErrorLog.Record(edmSchemaError);
287 errorLog.AddEntry(record);
298 #region Static Helpers
299 private static bool DoesCellGroupContainEntitySet(CellGroup group, EntitySetBase entity)
301 foreach (Cell cell in group)
303 if (cell.GetLeftQuery(ViewTarget.QueryView).Extent.Equals(entity))
313 internal override void ToCompactString(StringBuilder builder)