Code:
/ 4.0 / 4.0 / untmp / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / fx / src / DataEntity / System / Data / Common / CommandTrees / Internal / ViewSimplifier.cs / 1305376 / ViewSimplifier.cs
//---------------------------------------------------------------------- //// Copyright (c) Microsoft Corporation. All rights reserved. // // // @owner [....], [....] // @backupOwner [....] //--------------------------------------------------------------------- using System.Data.Common.CommandTrees; using System.Collections.Generic; using System.Data.Metadata.Edm; using System.Diagnostics; using System.Data.Common.Utils; using System.Linq; using System.Globalization; using System.Data.Common.CommandTrees.ExpressionBuilder; using System.Data.Common.CommandTrees.Internal; namespace System.Data.Common.CommandTrees.Internal { ////// Utility class that walks a mapping view and returns a simplified expression with projection /// nodes collapsed. Specifically recognizes the following common pattern in mapping views: /// /// outerProject(outerBinding(innerProject(innerBinding, innerNew)), outerProjection) /// /// Recognizes simple disciminator patterns of the form: /// /// select /// case when Disc = value1 then value Type1(...) /// case when Disc = value2 then value Type2(...) /// ... /// /// Recognizes redundant case statement of the form: /// /// select /// case when (case when Predicate1 then true else false) ... /// /// internal class ViewSimplifier { internal static DbQueryCommandTree SimplifyView(EntitySetBase extent, DbQueryCommandTree view) { ViewSimplifier vs = new ViewSimplifier(view.MetadataWorkspace, extent); view = vs.Simplify(view); return view; } private readonly MetadataWorkspace metadata; private readonly EntitySetBase extent; private ViewSimplifier(MetadataWorkspace mws, EntitySetBase viewTarget) { this.metadata = mws; this.extent = viewTarget; } private DbQueryCommandTree Simplify(DbQueryCommandTree view) { var simplifier = PatternMatchRuleProcessor.Create( // determines if an expression is of the form outerProject(outerProjection(innerProject(innerNew))) PatternMatchRule.Create(Pattern_CollapseNestedProjection, ViewSimplifier.CollapseNestedProjection), // A case statement can potentially be simplified PatternMatchRule.Create(Pattern_Case, ViewSimplifier.SimplifyCaseStatement), // Nested TPH discriminator pattern can be converted to the expected TPH discriminator pattern PatternMatchRule.Create(Pattern_NestedTphDiscriminator, ViewSimplifier.SimplifyNestedTphDiscriminator), // Entity constructors may be augmented with FK-based related entity refs PatternMatchRule.Create(Pattern_EntityConstructor, this.AddFkRelatedEntityRefs) ); DbExpression queryExpression = view.Query; queryExpression = simplifier(queryExpression); view = DbQueryCommandTree.FromValidExpression(view.MetadataWorkspace, view.DataSpace, queryExpression); return view; } #region Navigation simplification support by adding FK-based related entity refs private static readonly FuncPattern_EntityConstructor = Patterns.MatchProject( Patterns.AnyExpression, Patterns.And( Patterns.MatchEntityType, Patterns.Or ( Patterns.MatchNewInstance(), Patterns.MatchCase(Patterns.AnyExpressions, Patterns.MatchForAll(Patterns.MatchNewInstance()), Patterns.MatchNewInstance()) ) ) ); private bool doNotProcess; private DbExpression AddFkRelatedEntityRefs(DbExpression viewConstructor) { // If the extent being simplified is not a C-Space entity set, or if it has already // been processed by the simplifier, then keep the original expression by returning // null. // if (this.doNotProcess) { return null; } if(this.extent.BuiltInTypeKind != BuiltInTypeKind.EntitySet || this.extent.EntityContainer.DataSpace != DataSpace.CSpace) { this.doNotProcess = true; return null; } // Get a reference to the entity set being simplified, and find all the foreign key // (foreign key) associations for which the association set references that entity set, // with either association end. // EntitySet targetSet = (EntitySet)this.extent; var relSets = targetSet.EntityContainer.BaseEntitySets .Where(es => es.BuiltInTypeKind == BuiltInTypeKind.AssociationSet) .Cast () .Where(assocSet => assocSet.ElementType.IsForeignKey && assocSet.AssociationSetEnds.Any(se => se.EntitySet == targetSet) ) .ToList(); // If no foreign key association sets that reference the entity set are present, then // no further processing is necessary, because FK-based related entity references cannot // be computed and added to the entities constructed for the entity set. if (relSets.Count == 0) { this.doNotProcess = true; return null; } // For every relationship set that references this entity set, the relationship type and // foreign key constraint are used to determine if the entity set is the dependent set. // If it is the dependent set, then it is possible to augment the view definition with a // related entity ref that represents the navigation of the relationship set's relationship // from the dependent end (this entity set) to the the principal end (the entity set that // is referenced by the other association set end of the relationship set). // var principalSetsAndDependentTypes = new HashSet >(); foreach (AssociationSet relSet in relSets) { // Retrieve the single referential constraint from the foreign key association, and // use it to determine whether the association set end that represents the dependent // end of the association references this entity set. // var fkConstraint = relSet.ElementType.ReferentialConstraints[0]; var dependentSetEnd = relSet.AssociationSetEnds[fkConstraint.ToRole.Name]; if (dependentSetEnd.EntitySet == targetSet) { EntityType requiredSourceNavType = (EntityType)TypeHelpers.GetEdmType (dependentSetEnd.CorrespondingAssociationEndMember.TypeUsage).ElementType; var principalSetEnd = relSet.AssociationSetEnds[fkConstraint.FromRole.Name]; // Record the entity type that an element of this dependent entity set must have in order // to be a valid navigation source for the relationship set's relationship, along with the // association set end for the destination (principal) end of the navigation and the FK // constraint that is associated with the relationship type. This information may be used // later to construct a related entity ref for any entity constructor expression in the view // that produces an entity of the required source type or a subtype. // principalSetsAndDependentTypes.Add(Tuple.Create(requiredSourceNavType, principalSetEnd, fkConstraint)); } } // If no foreign key association sets that use the entity set as the dependent set are present, // then no further processing is possible, since FK-based related entity refs can only be added // to the view definition for navigations from the dependent end of the relationship to the principal. // if (principalSetsAndDependentTypes.Count == 0) { this.doNotProcess = true; return null; } // This rule supports a view that is capped with a projection of the form // (input).Project(x => new Entity()) // or // (input).Project(x => CASE WHEN (condition1) THEN new Entity1() ELSE WHEN (condition2) THEN new Entity2()... ELSE new EntityN()) // where every new instance expression Entity1()...EntityN() constructs an entity of a type // that is compatible with the entity set's element type. // Here, the list of all DbNewInstanceExpressions contained in the projection is remembered, // along with any CASE statement conditions, if present. These expressions will be updated // if necessary and used to build a new capping projection if any of the entity constructors // are augmented with FK-based related entity references. // DbProjectExpression entityProject = (DbProjectExpression)viewConstructor; List constructors = new List (); List conditions = null; if (entityProject.Projection.ExpressionKind == DbExpressionKind.Case) { // If the projection is a DbCaseExpression, then every result must be a DbNewInstanceExpression DbCaseExpression discriminatedConstructor = (DbCaseExpression)entityProject.Projection; conditions = new List (discriminatedConstructor.When.Count); for (int idx = 0; idx < discriminatedConstructor.When.Count; idx++) { conditions.Add(discriminatedConstructor.When[idx]); constructors.Add((DbNewInstanceExpression)discriminatedConstructor.Then[idx]); } constructors.Add((DbNewInstanceExpression)discriminatedConstructor.Else); } else { // Otherwise, the projection must be a single DbNewInstanceExpression constructors.Add((DbNewInstanceExpression)entityProject.Projection); } bool rebuildView = false; for (int idx = 0; idx < constructors.Count; idx++) { DbNewInstanceExpression entityConstructor = constructors[idx]; EntityType constructedEntityType = TypeHelpers.GetEdmType (entityConstructor.ResultType); List relatedRefs = principalSetsAndDependentTypes .Where(psdt => constructedEntityType == psdt.Item1 || constructedEntityType.IsSubtypeOf(psdt.Item1)) .Select(psdt => RelatedEntityRefFromAssociationSetEnd(constructedEntityType, entityConstructor, psdt.Item2, psdt.Item3)).ToList(); if (relatedRefs.Count > 0) { if (entityConstructor.HasRelatedEntityReferences) { relatedRefs = entityConstructor.RelatedEntityReferences.Concat(relatedRefs).ToList(); } entityConstructor = DbExpressionBuilder.CreateNewEntityWithRelationshipsExpression(constructedEntityType, entityConstructor.Arguments, relatedRefs); constructors[idx] = entityConstructor; rebuildView = true; } } // Default to returning null to indicate that this rule did not produce a modified expression // DbExpression result = null; if (rebuildView) { // rebuildView is true, so entity constructing DbNewInstanceExpression(s) were encountered // and updated with additional related entity refs. The DbProjectExpression that caps the // view definition therefore needs to be rebuilt and returned as the result of this rule. // if (conditions != null) { // The original view definition projection was a DbCaseExpression. // The new expression is also a DbCaseExpression that uses the conditions from the // original expression together with the updated result expressions to produce the // new capping projection. // List whens = new List (conditions.Count); List thens = new List (conditions.Count); for (int idx = 0; idx < conditions.Count; idx++) { whens.Add(conditions[idx]); thens.Add(constructors[idx]); } result = entityProject.Input.Project(DbExpressionBuilder.Case(whens, thens, constructors[conditions.Count])); } else { // Otherwise, the capping projection consists entirely of the updated DbNewInstanceExpression. // result = entityProject.Input.Project(constructors[0]); } } // Regardless of whether or not the view was updated, this rule should not be applied again during rule processing this.doNotProcess = true; return result; } private static DbRelatedEntityRef RelatedEntityRefFromAssociationSetEnd(EntityType constructedEntityType, DbNewInstanceExpression entityConstructor, AssociationSetEnd principalSetEnd, ReferentialConstraint fkConstraint) { EntityType principalEntityType = (EntityType)TypeHelpers.GetEdmType (fkConstraint.FromRole.TypeUsage).ElementType; IList principalKeyValues = null; // Create Entity Property/DbExpression value pairs from the entity constructor DbExpression, // then join these with the principal/dependent property pairs from the FK constraint // to produce principal property name/DbExpression value pairs from which to create the principal ref. // // Ideally the code would be as below, but anonymous types break asmmeta: //var keyPropAndValue = // from pv in constructedEntityType.Properties.Select((p, idx) => new { DependentProperty = p, Value = entityConstructor.Arguments[idx] }) // join ft in fkConstraint.FromProperties.Select((fp, idx) => new { PrincipalProperty = fp, DependentProperty = fkConstraint.ToProperties[idx] }) // on pv.DependentProperty equals ft.DependentProperty // select new { PrincipalProperty = ft.PrincipalProperty.Name, Value = pv.Value }; // var keyPropAndValue = from pv in constructedEntityType.Properties.Select((p, idx) => Tuple.Create(p, entityConstructor.Arguments[idx])) // new { DependentProperty = p, Value = entityConstructor.Arguments[idx] }) join ft in fkConstraint.FromProperties.Select((fp, idx) => Tuple.Create(fp, fkConstraint.ToProperties[idx])) //new { PrincipalProperty = fp, DependentProperty = fkConstraint.ToProperties[idx] }) on pv.Item1 equals ft.Item2 //pv.DependentProperty equals ft.DependentProperty select Tuple.Create(ft.Item1.Name, pv.Item2); // new { PrincipalProperty = ft.PrincipalProperty.Name, Value = pv.Value }; // If there is only a single property in the principal's key, then there is no ordering concern. // Otherwise, create a dictionary of principal key property name to DbExpression value so that // when used as the arguments to the ref expression, the dependent property values - used here // as principal key property values - are in the correct order, which is the same order as the // key members themselves. // if (fkConstraint.FromProperties.Count == 1) { var singleKeyNameAndValue = keyPropAndValue.Single(); Debug.Assert(singleKeyNameAndValue.Item1 == fkConstraint.FromProperties[0].Name, "Unexpected single key property name"); principalKeyValues = new[] { singleKeyNameAndValue.Item2 }; } else { var keyValueMap = keyPropAndValue.ToDictionary(pav => pav.Item1, pav => pav.Item2, StringComparer.Ordinal); principalKeyValues = principalEntityType.KeyMemberNames.Select(memberName => keyValueMap[memberName]).ToList(); } // Create the ref to the principal entity based on the (now correctly ordered) key value expressions. // DbRefExpression principalRef = principalSetEnd.EntitySet.CreateRef(principalEntityType, principalKeyValues); DbRelatedEntityRef result = DbExpressionBuilder.CreateRelatedEntityRef(fkConstraint.ToRole, fkConstraint.FromRole, principalRef); return result; } #endregion #region Nested TPH Discriminator simplification /// /// Matches the nested TPH discriminator pattern produced by view generation /// private static readonly FuncPattern_NestedTphDiscriminator = Patterns.MatchProject( Patterns.MatchFilter( Patterns.MatchProject( Patterns.MatchFilter( Patterns.AnyExpression, Patterns.Or( Patterns.MatchKind(DbExpressionKind.Equals), Patterns.MatchKind(DbExpressionKind.Or) ) ), Patterns.And( Patterns.MatchRowType, Patterns.MatchNewInstance( Patterns.MatchForAll( Patterns.Or( Patterns.And( Patterns.MatchNewInstance(), Patterns.MatchComplexType ), Patterns.MatchKind(DbExpressionKind.Property), Patterns.MatchKind(DbExpressionKind.Case) ) ) ) ) ), Patterns.Or( Patterns.MatchKind(DbExpressionKind.Property), Patterns.MatchKind(DbExpressionKind.Or) ) ), Patterns.And( Patterns.MatchEntityType, Patterns.MatchCase( Patterns.MatchForAll(Patterns.MatchKind(DbExpressionKind.Property)), Patterns.MatchForAll(Patterns.MatchKind(DbExpressionKind.NewInstance)), Patterns.MatchKind(DbExpressionKind.NewInstance) ) ) ); /// /// Converts the DbExpression equivalent of: /// /// SELECT CASE /// WHEN a._from0 THEN SUBTYPE1() /// ... /// WHEN a._from[n-2] THEN SUBTYPE_n-1() /// ELSE SUBTYPE_n /// FROM /// SELECT /// b.C1..., b.Cn /// CASE WHEN b.Discriminator = SUBTYPE1_Value THEN true ELSE false AS _from0 /// ... /// CASE WHEN b.Discriminator = SUBTYPE_n_Value THEN true ELSE false AS _from[n-1] /// FROM TSet AS b /// WHERE b.Discriminator = SUBTYPE1_Value... OR x.Discriminator = SUBTYPE_n_Value /// AS a /// WHERE a._from0... OR a._from[n-1] /// /// into the DbExpression equivalent of the following, which is matched as a TPH discriminator /// by the private static DbExpression SimplifyNestedTphDiscriminator(DbExpression expression) { DbProjectExpression entityProjection = (DbProjectExpression)expression; DbFilterExpression booleanColumnFilter = (DbFilterExpression)entityProjection.Input.Expression; DbProjectExpression rowProjection = (DbProjectExpression)booleanColumnFilter.Input.Expression; DbFilterExpression discriminatorFilter = (DbFilterExpression)rowProjection.Input.Expression; Listclass and so allows a /// to be produced for the view, which would not otherwise be possible. Note that C1 through Cn /// are only allowed to be scalars or complex type constructors based on direct property references /// to the store entity set's scalar properties. /// /// SELECT CASE /// WHEN y.Discriminator = SUBTTYPE1_Value THEN SUBTYPE1() /// ... /// WHEN y.Discriminator = SUBTYPE_n-1_Value THEN SUBTYPE_n-1() /// ELSE SUBTYPE_n() /// FROM /// SELECT x.C1..., x.Cn, Discriminator FROM TSet AS x /// WHERE x.Discriminator = SUBTYPE1_Value... OR x.Discriminator = SUBTYPE_n_Value /// AS y /// /// predicates = FlattenOr(booleanColumnFilter.Predicate).ToList(); List propertyPredicates = predicates.OfType () .Where(px => px.Instance.ExpressionKind == DbExpressionKind.VariableReference && ((DbVariableReferenceExpression)px.Instance).VariableName == booleanColumnFilter.Input.VariableName).ToList(); if (predicates.Count != propertyPredicates.Count) { return null; } List predicateColumnNames = propertyPredicates.Select(px => px.Property.Name).ToList(); Dictionary
Link Menu

This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- RegisteredExpandoAttribute.cs
- XpsSerializerFactory.cs
- UpdateProgress.cs
- WorkItem.cs
- TransactionFlowBindingElement.cs
- Attributes.cs
- HealthMonitoringSectionHelper.cs
- SerializationInfo.cs
- ListBase.cs
- Guid.cs
- HwndHost.cs
- NegotiateStream.cs
- SystemIPv4InterfaceProperties.cs
- GridSplitter.cs
- FragmentNavigationEventArgs.cs
- GeometryValueSerializer.cs
- SecurityCriticalDataForSet.cs
- ObjectManager.cs
- RoleManagerEventArgs.cs
- ResourcePermissionBase.cs
- SoapDocumentServiceAttribute.cs
- GraphicsContainer.cs
- XmlDataSourceNodeDescriptor.cs
- DesignerToolboxInfo.cs
- OleDbWrapper.cs
- ProfessionalColors.cs
- BaseCAMarshaler.cs
- FormsIdentity.cs
- DetailsViewUpdateEventArgs.cs
- FloaterParagraph.cs
- DbXmlEnabledProviderManifest.cs
- LinkConverter.cs
- CodeEntryPointMethod.cs
- StatusBarPanelClickEvent.cs
- SafeEventLogWriteHandle.cs
- BufferBuilder.cs
- CustomValidator.cs
- bidPrivateBase.cs
- SplashScreen.cs
- DbInsertCommandTree.cs
- RemoteTokenFactory.cs
- List.cs
- JournalEntryStack.cs
- ExternalException.cs
- ConfigXmlElement.cs
- X509Certificate.cs
- CheckoutException.cs
- TemplatedAdorner.cs
- SmtpSpecifiedPickupDirectoryElement.cs
- QilChoice.cs
- CodeCompiler.cs
- ArrayTypeMismatchException.cs
- ActivationArguments.cs
- DefinitionUpdate.cs
- FlowchartDesigner.Helpers.cs
- CompModSwitches.cs
- RepeatInfo.cs
- XmlReflectionImporter.cs
- Buffer.cs
- MobileControlsSection.cs
- CompiledRegexRunnerFactory.cs
- ContentOnlyMessage.cs
- TextBoxRenderer.cs
- DataBindEngine.cs
- TableCellsCollectionEditor.cs
- EventsTab.cs
- SqlXml.cs
- HttpWebRequestElement.cs
- ByteStack.cs
- SqlLiftIndependentRowExpressions.cs
- ParameterReplacerVisitor.cs
- MatrixValueSerializer.cs
- ElementProxy.cs
- ToolStripMenuItem.cs
- Gdiplus.cs
- AnnotationAdorner.cs
- GorillaCodec.cs
- TextServicesCompartmentContext.cs
- ClockController.cs
- DispatchChannelSink.cs
- MultipartContentParser.cs
- XmlTypeAttribute.cs
- GridProviderWrapper.cs
- FileNotFoundException.cs
- AdvancedBindingPropertyDescriptor.cs
- ModelTypeConverter.cs
- Assert.cs
- NextPreviousPagerField.cs
- CommonDialog.cs
- FixedSOMImage.cs
- SqlMethodTransformer.cs
- TransactionScopeDesigner.cs
- InteropAutomationProvider.cs
- FactoryGenerator.cs
- TextBreakpoint.cs
- FileUpload.cs
- IISUnsafeMethods.cs
- AxisAngleRotation3D.cs
- DataGridViewAutoSizeColumnModeEventArgs.cs
- HttpValueCollection.cs