private static string GetTypeNameMarkdown(string typeName, EntityFramework entityFramework) { string propertyType = typeName; if (propertyType.StartsWith(CollectionPrefix) && propertyType.EndsWith(")")) { propertyType = propertyType.Substring(CollectionPrefix.Length, propertyType.Length - CollectionPrefix.Length - 1); return(GetTypeNameMarkdown(propertyType, entityFramework) + " collection"); } IODataNavigable type = entityFramework.DataServices.Schemas.LookupNavigableType(propertyType); var simpleType = type as ODataSimpleType; if (simpleType != null) { return(simpleType.Type.ToString()); } return($"[{type.TypeIdentifier}]({type.TypeIdentifier}.md)"); }
public static IODataNavigable NavigateByFunction(this IODataNavigable source, string component, EntityFramework edmx, bool isLastSegment) { var matches = edmx.DataServices.Schemas.SelectMany(s => s.Functions). Where(f => f.IsBound && (f.Name == component || f.ParameterizedName == component) && f.ReturnType?.Type != null && f.Parameters.Any(p => p.Name == "bindingParameter" && p.Type.TypeOnly() == source.TypeIdentifier.TypeOnly())). ToList(); if (matches.Any()) { foreach (var m in matches) { m.IsComposable = true; } var match = matches.First().ReturnType.Type; return(edmx.ResourceWithIdentifier <IODataNavigable>(match)); } var otherCaseName = edmx.DataServices.Schemas.SelectMany(s => s.Functions). Where(f => f.IsBound && (f.Name.IEquals(component) || f.ParameterizedName.IEquals(component)) && f.Parameters.Any(p => p.Name == "bindingParameter" && p.Type.TypeOnly() == source.TypeIdentifier.TypeOnly())). Select(f => f.Name.IEquals(component) ? f.Name : f.ParameterizedName). FirstOrDefault(); if (otherCaseName != null) { throw new ArgumentException($"ERROR: case mismatch between URL segment '{component}' and schema element '{otherCaseName}'"); } return(null); }
/// <summary> /// Resolve an IODataNavigable instance into a type identifier string. /// </summary> /// <param name="edmx"></param> /// <param name="type"></param> /// <returns></returns> public static string LookupIdentifierForType(this EntityFramework edmx, IODataNavigable type) { if (type is ODataSimpleType) { return(((ODataSimpleType)type).Type.ODataResourceName()); } else if (type is ODataCollection) { return("Collection(" + ((ODataCollection)type).TypeIdentifier + ")"); } foreach (var schema in edmx.DataServices.Schemas) { if (type is EntityType) { foreach (var et in schema.EntityTypes) { if (et == type) { return(schema.Namespace + "." + et.Name); } } } else if (type is ComplexType) { foreach (var ct in schema.ComplexTypes) { if (ct == type) { return(schema.Namespace + "." + ct.Name); } } } } return(null); }
/// <summary> /// Walks the requestPath through the resources / entities defined in the edmx and resolves /// the type of request represented by the path /// </summary> /// <param name="requestPath"></param> /// <param name="requestMethod"></param> /// <param name="edmx"></param> /// <returns></returns> private static ODataTargetInfo ParseRequestTargetType(string requestPath, MethodCollection requestMethodCollection, EntityFramework edmx) { string[] requestParts = requestPath.Substring(1).Split(new char[] { '/' }); EntityContainer entryPoint = (from s in edmx.DataServices.Schemas where s.EntityContainers.Count > 0 select s.EntityContainers.FirstOrDefault()).SingleOrDefault(); if (entryPoint == null) { throw new InvalidOperationException("Couldn't locate an EntityContainer to begin target resolution"); } IODataNavigable currentObject = entryPoint; IODataNavigable previousObject = null; for (int i = 0; i < requestParts.Length; i++) { string uriPart = requestParts[i]; IODataNavigable nextObject = null; if (uriPart == "{var}") { try { nextObject = currentObject.NavigateByEntityTypeKey(edmx); } catch (Exception ex) { throw new NotSupportedException("Unable to navigation into EntityType by key: " + currentObject.TypeIdentifier + " (" + ex.Message + ")"); } } else { nextObject = currentObject.NavigateByUriComponent(uriPart, edmx); } if (nextObject == null && i == requestParts.Length - 1) { // The last component wasn't known already, so that means we have a new thing. // We assume that if the uriPart doesnt' have a namespace that this is a navigation property that isn't documented. // TODO: We may need to be smarter about this if we allow actions without namespaces. If that's the case, we could look at the request // method to figure out of this appears to be an action (POST?) or a navigationProperty (GET?) return(new ODataTargetInfo { Name = uriPart, Classification = uriPart.HasNamespace() ? ODataTargetClassification.Unknown : ODataTargetClassification.NavigationProperty, QualifiedType = edmx.LookupIdentifierForType(currentObject) }); } else if (nextObject == null) { throw new InvalidOperationException( string.Format("Uri path requires navigating into unknown object hierarchy: missing property '{0}' on '{1}'", uriPart, currentObject.TypeIdentifier)); } previousObject = currentObject; currentObject = nextObject; } var response = new ODataTargetInfo { Name = requestParts.Last(), QualifiedType = edmx.LookupIdentifierForType(currentObject) }; if (currentObject is EntityType) { response.Classification = ODataTargetClassification.EntityType; } else if (currentObject is EntityContainer) { response.Classification = ODataTargetClassification.EntityContainer; } else if (currentObject is ODataSimpleType) { response.Classification = ODataTargetClassification.SimpleType; } else if (currentObject is ODataCollection) { if (previousObject != entryPoint) { response.Classification = ODataTargetClassification.NavigationProperty; response.QualifiedType = edmx.LookupIdentifierForType(previousObject); } else { response.Classification = ODataTargetClassification.EntitySet; } } else if (currentObject is ComplexType) { throw new NotSupportedException(string.Format("Encountered a ComplexType. This is probably a doc bug where type '{0}' should be defined with keyProperty to be an EntityType", currentObject.TypeIdentifier)); } else { throw new NotSupportedException(string.Format("Unhandled object type: {0}", currentObject.GetType().Name)); } return(response); }