/// <summary> /// This method compiles all the user defined query views in the <paramref name="entityContainerMapping"/>. /// </summary> private static void CompileUserDefinedQueryViews(StorageEntityContainerMapping entityContainerMapping, Dictionary <EntitySetBase, GeneratedView> userDefinedQueryViewsDict, Dictionary <OfTypeQVCacheKey, GeneratedView> userDefinedQueryViewsOfTypeDict, IList <EdmSchemaError> errors) { ConfigViewGenerator config = new ConfigViewGenerator(); foreach (StorageSetMapping setMapping in entityContainerMapping.AllSetMaps) { if (setMapping.QueryView != null) { GeneratedView generatedView; if (!userDefinedQueryViewsDict.TryGetValue(setMapping.Set, out generatedView)) { // Parse the view so that we will get back any errors in the view. if (GeneratedView.TryParseUserSpecifiedView(setMapping, setMapping.Set.ElementType, setMapping.QueryView, true, // includeSubtypes entityContainerMapping.StorageMappingItemCollection, config, /*out*/ errors, out generatedView)) { // Add first QueryView userDefinedQueryViewsDict.Add(setMapping.Set, generatedView); } // Add all type-specific QueryViews foreach (OfTypeQVCacheKey key in setMapping.GetTypeSpecificQVKeys()) { Debug.Assert(key.First.Equals(setMapping.Set)); if (GeneratedView.TryParseUserSpecifiedView(setMapping, key.Second.First, // type setMapping.GetTypeSpecificQueryView(key), key.Second.Second, // includeSubtypes entityContainerMapping.StorageMappingItemCollection, config, /*out*/ errors, out generatedView)) { userDefinedQueryViewsOfTypeDict.Add(key, generatedView); } } } } } }
//Collect the names of the entitysetbases and the generated views from //the generated type into a string so that we can produce a hash over it. private void SerializedAddGeneratedViews(MetadataWorkspace workspace, EntityViewContainer viewContainer, Dictionary <EntitySetBase, GeneratedView> extentMappingViews) { foreach (KeyValuePair <string, string> extentView in viewContainer.ExtentViews) { EntityContainer entityContainer = null; EntitySetBase extent = null; string extentFullName = extentView.Key; int extentNameIndex = extentFullName.LastIndexOf('.'); if (extentNameIndex != -1) { string entityContainerName = extentFullName.Substring(0, extentNameIndex); string extentName = extentFullName.Substring(extentFullName.LastIndexOf('.') + 1); if (!workspace.TryGetItem <EntityContainer>(entityContainerName, DataSpace.CSpace, out entityContainer)) { workspace.TryGetItem <EntityContainer>(entityContainerName, DataSpace.SSpace, out entityContainer); } if (entityContainer != null) { entityContainer.BaseEntitySets.TryGetValue(extentName, false, out extent); } } if (extent == null) { throw new MappingException(System.Data.Entity.Strings.Generated_Views_Invalid_Extent(extentFullName)); } //Create a Generated view and cache it GeneratedView generatedView; //Add the view to the local dictionary if (!extentMappingViews.TryGetValue(extent, out generatedView)) { generatedView = GeneratedView.CreateGeneratedView( extent, null, // edmType null, // commandTree extentView.Value, // eSQL m_storageMappingItemCollection, new ConfigViewGenerator()); extentMappingViews.Add(extent, generatedView); } } }
/// <summary> /// Returns the update or query view for an Extent as a /// string. /// There are a series of steps that we go through for discovering a view for an extent. /// To start with we assume that we are working with Generated Views. To find out the /// generated view we go to the ObjectItemCollection and see if it is not-null. If the ObjectItemCollection /// is non-null, we get the view generation assemblies that it might have cached during the /// Object metadata discovery.If there are no view generation assemblies we switch to the /// runtime view generation strategy. If there are view generation assemblies, we get the list and /// go through them and see if there are any assemblies that are there from which we have not already loaded /// the views. We collect the views from assemblies that we have not already collected from earlier. /// If the ObjectItemCollection is null and we are in the view generation mode, that means that /// the query or update is issued from the Value layer and this is the first time view has been asked for. /// The compile time view gen for value layer queries will work for very simple scenarios. /// If the users wants to get the performance benefit, they should call MetadataWorkspace.LoadFromAssembly. /// At this point we go through the referenced assemblies of the entry assembly( this wont work for Asp.net /// or if the viewgen assembly was not referenced by the executing application). /// and try to see if there were any view gen assemblies. If there are, we collect the views for all extents. /// Once we have all the generated views gathered, we try to get the view for the extent passed in. /// If we find one we will return it. If we can't find one an exception will be thrown. /// If there were no view gen assemblies either in the ObjectItemCollection or in the list of referenced /// assemblies of calling assembly, we change the mode to runtime view generation and will continue to /// be in that mode for the rest of the lifetime of the mapping item collection. /// </summary> internal GeneratedView GetGeneratedView(EntitySetBase extent, MetadataWorkspace workspace, StorageMappingItemCollection storageMappingItemCollection) { //First check if we have collected a view from user-defined query views //Dont need to worry whether to generate Query view or update viw, because that is relative to the extent. GeneratedView view; if (TryGetUserDefinedQueryView(extent, out view)) { return(view); } //If this is a foreign key association, manufacture a view on the fly. if (extent.BuiltInTypeKind == BuiltInTypeKind.AssociationSet) { AssociationSet aSet = (AssociationSet)extent; if (aSet.ElementType.IsForeignKey) { if (m_config.IsViewTracing) { Helpers.StringTraceLine(String.Empty); Helpers.StringTraceLine(String.Empty); Helpers.FormatTraceLine("================= Generating FK Query View for: {0} =================", aSet.Name); Helpers.StringTraceLine(String.Empty); Helpers.StringTraceLine(String.Empty); } // Although we expose a collection of constraints in the API, there is only ever one constraint. Debug.Assert(aSet.ElementType.ReferentialConstraints.Count == 1, "aSet.ElementType.ReferentialConstraints.Count == 1"); ReferentialConstraint rc = aSet.ElementType.ReferentialConstraints.Single(); EntitySet dependentSet = aSet.AssociationSetEnds[rc.ToRole.Name].EntitySet; EntitySet principalSet = aSet.AssociationSetEnds[rc.FromRole.Name].EntitySet; DbExpression qView = dependentSet.Scan(); // Introduce an OfType view if the dependent end is a subtype of the entity set EntityType dependentType = MetadataHelper.GetEntityTypeForEnd((AssociationEndMember)rc.ToRole); EntityType principalType = MetadataHelper.GetEntityTypeForEnd((AssociationEndMember)rc.FromRole); if (dependentSet.ElementType.IsBaseTypeOf(dependentType)) { qView = qView.OfType(TypeUsage.Create(dependentType)); } if (rc.FromRole.RelationshipMultiplicity == RelationshipMultiplicity.ZeroOrOne) { // Filter out instances with existing relationships. qView = qView.Where(e => { DbExpression filter = null; foreach (EdmProperty fkProp in rc.ToProperties) { DbExpression notIsNull = e.Property(fkProp).IsNull().Not(); filter = null == filter ? notIsNull : filter.And(notIsNull); } return(filter); }); } qView = qView.Select(e => { List <DbExpression> ends = new List <DbExpression>(); foreach (AssociationEndMember end in aSet.ElementType.AssociationEndMembers) { if (end.Name == rc.ToRole.Name) { var keyValues = new List <KeyValuePair <string, DbExpression> >(); foreach (EdmMember keyMember in dependentSet.ElementType.KeyMembers) { keyValues.Add(e.Property((EdmProperty)keyMember)); } ends.Add(dependentSet.RefFromKey(DbExpressionBuilder.NewRow(keyValues), dependentType)); } else { // Manufacture a key using key values. var keyValues = new List <KeyValuePair <string, DbExpression> >(); foreach (EdmMember keyMember in principalSet.ElementType.KeyMembers) { int offset = rc.FromProperties.IndexOf((EdmProperty)keyMember); keyValues.Add(e.Property(rc.ToProperties[offset])); } ends.Add(principalSet.RefFromKey(DbExpressionBuilder.NewRow(keyValues), principalType)); } } return(TypeUsage.Create(aSet.ElementType).New(ends)); }); return(GeneratedView.CreateGeneratedViewForFKAssociationSet(aSet, aSet.ElementType, new DbQueryCommandTree(workspace, DataSpace.SSpace, qView), storageMappingItemCollection, m_config)); } } // If no User-defined QV is found, call memoized View Generation procedure. Dictionary <EntitySetBase, GeneratedView> generatedViews = m_generatedViewsMemoizer.Evaluate(extent.EntityContainer); if (!generatedViews.TryGetValue(extent, out view)) { throw EntityUtil.InvalidOperation(System.Data.Entity.Strings.Mapping_Views_For_Extent_Not_Generated( (extent.EntityContainer.DataSpace == DataSpace.SSpace)?"Table":"EntitySet", extent.Name)); } return(view); }
/// <summary> /// Tries to generate the Oftype or OfTypeOnly query view for a given entity set and type. /// Returns false if the view could not be generated. /// Possible reasons for failing are /// 1) Passing in OfTypeOnly on an abstract type /// 2) In user-specified query views mode a query for the given type is absent /// </summary> internal bool TryGetGeneratedViewOfType(MetadataWorkspace workspace, EntitySetBase entity, EntityTypeBase type, bool includeSubtypes, out GeneratedView generatedView) { OfTypeQVCacheKey key = new OfTypeQVCacheKey(entity, new Pair <EntityTypeBase, bool>(type, includeSubtypes)); generatedView = this.m_generatedViewOfTypeMemoizer.Evaluate(key); return(generatedView != null); }
/// <summary> /// Generates a single query view for a given Extent and type. It is used to generate OfType and OfTypeOnly views. /// </summary> /// <param name="includeSubtypes">Whether the view should include extents that are subtypes of the given entity</param> private bool TryGenerateQueryViewOfType(EntityContainer entityContainer, EntitySetBase entity, EntityTypeBase type, bool includeSubtypes, out GeneratedView generatedView) { Debug.Assert(entityContainer != null); Debug.Assert(entity != null); Debug.Assert(type != null); if (type.Abstract) { generatedView = null; return(false); } //Get the mapping that has the entity container mapped. StorageEntityContainerMapping entityContainerMap = MappingMetadataHelper.GetEntityContainerMap(m_storageMappingItemCollection, entityContainer); Debug.Assert(!entityContainerMap.IsEmpty, "There are no entity set maps"); bool success; ViewGenResults viewGenResults = ViewgenGatekeeper.GenerateTypeSpecificQueryView(entityContainerMap, m_config, entity, type, includeSubtypes, out success); if (!success) { generatedView = null; return(false); //could not generate view } KeyToListMap <EntitySetBase, GeneratedView> extentMappingViews = viewGenResults.Views; if (viewGenResults.HasErrors) { throw new MappingException(Helper.CombineErrorMessage(viewGenResults.Errors)); } Debug.Assert(extentMappingViews.AllValues.Count() == 1, "Viewgen should have produced only one view"); generatedView = extentMappingViews.AllValues.First(); return(true); }
/// <summary> /// this method will be called in metadatworkspace, the signature is the same as the one in ViewDictionary /// </summary> /// <param name="workspace"></param> /// <param name="entity"></param> /// <param name="type"></param> /// <param name="includeSubtypes"></param> /// <param name="generatedView"></param> /// <returns></returns> internal bool TryGetGeneratedViewOfType(MetadataWorkspace workspace, EntitySetBase entity, EntityTypeBase type, bool includeSubtypes, out GeneratedView generatedView) { return(this.m_viewDictionary.TryGetGeneratedViewOfType(workspace, entity, type, includeSubtypes, out generatedView)); }