From e5786e50efa41188834b16e61b3b1483381ba8af Mon Sep 17 00:00:00 2001
From: Lisi Case <49213376+lisicase@users.noreply.github.com>
Date: Fri, 23 Sep 2022 12:01:30 -0700
Subject: [PATCH 01/17] Created repro of issue 701
---
AspNetCoreOData.sln | 11 +-
.../Controllers/SampleController.cs | 40 +++++
sample/Issue701_Repro/Issue701_Repro.csproj | 11 ++
sample/Issue701_Repro/Models/DataSource.cs | 168 ++++++++++++++++++
sample/Issue701_Repro/Models/Sample.cs | 33 ++++
sample/Issue701_Repro/Program.cs | 85 +++++++++
.../Properties/launchSettings.json | 31 ++++
sample/Issue701_Repro/Startup.cs | 87 +++++++++
.../appsettings.Development.json | 9 +
sample/Issue701_Repro/appsettings.json | 10 ++
10 files changed, 483 insertions(+), 2 deletions(-)
create mode 100644 sample/Issue701_Repro/Controllers/SampleController.cs
create mode 100644 sample/Issue701_Repro/Issue701_Repro.csproj
create mode 100644 sample/Issue701_Repro/Models/DataSource.cs
create mode 100644 sample/Issue701_Repro/Models/Sample.cs
create mode 100644 sample/Issue701_Repro/Program.cs
create mode 100644 sample/Issue701_Repro/Properties/launchSettings.json
create mode 100644 sample/Issue701_Repro/Startup.cs
create mode 100644 sample/Issue701_Repro/appsettings.Development.json
create mode 100644 sample/Issue701_Repro/appsettings.json
diff --git a/AspNetCoreOData.sln b/AspNetCoreOData.sln
index 64dd16091..b691f705b 100644
--- a/AspNetCoreOData.sln
+++ b/AspNetCoreOData.sln
@@ -1,7 +1,7 @@
Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio Version 17
-VisualStudioVersion = 17.0.31815.197
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.32901.82
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{2F0E102B-EB33-4025-BE56-7B8F9D2C4B8A}"
EndProject
@@ -27,6 +27,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ODataSampleCommon", "sample
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ODataAlternateKeySample", "sample\ODataAlternateKeySample\ODataAlternateKeySample.csproj", "{7B153669-A42F-4511-8BDB-587B3B27B2F3}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Issue701_Repro", "sample\Issue701_Repro\Issue701_Repro.csproj", "{C1070485-57AD-4904-B4C2-45F7F683565E}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -69,6 +71,10 @@ Global
{7B153669-A42F-4511-8BDB-587B3B27B2F3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7B153669-A42F-4511-8BDB-587B3B27B2F3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7B153669-A42F-4511-8BDB-587B3B27B2F3}.Release|Any CPU.Build.0 = Release|Any CPU
+ {C1070485-57AD-4904-B4C2-45F7F683565E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {C1070485-57AD-4904-B4C2-45F7F683565E}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {C1070485-57AD-4904-B4C2-45F7F683565E}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {C1070485-57AD-4904-B4C2-45F7F683565E}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -83,6 +89,7 @@ Global
{CE04E38B-547F-46C0-ABE4-F981E3A1874F} = {B1F86961-6958-4617-ACA4-C231F95AE099}
{647EFCFA-55A7-4F0A-AD40-4B6EB1BFCFFA} = {B1F86961-6958-4617-ACA4-C231F95AE099}
{7B153669-A42F-4511-8BDB-587B3B27B2F3} = {B1F86961-6958-4617-ACA4-C231F95AE099}
+ {C1070485-57AD-4904-B4C2-45F7F683565E} = {B1F86961-6958-4617-ACA4-C231F95AE099}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {540C9752-AAC0-49EA-BA60-78490C90FF86}
diff --git a/sample/Issue701_Repro/Controllers/SampleController.cs b/sample/Issue701_Repro/Controllers/SampleController.cs
new file mode 100644
index 000000000..841ce086c
--- /dev/null
+++ b/sample/Issue701_Repro/Controllers/SampleController.cs
@@ -0,0 +1,40 @@
+using Microsoft.AspNetCore.Mvc;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Microsoft.AspNetCore.OData.Formatter;
+using Microsoft.AspNetCore.OData.Formatter.Value;
+using Microsoft.AspNetCore.OData.Routing.Controllers;
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using Issue701_Repro.Models;
+using Microsoft.OData;
+using Microsoft.AspNetCore.OData.Query;
+
+namespace Issue701_nextLink.Tests.Controllers
+{
+ public class SampleController : ODataController
+ {
+ //[ApiVersion("1.0")]
+ // [HttpGet("v{v:apiVersion}/sample")]
+ [HttpGet("sample")]
+ [EnableQuery]
+ public IActionResult GetSampleAsync(/*[FromQuery] QueryStringParameters queryString*/)
+ {
+ return this.Ok(DataSource.GetSample());
+ }
+
+ //[ApiVersion("1.0")]
+ //[HttpGet("v{v:apiVersion}/sample/sampleitems")]
+ [HttpGet("sample/SItems")]
+ [EnableQuery(PageSize = 2)]
+ public IActionResult GetCatalogExamsAsync(/*[FromQuery] QueryStringParameters queryString*/)
+ {
+ var sItems = DataSource.GetSampleItems();
+ return this.Ok(sItems);
+ }
+ }
+}
\ No newline at end of file
diff --git a/sample/Issue701_Repro/Issue701_Repro.csproj b/sample/Issue701_Repro/Issue701_Repro.csproj
new file mode 100644
index 000000000..21e6c0786
--- /dev/null
+++ b/sample/Issue701_Repro/Issue701_Repro.csproj
@@ -0,0 +1,11 @@
+
+
+
+ net5.0
+
+
+
+
+
+
+
diff --git a/sample/Issue701_Repro/Models/DataSource.cs b/sample/Issue701_Repro/Models/DataSource.cs
new file mode 100644
index 000000000..58de412da
--- /dev/null
+++ b/sample/Issue701_Repro/Models/DataSource.cs
@@ -0,0 +1,168 @@
+/*using Microsoft.AspNetCore.Mvc.ViewFeatures;
+using System;
+using System.Collections.Generic;
+using System.Data.SqlTypes;
+using System.Linq;
+using System.Threading.Tasks;*/
+
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Linq;
+
+namespace Issue701_Repro.Models
+{
+ public static class DataSource
+ {
+ private static Sample _sample { get; set; }
+
+ public static Sample GetSample()
+ {
+ ensureData();
+ return _sample;
+ }
+
+ public static IEnumerable GetSampleItems()
+ {
+ ensureData();
+ return _sample.SItems;
+ }
+
+ private static void ensureData()
+ {
+ if (_sample == null)
+ {
+ // Create sample item guides
+ SampleItemGuide si1 = createSIG(1);
+ SampleItemGuide si2 = createSIG(2);
+ SampleItemGuide si3 = createSIG(3);
+
+ // Create sample items
+ SampleItems sampleItems1 = new SampleItems
+ {
+ Uid = "sampleitems1"
+ };
+ var items1 = new List();
+ items1.Add(si1);
+ items1.Add(si2);
+ items1.Add(si3);
+ sampleItems1.SampleItem_guide = items1;
+
+ SampleItems sampleItems2 = new SampleItems
+ {
+ Uid = "sampleitems2"
+ };
+ var items2 = new List();
+ items2.Add(si3);
+ sampleItems2.SampleItem_guide = items2;
+
+ // Create sample
+ _sample = new Sample();
+ var SItems = new List();
+ SItems.Add(sampleItems1);
+ SItems.Add(sampleItems2);
+ _sample.SItems = SItems;
+ }
+ }
+
+ private static SampleItemGuide createSIG(int num)
+ {
+ SampleItemGuide sig = new SampleItemGuide
+ {
+ Uid = "sampleuid" + num,
+ Type = "sampletype" + num
+ };
+ return sig;
+ }
+ }
+}
+
+/*********************** EXPECTED RESULT ***********************/
+
+// REQUEST: /sample/sampleitems?$expand=SampleItem_guide
+
+/*{
+ "@odata.context": "https://localhost:44335/v1.0/$metadata#sample/sampleitems(sampleitem_guide())",
+ "value": [
+ {
+ "uid": "sample.uid",
+
+ "sampleitem_guide": [
+ {
+ "uid": "sampleitem.uid1",
+ "type": "sampletype1"
+ },
+ {
+ "uid": "sampleitem.uid2",
+ "type": "sampletype2"
+ }
+ ]
+ }
+ ],
+ "@odata.nextLink": "https://localhost:44335/v1.0/sample/sampleitems?$skip=1"
+}*/
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/************************** PREVIOUS ITERATION ********************************/
+
+/*namespace Issue701_Repro2.Models
+{
+ public static class DataSource
+ {
+
+ private static Sample _sample { get; set; } = new Sample();
+
+ private static Sample GetSample()
+ {
+ // Create sample item guides
+ SampleItemGuide si1 = new SampleItemGuide
+ {
+ Uid = "sampleuid1",
+ Type = "sampletype1"
+ };
+ SampleItemGuide si2 = new SampleItemGuide
+ {
+ Uid = "sampleuid2",
+ Type = "sampletype2"
+ };
+ SampleItemGuide si3 = new SampleItemGuide
+ {
+ Uid = "sampleuid3",
+ Type = "sampletype3"
+ };
+
+ // Create sample items
+ SampleItems sampleItems1 = new SampleItems();
+ sampleItems1.SampleItem_guide.Append(si1);
+ sampleItems1.SampleItem_guide.Append(si2);
+ SampleItems sampleItems2 = new SampleItems();
+ sampleItems2.SampleItem_guide.Append(si3);
+
+ // Create sample
+ _sample = new Sample();
+ _sample.SItems.Append(sampleItems1);
+ _sample.SItems.Append(sampleItems2);
+
+ return _sample;
+ }
+ }
+}*/
\ No newline at end of file
diff --git a/sample/Issue701_Repro/Models/Sample.cs b/sample/Issue701_Repro/Models/Sample.cs
new file mode 100644
index 000000000..d89ca0688
--- /dev/null
+++ b/sample/Issue701_Repro/Models/Sample.cs
@@ -0,0 +1,33 @@
+using Microsoft.OData.ModelBuilder;
+using System.ComponentModel.DataAnnotations.Schema;
+using System.ComponentModel.DataAnnotations;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace Issue701_Repro.Models
+{
+ public class Sample
+ {
+ [Contained]
+ [ForeignKey("Uid")]
+ [AutoExpand]
+ public IEnumerable SItems { get; set; } = Enumerable.Empty();
+ }
+
+ public class SampleItems
+ {
+ [Key]
+ public string Uid { get; set; }
+
+ [AutoExpand]
+ [Contained]
+ public IEnumerable SampleItem_guide { get; set; } = System.Array.Empty();
+ }
+
+ public class SampleItemGuide
+ {
+ [Key]
+ public string Uid { get; set; }
+ public string Type { get; set; }
+ }
+}
diff --git a/sample/Issue701_Repro/Program.cs b/sample/Issue701_Repro/Program.cs
new file mode 100644
index 000000000..9f86800b7
--- /dev/null
+++ b/sample/Issue701_Repro/Program.cs
@@ -0,0 +1,85 @@
+using Issue701_Repro;
+using Microsoft.AspNetCore;
+using Microsoft.AspNetCore.Hosting;
+
+namespace Issue701_Repro
+{
+
+ /***************** EXAMPLE ****************/
+ public class Program
+ {
+ public static void Main(string[] args)
+ {
+ CreateWebHostBuilder(args).Build().Run();
+ }
+
+ public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
+ WebHost.CreateDefaultBuilder(args)
+ .UseStartup();
+ }
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/************** TEMPLATE **************/
+/*namespace Issue701_Repro2
+{
+ public class Program
+ {
+ public static void Main(string[] args)
+ {
+ var builder = WebApplication.CreateBuilder(args);
+
+ // Add services to the container.
+
+ builder.Services.AddControllers();
+ // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
+ builder.Services.AddEndpointsApiExplorer();
+ builder.Services.AddSwaggerGen();
+
+ var app = builder.Build();
+
+ // Configure the HTTP request pipeline.
+ if (app.Environment.IsDevelopment())
+ {
+ app.UseSwagger();
+ app.UseSwaggerUI();
+ }
+
+ app.UseAuthorization();
+
+
+ app.MapControllers();
+
+ app.Run();
+ }
+ }
+}*/
\ No newline at end of file
diff --git a/sample/Issue701_Repro/Properties/launchSettings.json b/sample/Issue701_Repro/Properties/launchSettings.json
new file mode 100644
index 000000000..271df4752
--- /dev/null
+++ b/sample/Issue701_Repro/Properties/launchSettings.json
@@ -0,0 +1,31 @@
+{
+ "$schema": "http://json.schemastore.org/launchsettings.json",
+ "iisSettings": {
+ "windowsAuthentication": false,
+ "anonymousAuthentication": true,
+ "iisExpress": {
+ "applicationUrl": "http://localhost:51579",
+ "sslPort": 44377
+ }
+ },
+ "profiles": {
+ "IIS Express": {
+ "commandName": "IISExpress",
+ "launchBrowser": true,
+ "launchUrl": "sample",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ },
+ "Issue701_Repro": {
+ "commandName": "Project",
+ "dotnetRunMessages": "true",
+ "launchBrowser": true,
+ "launchUrl": "sample",
+ "applicationUrl": "https://localhost:5001;http://localhost:5000",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ }
+ }
+}
diff --git a/sample/Issue701_Repro/Startup.cs b/sample/Issue701_Repro/Startup.cs
new file mode 100644
index 000000000..d4f790c2e
--- /dev/null
+++ b/sample/Issue701_Repro/Startup.cs
@@ -0,0 +1,87 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.OData;
+using Microsoft.AspNetCore.OData.Extensions;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Options;
+using Microsoft.OData.Edm;
+using Microsoft.OData.ModelBuilder;
+using Issue701_Repro.Models;
+
+namespace Issue701_Repro
+{
+ public class Startup
+ {
+ public Startup(IConfiguration configuration)
+ {
+ Configuration = configuration;
+ }
+
+ public IConfiguration Configuration { get; }
+
+ // This method gets called by the runtime. Use this method to add services to the container.
+ public void ConfigureServices(IServiceCollection services)
+ {
+ services.AddControllers().AddOData(opt => opt.Count().Filter().Expand().Select().OrderBy().SetMaxTop(5)
+ .AddRouteComponents(GetSampleEntityDataModel()));
+ }
+
+ // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
+ public void Configure(IApplicationBuilder app, IHostingEnvironment env)
+ {
+ if (env.IsDevelopment())
+ {
+ app.UseDeveloperExceptionPage();
+ }
+ app.UseRouting();
+ //// Use odata route debug, /$odata
+ //app.UseODataRouteDebug();
+ //// Add OData /$query middleware
+ //app.UseODataQueryRequest();
+
+ //// Add the OData Batch middleware to support OData $Batch
+ //app.UseODataBatching();
+
+ // Test middleware
+ //app.Use(next => context =>
+ //{
+ // var endpoint = context.GetEndpoint();
+ // if (endpoint == null)
+ // {
+ // return next(context);
+ // }
+
+ // return next(context);
+ //});
+
+ app.UseEndpoints(endpoints =>
+ {
+ endpoints.MapControllers();
+ });
+ }
+
+ public IEdmModel GetSampleEntityDataModel()
+ {
+ var builder = new ODataConventionModelBuilder()
+ {
+ Namespace = "Samples",
+ ContainerName = "SamplesContainer"
+ };
+
+ //Catalog endpoint
+ builder.Singleton("sample");
+ builder.EntityType();
+ builder.EnableLowerCamelCase();
+ IEdmModel model = builder.GetEdmModel();
+
+ return model; // I ADDED THIS
+ }
+ }
+}
\ No newline at end of file
diff --git a/sample/Issue701_Repro/appsettings.Development.json b/sample/Issue701_Repro/appsettings.Development.json
new file mode 100644
index 000000000..8983e0fc1
--- /dev/null
+++ b/sample/Issue701_Repro/appsettings.Development.json
@@ -0,0 +1,9 @@
+{
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft": "Warning",
+ "Microsoft.Hosting.Lifetime": "Information"
+ }
+ }
+}
diff --git a/sample/Issue701_Repro/appsettings.json b/sample/Issue701_Repro/appsettings.json
new file mode 100644
index 000000000..d9d9a9bff
--- /dev/null
+++ b/sample/Issue701_Repro/appsettings.json
@@ -0,0 +1,10 @@
+{
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft": "Warning",
+ "Microsoft.Hosting.Lifetime": "Information"
+ }
+ },
+ "AllowedHosts": "*"
+}
From 71acab00fce5f92d4c452473954785443ae1ea8d Mon Sep 17 00:00:00 2001
From: Lisi Case <49213376+lisicase@users.noreply.github.com>
Date: Fri, 23 Sep 2022 14:31:21 -0700
Subject: [PATCH 02/17] Process segments contained by a singleton with a path
---
.../Formatter/LinkGenerationHelpers.cs | 22 ++-
.../Microsoft.AspNetCore.OData.xml | 153 ++++++++----------
2 files changed, 84 insertions(+), 91 deletions(-)
diff --git a/src/Microsoft.AspNetCore.OData/Formatter/LinkGenerationHelpers.cs b/src/Microsoft.AspNetCore.OData/Formatter/LinkGenerationHelpers.cs
index 8f688e6e0..2a5948810 100644
--- a/src/Microsoft.AspNetCore.OData/Formatter/LinkGenerationHelpers.cs
+++ b/src/Microsoft.AspNetCore.OData/Formatter/LinkGenerationHelpers.cs
@@ -349,13 +349,13 @@ internal static IList GenerateBaseODataPathSegments(this Resou
return odataPath;
}
- private static void GenerateBaseODataPathSegmentsForNonSingletons(
+ private static void GenerateBaseODataPathSegmentsForContainedNavProperties(
ODataPath path,
IEdmNavigationSource navigationSource,
IList odataPath)
{
- // If the navigation is not a singleton we need to walk all of the path segments to generate a
- // contextually accurate URI.
+ // If the navigation is a contained property, we need to walk all of the path segments
+ // to generate a contextually accurate URI.
bool segmentFound = false;
bool containedFound = false;
if (path != null)
@@ -379,6 +379,14 @@ private static void GenerateBaseODataPathSegmentsForNonSingletons(
{
currentNavigationSource = navigationPathSegment.NavigationSource;
}
+
+ var singletonPathSegment = pathSegment as SingletonSegment;
+ if (singletonPathSegment != null)
+ {
+ currentNavigationSource = singletonPathSegment.Singleton;
+ }
+
+
if (containedFound)
{
odataPath.Add(pathSegment);
@@ -440,9 +448,9 @@ private static void GenerateBaseODataPathSegmentsForEntity(
this ResourceContext resourceContext,
IList odataPath)
{
- // If the navigation is not a singleton we need to walk all of the path segments to generate a
- // contextually accurate URI.
- GenerateBaseODataPathSegmentsForNonSingletons(
+ // If the navigation is a contained property, we need to walk all of the path segments
+ // to generate a contextually accurate URI.
+ GenerateBaseODataPathSegmentsForContainedNavProperties(
resourceContext.SerializerContext.Path, resourceContext.NavigationSource, odataPath);
odataPath.Add(new KeySegment(ConventionsHelpers.GetEntityKey(resourceContext), resourceContext.StructuredType as IEdmEntityType,
@@ -453,7 +461,7 @@ private static void GenerateBaseODataPathSegmentsForFeed(
this ResourceSetContext feedContext,
IList odataPath)
{
- GenerateBaseODataPathSegmentsForNonSingletons(feedContext.Request.ODataFeature().Path,
+ GenerateBaseODataPathSegmentsForContainedNavProperties(feedContext.Request.ODataFeature().Path,
feedContext.EntitySetBase,
odataPath);
}
diff --git a/src/Microsoft.AspNetCore.OData/Microsoft.AspNetCore.OData.xml b/src/Microsoft.AspNetCore.OData/Microsoft.AspNetCore.OData.xml
index 7a13ad99f..10eda6270 100644
--- a/src/Microsoft.AspNetCore.OData/Microsoft.AspNetCore.OData.xml
+++ b/src/Microsoft.AspNetCore.OData/Microsoft.AspNetCore.OData.xml
@@ -1094,20 +1094,6 @@
The type to test.
True if the type is a DateTime; false otherwise.
-
-
- Determine if a type is a .
-
- The type to test.
- True if the type is a DateOnly; false otherwise.
-
-
-
- Determine if a type is a .
-
- The type to test.
- True if the type is a TimeOnly; false otherwise.
-
Determine if a type is a TimeSpan.
@@ -1488,7 +1474,22 @@
Helper method to check whether the given object is Delta resource set.
The given object.
- Object (Equivalent of ODataDeletedResource in ODL).
+ True/False.
+
+
+
+ The Kind of the object within the DeltaPayload used to distinguish between
+ Resource/DeletedResource/DeltaDeletedLink/AddedLink.
+
+
+
+
+ Corresponds to EdmEntityObject (Equivalent of ODataResource in ODL).
+
+
+
+
+ Corresponds to EdmDeltaDeletedResourceObject (Equivalent of ODataDeletedResource in ODL).
@@ -1569,20 +1570,6 @@
-
-
- A class the tracnew instance of .
-
-
-
-
- Initializes a new instance of .
-
- The derived structural type for which the changes would be tracked.
-
-
-
-
A class the tracks changes (i.e. the Delta) for a particular .
@@ -1637,7 +1624,21 @@
The list of property names that can be updated.
- When the list is modified, ttempts to get the value of the nested Property called from the underlying resource.
+ When the list is modified, any modified properties that were removed from the list are no longer
+ considered to be changed.
+
+
+
+
+
+
+
+
+
+
+
+
+ Attempts to get the value of the nested Property called from the underlying resource.
Only properties that exist on Entity can be retrieved.
Only modified nested properties can be retrieved.
@@ -1699,19 +1700,6 @@
The entity to be updated.
-
-
- Attempts to get the property by the specmarks>
-
- The entity to be updated.
-
-
-
- Overwrites the entity with the values stored in this Delta.
- The semantics of this operation are equivalent to a HTTP PUT operation, hence the name.
-
- The entity to be updated.
-
Attempts to get the property by the specified name.
@@ -2669,7 +2657,16 @@
This is how formatters create links to invoke bound actions or functions.
-
+
+
+ Create a new based on an entity link factory.
+
+ The link factory this should use when building links.
+
+ A value indicating whether the link factory generates links that follow OData conventions.
+
+
+
Create a new based on a feed link factory.
@@ -7981,18 +7978,6 @@
Enables a controller action to support OData query parameters.
-
-
- Gets or sets a value indicating whether query composition should
- alter the original query when necessary to ensure a stable sort order.
-
- A true value indicates the original query should
- be modified when necessary to guarantee a stable sort order.
- A false value indicates the sort order can be considered
- stable without modifying the query. Query providers that ensure
- a stable sort order shoa controller action to support OData query parameters.
-
-
Gets or sets a value indicating whether query composition should
@@ -10357,7 +10342,19 @@
- ">
+ Translate a SingleNavigationNode.
+
+ The node to be translated.
+ The translated node.
+
+
+
+ Translate a SingleValueFunctionCallNode.
+
+ The node to be translated.
+ The translated node.
+
+
Translate a SingleValueOpenPropertyAccessNode.
@@ -10423,18 +10420,6 @@
The filter context.
-
-
- Summary:
- Called in decreasing Microsoft.AspNetCore.Mvc.Filters.IFilterProvider.Order,
- after all Microsoft.AspNetCore.Mvc.Filters.IFilterProviders have executed once.
-
- The Mica.Query.QueryFilterProvider.OnProvidersExecuting(Microsoft.AspNetCore.Mvc.Filters.FilterProviderContext)">
-
- Provides filters to apply to the specified action.
-
- The filter context.
-
Summary:
@@ -10554,7 +10539,21 @@
Gets the raw $count value.
- validation failed.
+
+
+
+ Gets the value of the $count in a parsed form.
+
+
+
+
+ Gets or sets the $count query validator.
+
+
+
+
+ Validate the count query based on the given .
+ It throws an ODataException if validation failed.
The instance
which contains all the validation settings.
@@ -11590,20 +11589,6 @@
Gets a list of properties one can orderby the result with. Note, by default this list is empty,
it means it can be ordered by any property.
- For example, having an empty col: any, all
-
-
-
-
-
- Gets or sets a list of allowed logical operators such as 'eq', 'ne', 'gt', 'ge', 'lt', 'le', 'and', 'or', 'not'.
-
-
-
-
- Gets a list of properties one can orderby the result with. Note, by default this list is empty,
- it means it can be ordered by any property.
-
For example, having an empty collection means client can order the queryable result by any properties.
Adding "Name" to this list means that it only allows queryable result to be ordered by Name property.
From 5333a9d04ea1d0bbcd2eac9b86ebff418f495ff5 Mon Sep 17 00:00:00 2001
From: Lisi Case <49213376+lisicase@users.noreply.github.com>
Date: Fri, 23 Sep 2022 15:25:25 -0700
Subject: [PATCH 03/17] Cleaned up code
---
.../Controllers/SampleController.cs | 16 +--
sample/Issue701_Repro/Models/DataSource.cs | 104 +-----------------
sample/Issue701_Repro/Program.cs | 69 +-----------
sample/Issue701_Repro/Startup.cs | 34 +-----
4 files changed, 8 insertions(+), 215 deletions(-)
diff --git a/sample/Issue701_Repro/Controllers/SampleController.cs b/sample/Issue701_Repro/Controllers/SampleController.cs
index 841ce086c..09f98900f 100644
--- a/sample/Issue701_Repro/Controllers/SampleController.cs
+++ b/sample/Issue701_Repro/Controllers/SampleController.cs
@@ -1,17 +1,6 @@
using Microsoft.AspNetCore.Mvc;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using Microsoft.AspNetCore.OData.Formatter;
-using Microsoft.AspNetCore.OData.Formatter.Value;
using Microsoft.AspNetCore.OData.Routing.Controllers;
-
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading.Tasks;
using Issue701_Repro.Models;
-using Microsoft.OData;
using Microsoft.AspNetCore.OData.Query;
namespace Issue701_nextLink.Tests.Controllers
@@ -19,7 +8,7 @@ namespace Issue701_nextLink.Tests.Controllers
public class SampleController : ODataController
{
//[ApiVersion("1.0")]
- // [HttpGet("v{v:apiVersion}/sample")]
+ //[HttpGet("v{v:apiVersion}/sample")]
[HttpGet("sample")]
[EnableQuery]
public IActionResult GetSampleAsync(/*[FromQuery] QueryStringParameters queryString*/)
@@ -33,8 +22,7 @@ public IActionResult GetSampleAsync(/*[FromQuery] QueryStringParameters queryStr
[EnableQuery(PageSize = 2)]
public IActionResult GetCatalogExamsAsync(/*[FromQuery] QueryStringParameters queryString*/)
{
- var sItems = DataSource.GetSampleItems();
- return this.Ok(sItems);
+ return this.Ok(DataSource.GetSampleItems());
}
}
}
\ No newline at end of file
diff --git a/sample/Issue701_Repro/Models/DataSource.cs b/sample/Issue701_Repro/Models/DataSource.cs
index 58de412da..4c88acadc 100644
--- a/sample/Issue701_Repro/Models/DataSource.cs
+++ b/sample/Issue701_Repro/Models/DataSource.cs
@@ -1,14 +1,4 @@
-/*using Microsoft.AspNetCore.Mvc.ViewFeatures;
-using System;
-using System.Collections.Generic;
-using System.Data.SqlTypes;
-using System.Linq;
-using System.Threading.Tasks;*/
-
-using System;
-using System.Collections.Generic;
-using System.Collections.ObjectModel;
-using System.Linq;
+using System.Collections.Generic;
namespace Issue701_Repro.Models
{
@@ -75,94 +65,4 @@ private static SampleItemGuide createSIG(int num)
return sig;
}
}
-}
-
-/*********************** EXPECTED RESULT ***********************/
-
-// REQUEST: /sample/sampleitems?$expand=SampleItem_guide
-
-/*{
- "@odata.context": "https://localhost:44335/v1.0/$metadata#sample/sampleitems(sampleitem_guide())",
- "value": [
- {
- "uid": "sample.uid",
-
- "sampleitem_guide": [
- {
- "uid": "sampleitem.uid1",
- "type": "sampletype1"
- },
- {
- "uid": "sampleitem.uid2",
- "type": "sampletype2"
- }
- ]
- }
- ],
- "@odata.nextLink": "https://localhost:44335/v1.0/sample/sampleitems?$skip=1"
-}*/
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-/************************** PREVIOUS ITERATION ********************************/
-
-/*namespace Issue701_Repro2.Models
-{
- public static class DataSource
- {
-
- private static Sample _sample { get; set; } = new Sample();
-
- private static Sample GetSample()
- {
- // Create sample item guides
- SampleItemGuide si1 = new SampleItemGuide
- {
- Uid = "sampleuid1",
- Type = "sampletype1"
- };
- SampleItemGuide si2 = new SampleItemGuide
- {
- Uid = "sampleuid2",
- Type = "sampletype2"
- };
- SampleItemGuide si3 = new SampleItemGuide
- {
- Uid = "sampleuid3",
- Type = "sampletype3"
- };
-
- // Create sample items
- SampleItems sampleItems1 = new SampleItems();
- sampleItems1.SampleItem_guide.Append(si1);
- sampleItems1.SampleItem_guide.Append(si2);
- SampleItems sampleItems2 = new SampleItems();
- sampleItems2.SampleItem_guide.Append(si3);
-
- // Create sample
- _sample = new Sample();
- _sample.SItems.Append(sampleItems1);
- _sample.SItems.Append(sampleItems2);
-
- return _sample;
- }
- }
-}*/
\ No newline at end of file
+}
\ No newline at end of file
diff --git a/sample/Issue701_Repro/Program.cs b/sample/Issue701_Repro/Program.cs
index 9f86800b7..413e93d97 100644
--- a/sample/Issue701_Repro/Program.cs
+++ b/sample/Issue701_Repro/Program.cs
@@ -4,8 +4,6 @@
namespace Issue701_Repro
{
-
- /***************** EXAMPLE ****************/
public class Program
{
public static void Main(string[] args)
@@ -17,69 +15,4 @@ public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup();
}
-}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-/************** TEMPLATE **************/
-/*namespace Issue701_Repro2
-{
- public class Program
- {
- public static void Main(string[] args)
- {
- var builder = WebApplication.CreateBuilder(args);
-
- // Add services to the container.
-
- builder.Services.AddControllers();
- // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
- builder.Services.AddEndpointsApiExplorer();
- builder.Services.AddSwaggerGen();
-
- var app = builder.Build();
-
- // Configure the HTTP request pipeline.
- if (app.Environment.IsDevelopment())
- {
- app.UseSwagger();
- app.UseSwaggerUI();
- }
-
- app.UseAuthorization();
-
-
- app.MapControllers();
-
- app.Run();
- }
- }
-}*/
\ No newline at end of file
+}
\ No newline at end of file
diff --git a/sample/Issue701_Repro/Startup.cs b/sample/Issue701_Repro/Startup.cs
index d4f790c2e..f8de6ba0c 100644
--- a/sample/Issue701_Repro/Startup.cs
+++ b/sample/Issue701_Repro/Startup.cs
@@ -1,16 +1,8 @@
-using System;
-using System.Collections.Generic;
-using System.Diagnostics;
-using System.Linq;
-using System.Threading.Tasks;
using Microsoft.AspNetCore.OData;
-using Microsoft.AspNetCore.OData.Extensions;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
-using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.Options;
using Microsoft.OData.Edm;
using Microsoft.OData.ModelBuilder;
using Issue701_Repro.Models;
@@ -26,14 +18,14 @@ public Startup(IConfiguration configuration)
public IConfiguration Configuration { get; }
- // This method gets called by the runtime. Use this method to add services to the container.
+ // This method gets called by the runtime to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers().AddOData(opt => opt.Count().Filter().Expand().Select().OrderBy().SetMaxTop(5)
.AddRouteComponents(GetSampleEntityDataModel()));
}
- // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
+ // This method gets called by the runtime to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
@@ -41,26 +33,6 @@ public void Configure(IApplicationBuilder app, IHostingEnvironment env)
app.UseDeveloperExceptionPage();
}
app.UseRouting();
- //// Use odata route debug, /$odata
- //app.UseODataRouteDebug();
- //// Add OData /$query middleware
- //app.UseODataQueryRequest();
-
- //// Add the OData Batch middleware to support OData $Batch
- //app.UseODataBatching();
-
- // Test middleware
- //app.Use(next => context =>
- //{
- // var endpoint = context.GetEndpoint();
- // if (endpoint == null)
- // {
- // return next(context);
- // }
-
- // return next(context);
- //});
-
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
@@ -81,7 +53,7 @@ public IEdmModel GetSampleEntityDataModel()
builder.EnableLowerCamelCase();
IEdmModel model = builder.GetEdmModel();
- return model; // I ADDED THIS
+ return model;
}
}
}
\ No newline at end of file
From 99f565740e011234464ccb5c3011504ad284660f Mon Sep 17 00:00:00 2001
From: Lisi Case <49213376+lisicase@users.noreply.github.com>
Date: Wed, 28 Sep 2022 13:05:55 -0700
Subject: [PATCH 04/17] Created singleton test with new sample data model
---
sample/Issue701_Repro/Models/DataSource.cs | 16 ++--
.../Microsoft.AspNetCore.OData.xml | 14 ++++
.../Singleton/SampleController.cs | 83 +++++++++++++++++++
.../Singleton/SingletonDataModel.cs | 30 +++++++
.../Singleton/SingletonEdmModel.cs | 5 ++
.../Singleton/SingletonTest.cs | 21 ++++-
6 files changed, 161 insertions(+), 8 deletions(-)
create mode 100644 test/Microsoft.AspNetCore.OData.E2E.Tests/Singleton/SampleController.cs
diff --git a/sample/Issue701_Repro/Models/DataSource.cs b/sample/Issue701_Repro/Models/DataSource.cs
index 4c88acadc..082345f2a 100644
--- a/sample/Issue701_Repro/Models/DataSource.cs
+++ b/sample/Issue701_Repro/Models/DataSource.cs
@@ -8,24 +8,24 @@ public static class DataSource
public static Sample GetSample()
{
- ensureData();
+ EnsureData();
return _sample;
}
public static IEnumerable GetSampleItems()
{
- ensureData();
+ EnsureData();
return _sample.SItems;
}
- private static void ensureData()
+ private static void EnsureData()
{
if (_sample == null)
{
// Create sample item guides
- SampleItemGuide si1 = createSIG(1);
- SampleItemGuide si2 = createSIG(2);
- SampleItemGuide si3 = createSIG(3);
+ SampleItemGuide si1 = CreateSIG(1);
+ SampleItemGuide si2 = CreateSIG(2);
+ SampleItemGuide si3 = CreateSIG(3);
// Create sample items
SampleItems sampleItems1 = new SampleItems
@@ -51,11 +51,13 @@ private static void ensureData()
var SItems = new List();
SItems.Add(sampleItems1);
SItems.Add(sampleItems2);
+ SItems.Add(sampleItems2);
+ SItems.Add(sampleItems2);
_sample.SItems = SItems;
}
}
- private static SampleItemGuide createSIG(int num)
+ private static SampleItemGuide CreateSIG(int num)
{
SampleItemGuide sig = new SampleItemGuide
{
diff --git a/src/Microsoft.AspNetCore.OData/Microsoft.AspNetCore.OData.xml b/src/Microsoft.AspNetCore.OData/Microsoft.AspNetCore.OData.xml
index 10eda6270..4d0e931a0 100644
--- a/src/Microsoft.AspNetCore.OData/Microsoft.AspNetCore.OData.xml
+++ b/src/Microsoft.AspNetCore.OData/Microsoft.AspNetCore.OData.xml
@@ -1094,6 +1094,20 @@
The type to test.
True if the type is a DateTime; false otherwise.
+
+
+ Determine if a type is a .
+
+ The type to test.
+ True if the type is a DateOnly; false otherwise.
+
+
+
+ Determine if a type is a .
+
+ The type to test.
+ True if the type is a TimeOnly; false otherwise.
+
Determine if a type is a TimeSpan.
diff --git a/test/Microsoft.AspNetCore.OData.E2E.Tests/Singleton/SampleController.cs b/test/Microsoft.AspNetCore.OData.E2E.Tests/Singleton/SampleController.cs
new file mode 100644
index 000000000..f94225c27
--- /dev/null
+++ b/test/Microsoft.AspNetCore.OData.E2E.Tests/Singleton/SampleController.cs
@@ -0,0 +1,83 @@
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.OData.Routing.Controllers;
+using Microsoft.AspNetCore.OData.Query;
+using System.Collections.Generic;
+
+namespace Microsoft.AspNetCore.OData.E2E.Tests.Singleton
+{
+ ///
+ /// Present a sample situation for Issue #701
+ ///
+ [Route("odata/Sample")]
+ public class SampleController : ODataController
+ {
+ public static Sample _sample;
+
+ static SampleController()
+ {
+ InitData();
+ }
+
+ private static void InitData()
+ {
+ // Create sample item guides
+ SampleItemGuide si1 = CreateSIG(1);
+ SampleItemGuide si2 = CreateSIG(2);
+ SampleItemGuide si3 = CreateSIG(3);
+
+ // Create sample items
+ SampleItems sampleItems1 = new SampleItems
+ {
+ Uid = "sampleitems1"
+ };
+ var items1 = new List();
+ items1.Add(si1);
+ items1.Add(si2);
+ items1.Add(si3);
+ sampleItems1.SampleItem_guide = items1;
+
+ SampleItems sampleItems2 = new SampleItems
+ {
+ Uid = "sampleitems2"
+ };
+ var items2 = new List();
+ items2.Add(si3);
+ sampleItems2.SampleItem_guide = items2;
+
+ // Create sample
+ _sample = new Sample();
+ var SItems = new List();
+ SItems.Add(sampleItems1);
+ SItems.Add(sampleItems2);
+ SItems.Add(sampleItems2);
+ SItems.Add(sampleItems2);
+ _sample.SItems = SItems;
+ }
+
+ private static SampleItemGuide CreateSIG(int num)
+ {
+ SampleItemGuide sig = new SampleItemGuide
+ {
+ Uid = "sampleuid" + num,
+ Type = "sampletype" + num
+ };
+ return sig;
+ }
+
+ #region Query
+ [HttpGet("")]
+ [EnableQuery]
+ public IActionResult GetSampleAsync()
+ {
+ return Ok(_sample);
+ }
+
+ [HttpGet("SItems")]
+ [EnableQuery(PageSize = 2)]
+ public IActionResult GetCatalogExamsAsync()
+ {
+ return Ok(_sample.SItems);
+ }
+ #endregion
+ }
+}
diff --git a/test/Microsoft.AspNetCore.OData.E2E.Tests/Singleton/SingletonDataModel.cs b/test/Microsoft.AspNetCore.OData.E2E.Tests/Singleton/SingletonDataModel.cs
index 92f1bacb4..6325cc2a7 100644
--- a/test/Microsoft.AspNetCore.OData.E2E.Tests/Singleton/SingletonDataModel.cs
+++ b/test/Microsoft.AspNetCore.OData.E2E.Tests/Singleton/SingletonDataModel.cs
@@ -6,6 +6,9 @@
//------------------------------------------------------------------------------
using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations;
+using System.ComponentModel.DataAnnotations.Schema;
+using System.Linq;
using Microsoft.OData.ModelBuilder;
namespace Microsoft.AspNetCore.OData.E2E.Tests.Singleton
@@ -68,4 +71,31 @@ public class SubCompany : Company
public string Description { get; set; }
public Office Office { get; set; }
}
+
+ // Sample case for Issue #701
+
+ public class Sample
+ {
+ [Contained]
+ [ForeignKey("Uid")]
+ [AutoExpand]
+ public IEnumerable SItems { get; set; } = Enumerable.Empty();
+ }
+
+ public class SampleItems
+ {
+ [Key]
+ public string Uid { get; set; }
+
+ [AutoExpand]
+ [Contained]
+ public IEnumerable SampleItem_guide { get; set; } = System.Array.Empty();
+ }
+
+ public class SampleItemGuide
+ {
+ [Key]
+ public string Uid { get; set; }
+ public string Type { get; set; }
+ }
}
diff --git a/test/Microsoft.AspNetCore.OData.E2E.Tests/Singleton/SingletonEdmModel.cs b/test/Microsoft.AspNetCore.OData.E2E.Tests/Singleton/SingletonEdmModel.cs
index 83859e030..24642a635 100644
--- a/test/Microsoft.AspNetCore.OData.E2E.Tests/Singleton/SingletonEdmModel.cs
+++ b/test/Microsoft.AspNetCore.OData.E2E.Tests/Singleton/SingletonEdmModel.cs
@@ -109,6 +109,11 @@ public static IEdmModel GetEdmModel()
//monstersIncConfiguration.EntityType.Action("ResetDataSource");
monstersIncConfiguration.EntityType.Function("GetPartnersCount").Returns();
+ // Singleton "Sample" - makes the NotCountable test fail
+ builder.Singleton("Sample");
+ builder.EntityType();
+ //builder.EnableLowerCamelCase(); // makes the NotCountable test fail
+
builder.Namespace = typeof(Company).Namespace;
return builder.GetEdmModel();
}
diff --git a/test/Microsoft.AspNetCore.OData.E2E.Tests/Singleton/SingletonTest.cs b/test/Microsoft.AspNetCore.OData.E2E.Tests/Singleton/SingletonTest.cs
index 7b246b6f8..77ea7852f 100644
--- a/test/Microsoft.AspNetCore.OData.E2E.Tests/Singleton/SingletonTest.cs
+++ b/test/Microsoft.AspNetCore.OData.E2E.Tests/Singleton/SingletonTest.cs
@@ -31,7 +31,8 @@ protected static void UpdateConfigureServices(IServiceCollection services)
typeof(UmbrellaController),
typeof(MonstersIncController),
typeof(PartnersController),
- typeof(ODataEndpointController)
+ typeof(ODataEndpointController),
+ typeof(SampleController)
};
services.ConfigureControllers(controllers);
@@ -73,6 +74,24 @@ public async Task TestRoutes()
string contentOfString = await response.Content.ReadAsStringAsync();
}
+ [Fact]
+ public async Task SingletonContainer()
+ {
+ // Arrange
+ string requestUri = "odata/Sample/SItems"; // TODO: utilize/modify existing EDM classes instead
+ HttpClient client = CreateClient();
+
+ // Act
+ HttpResponseMessage response = await client.GetAsync(requestUri);
+
+ // Assert
+ string result = await response.Content.ReadAsStringAsync();
+ response.EnsureSuccessStatusCode();
+
+ string expectedResult = "{\"@odata.context\":\"http://localhost/odata/$metadata#Sample/SItems(SampleItem_guide())\",\"value\":[{\"Uid\":\"sampleitems1\",\"SampleItem_guide\":[{\"Uid\":\"sampleuid1\",\"Type\":\"sampletype1\"},{\"Uid\":\"sampleuid2\",\"Type\":\"sampletype2\"}],\"SampleItem_guide@odata.nextLink\":\"http://localhost/odata/Sample/SItems/sampleitems1/SampleItem_guide?$skip=2\"},{\"Uid\":\"sampleitems2\",\"SampleItem_guide\":[{\"Uid\":\"sampleuid3\",\"Type\":\"sampletype3\"}]}],\"@odata.nextLink\":\"http://localhost/odata/Sample/SItems?$skip=2\"}";
+ Assert.Equal(expectedResult, result);
+ }
+
[Fact]
public async Task NotCountable()
{
From 3e8aa4f0c3a1599486904b940a4171554c14bb64 Mon Sep 17 00:00:00 2001
From: Lisi Case <49213376+lisicase@users.noreply.github.com>
Date: Thu, 29 Sep 2022 21:32:29 -0700
Subject: [PATCH 05/17] Added unit test
---
.../Formatter/LinkGenerationHelpersTest.cs | 51 ++++++++++++++++++-
1 file changed, 49 insertions(+), 2 deletions(-)
diff --git a/test/Microsoft.AspNetCore.OData.Tests/Formatter/LinkGenerationHelpersTest.cs b/test/Microsoft.AspNetCore.OData.Tests/Formatter/LinkGenerationHelpersTest.cs
index 05f3d690c..216594220 100644
--- a/test/Microsoft.AspNetCore.OData.Tests/Formatter/LinkGenerationHelpersTest.cs
+++ b/test/Microsoft.AspNetCore.OData.Tests/Formatter/LinkGenerationHelpersTest.cs
@@ -110,6 +110,41 @@ public void GenerateNavigationLink_WorksToGenerateExpectedNavigationLink_ForSing
Assert.Equal(expectedNavigationLink, uri.AbsoluteUri);
}
+ [Fact]
+ public void GenerateNavigationLink_WorksToGenerateExpectedBasePathSegments_ForSingletonContainer()
+ {
+ // Arrange
+ IEdmSingleton myVipOrder = _myOrderModel.FindDeclaredSingleton("VipOrder");
+ IEdmEntityType vipOrderType = (IEdmEntityType)myVipOrder.Type;
+ IEdmNavigationProperty orderLinesProperty = vipOrderType.NavigationProperties().Single(x => x.ContainsTarget && x.Name == "OrderLines");
+ IEdmContainedEntitySet orderLines = (IEdmContainedEntitySet)myVipOrder.FindNavigationTarget(orderLinesProperty);
+ IEdmEntityType orderLine = _myOrderModel.SchemaElements.OfType().First(e => e.Name == "OrderLine");
+ IEdmNavigationProperty orderLineDetailsNav = orderLine.NavigationProperties().First();
+
+ // Link relationships
+ HttpRequest request = RequestFactory.Create(_myOrderModel);
+
+ ODataPath path = new ODataPath(
+ new SingletonSegment(myVipOrder),
+ new NavigationPropertySegment(orderLinesProperty, orderLines));
+
+ var orderLineSerializerContext = ODataSerializerContextFactory.Create(_myOrderModel, orderLines, path, request);
+ orderLineSerializerContext.EdmProperty = orderLineDetailsNav;
+ var orderLineResource = new ResourceContext(orderLineSerializerContext, orderLine.AsReference(), new { ID = 21 });
+ orderLineSerializerContext.ExpandedResource = orderLineResource;
+
+ // Act
+ IList newPaths = orderLineResource.GenerateBaseODataPathSegments();
+ Uri selfLink = orderLineResource.GenerateSelfLink(false);
+
+ // Assert
+ Assert.Equal(3, newPaths.Count());
+ Assert.Equal("Microsoft.OData.UriParser.SingletonSegment", newPaths[0].GetType().FullName); // VipOrder
+ Assert.Equal("Microsoft.OData.UriParser.NavigationPropertySegment", newPaths[1].GetType().FullName); // OrderLines
+ Assert.Equal("Microsoft.OData.UriParser.KeySegment", newPaths[2].GetType().FullName); // 21
+ Assert.Equal("http://localhost/VipOrder/OrderLines(21)", selfLink.AbsoluteUri);
+ }
+
[Theory]
[InlineData(false, "http://localhost/MyOrders(42)/OrderLines(21)/OrderLines")]
[InlineData(true, "http://localhost/MyOrders(42)/OrderLines(21)/NS.OrderLine/OrderLines")]
@@ -125,8 +160,8 @@ public void GenerateNavigationLink_WorksToGenerateExpectedNavigationLink_ForCont
IEdmEntityType myOrder = (IEdmEntityType)_myOrderModel.FindDeclaredType("NS.MyOrder");
IEdmEntityType orderLine = (IEdmEntityType)_myOrderModel.FindDeclaredType("NS.OrderLine");
- IEdmNavigationProperty orderLinesProperty = myOrder.NavigationProperties().Single(x => x.ContainsTarget);
-
+ IEdmNavigationProperty orderLinesProperty = myOrder.NavigationProperties().Single(x => x.ContainsTarget && x.Name == "OrderLines");
+
IEdmEntitySet entitySet = _myOrderModel.FindDeclaredEntitySet("MyOrders");
IDictionary parameters = new Dictionary
{
@@ -689,6 +724,7 @@ private static IEdmModel GetEdmModel2()
ODataConventionModelBuilder builder = new ODataConventionModelBuilder();
builder.Namespace = "NS";
builder.EntitySet("MyOrders");
+ builder.Singleton("VipOrder");
return builder.GetEdmModel();
}
@@ -699,10 +735,21 @@ private class MyOrder
[Contained]
public IList OrderLines { get; set; }
+ /*[Contained]
+ public OrderLine SingleContainedOrderLine { get; set; }*/
+
public IList NonContainedOrderLines { get; set; }
}
private class OrderLine
+ {
+ public int ID { get; set; }
+
+ [Contained]
+ public IList OrderLineDetails { get; set; }
+ }
+
+ private class OrderLineDetail
{
public int ID { get; set; }
}
From bb120c6a468b8096c35a15654ed77d2186b658c0 Mon Sep 17 00:00:00 2001
From: Lisi Case <49213376+lisicase@users.noreply.github.com>
Date: Thu, 29 Sep 2022 21:36:30 -0700
Subject: [PATCH 06/17] Removed unnecessary code
---
.../Formatter/LinkGenerationHelpersTest.cs | 3 ---
1 file changed, 3 deletions(-)
diff --git a/test/Microsoft.AspNetCore.OData.Tests/Formatter/LinkGenerationHelpersTest.cs b/test/Microsoft.AspNetCore.OData.Tests/Formatter/LinkGenerationHelpersTest.cs
index 216594220..3b22d1c15 100644
--- a/test/Microsoft.AspNetCore.OData.Tests/Formatter/LinkGenerationHelpersTest.cs
+++ b/test/Microsoft.AspNetCore.OData.Tests/Formatter/LinkGenerationHelpersTest.cs
@@ -735,9 +735,6 @@ private class MyOrder
[Contained]
public IList OrderLines { get; set; }
- /*[Contained]
- public OrderLine SingleContainedOrderLine { get; set; }*/
-
public IList NonContainedOrderLines { get; set; }
}
From 863f4e1765d1de5819fb101ebb500799190310f0 Mon Sep 17 00:00:00 2001
From: Lisi Case <49213376+lisicase@users.noreply.github.com>
Date: Thu, 29 Sep 2022 22:56:59 -0700
Subject: [PATCH 07/17] Updated target frameowkr
---
sample/Issue701_Repro/Issue701_Repro.csproj | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/sample/Issue701_Repro/Issue701_Repro.csproj b/sample/Issue701_Repro/Issue701_Repro.csproj
index 21e6c0786..c2648027b 100644
--- a/sample/Issue701_Repro/Issue701_Repro.csproj
+++ b/sample/Issue701_Repro/Issue701_Repro.csproj
@@ -1,7 +1,7 @@
- net5.0
+ net6.0
From 909f2d94594351c729530caa9fccf31e17ed2339 Mon Sep 17 00:00:00 2001
From: Lisi Case <49213376+lisicase@users.noreply.github.com>
Date: Thu, 29 Sep 2022 22:57:54 -0700
Subject: [PATCH 08/17] Created test using modified existing models
---
.../Singleton/MonstersIncController.cs | 37 +++++++++++++++++++
.../Singleton/SampleController.cs | 2 +-
.../Singleton/SingletonDataModel.cs | 37 +++++++++++++++++++
.../Singleton/SingletonEdmModel.cs | 4 +-
.../Singleton/SingletonTest.cs | 20 +++++++++-
.../Singleton/UmbrellaController.cs | 1 +
6 files changed, 97 insertions(+), 4 deletions(-)
diff --git a/test/Microsoft.AspNetCore.OData.E2E.Tests/Singleton/MonstersIncController.cs b/test/Microsoft.AspNetCore.OData.E2E.Tests/Singleton/MonstersIncController.cs
index 7c6b84eaa..4c226f991 100644
--- a/test/Microsoft.AspNetCore.OData.E2E.Tests/Singleton/MonstersIncController.cs
+++ b/test/Microsoft.AspNetCore.OData.E2E.Tests/Singleton/MonstersIncController.cs
@@ -42,6 +42,36 @@ private static void InitData()
Category = CompanyCategory.Electronics,
Partners = new List(),
Branches = new List() { new Office { City = "Shanghai", Address = "Minhang" }, new Office { City = "Xi'an", Address = "Dayanta" } },
+ Projects = new List()
+ {
+ new Project
+ {
+ Uid = 1,
+ Title = "In Closet Scare",
+ ProjectDetails = new List()
+ {
+ new ProjectDetail { Uid = 1, Comment = "The original scare" },
+ new ProjectDetail { Uid = 2, Comment = "Leaving the door open is the worst mistake any employee can make" },
+ new ProjectDetail { Uid = 3, Comment = "Leaving the door open could let it not only a draft, but a child" },
+ new ProjectDetail { Uid = 4, Comment = "Has led to the intrusion of a young girl, Boo" }
+ },
+ },
+ new Project
+ {
+ Uid = 2,
+ Title = "Under Bed Scare",
+ ProjectDetails = new List() {
+ new ProjectDetail { Uid = 5, Comment = "Tried and true" },
+ new ProjectDetail { Uid = 6, Comment = "Tip: grab a foot"}
+ },
+ },
+ new Project
+ {
+ Uid = 3,
+ Title = "Midnight Snack in Kitchen Scare",
+ ProjectDetails= new List(),
+ },
+ },
};
}
@@ -70,6 +100,13 @@ public IActionResult GetCompanyRevenue()
return Ok(MonstersInc.Revenue);
}
+ [HttpGet("Projects")]
+ [EnableQuery(PageSize = 2)]
+ public IActionResult GetProjects()
+ {
+ return Ok(MonstersInc.Projects);
+ }
+
[HttpGet("Branches/$count")]
public IActionResult GetBranchesCount(ODataQueryOptions options)
{
diff --git a/test/Microsoft.AspNetCore.OData.E2E.Tests/Singleton/SampleController.cs b/test/Microsoft.AspNetCore.OData.E2E.Tests/Singleton/SampleController.cs
index f94225c27..b32ecec16 100644
--- a/test/Microsoft.AspNetCore.OData.E2E.Tests/Singleton/SampleController.cs
+++ b/test/Microsoft.AspNetCore.OData.E2E.Tests/Singleton/SampleController.cs
@@ -32,7 +32,7 @@ private static void InitData()
};
var items1 = new List();
items1.Add(si1);
- items1.Add(si2);
+ items1.Add(si2);
items1.Add(si3);
sampleItems1.SampleItem_guide = items1;
diff --git a/test/Microsoft.AspNetCore.OData.E2E.Tests/Singleton/SingletonDataModel.cs b/test/Microsoft.AspNetCore.OData.E2E.Tests/Singleton/SingletonDataModel.cs
index 6325cc2a7..539d42104 100644
--- a/test/Microsoft.AspNetCore.OData.E2E.Tests/Singleton/SingletonDataModel.cs
+++ b/test/Microsoft.AspNetCore.OData.E2E.Tests/Singleton/SingletonDataModel.cs
@@ -51,6 +51,11 @@ public class Company
[NotCountable]
public IList Partners { get; set; }
public IList Branches { get; set; }
+
+ [Contained]
+ [ForeignKey("Uid")]
+ [AutoExpand]
+ public IList Projects { get; set; }
}
///
@@ -58,10 +63,35 @@ public class Company
///
public class Office
{
+ public string Uid { get; set; }
public string City { get; set; }
public string Address { get; set; }
}
+ ///
+ /// Present a contained navigation property
+ ///
+ public class Project
+ {
+ [Key]
+ public int Uid { get; set; }
+ public string Title { get; set; }
+
+ [AutoExpand]
+ [Contained]
+ public IList ProjectDetails { get; set; }
+ }
+
+ ///
+ /// Present a nested contained navigation property
+ ///
+ public class ProjectDetail
+ {
+ [Key]
+ public int Uid { get; set; }
+ public string Comment { get; set; }
+ }
+
///
/// EntityType derives from "Company"
///
@@ -73,6 +103,13 @@ public class SubCompany : Company
}
// Sample case for Issue #701
+ public class BestCompaniesEver
+ {
+ [Contained]
+ [ForeignKey("Uid")]
+ [AutoExpand]
+ public IEnumerable BestCompanies { get; set; } = Enumerable.Empty();
+ }
public class Sample
{
diff --git a/test/Microsoft.AspNetCore.OData.E2E.Tests/Singleton/SingletonEdmModel.cs b/test/Microsoft.AspNetCore.OData.E2E.Tests/Singleton/SingletonEdmModel.cs
index 24642a635..1fbd4bad9 100644
--- a/test/Microsoft.AspNetCore.OData.E2E.Tests/Singleton/SingletonEdmModel.cs
+++ b/test/Microsoft.AspNetCore.OData.E2E.Tests/Singleton/SingletonEdmModel.cs
@@ -108,11 +108,11 @@ public static IEdmModel GetEdmModel()
SingletonConfiguration monstersIncConfiguration = builder.Singleton("MonstersInc");
//monstersIncConfiguration.EntityType.Action("ResetDataSource");
monstersIncConfiguration.EntityType.Function("GetPartnersCount").Returns();
+ builder.EntityType();
- // Singleton "Sample" - makes the NotCountable test fail
+ // Singleton "Sample"
builder.Singleton("Sample");
builder.EntityType();
- //builder.EnableLowerCamelCase(); // makes the NotCountable test fail
builder.Namespace = typeof(Company).Namespace;
return builder.GetEdmModel();
diff --git a/test/Microsoft.AspNetCore.OData.E2E.Tests/Singleton/SingletonTest.cs b/test/Microsoft.AspNetCore.OData.E2E.Tests/Singleton/SingletonTest.cs
index 77ea7852f..8e350b203 100644
--- a/test/Microsoft.AspNetCore.OData.E2E.Tests/Singleton/SingletonTest.cs
+++ b/test/Microsoft.AspNetCore.OData.E2E.Tests/Singleton/SingletonTest.cs
@@ -75,7 +75,7 @@ public async Task TestRoutes()
}
[Fact]
- public async Task SingletonContainer()
+ public async Task SingletonContainerSample()
{
// Arrange
string requestUri = "odata/Sample/SItems"; // TODO: utilize/modify existing EDM classes instead
@@ -92,6 +92,24 @@ public async Task SingletonContainer()
Assert.Equal(expectedResult, result);
}
+ [Fact]
+ public async Task SingletonContainerGeneratesCorrectNestedNextLink()
+ {
+ // Arrange
+ string requestUri = "odata/MonstersInc/Projects";
+ HttpClient client = CreateClient();
+
+ // Act
+ HttpResponseMessage response = await client.GetAsync(requestUri);
+
+ // Assert
+ string result = await response.Content.ReadAsStringAsync();
+ response.EnsureSuccessStatusCode();
+
+ string expectedResult = "{\"@odata.context\":\"http://localhost/odata/$metadata#MonstersInc/Projects(ProjectDetails())\",\"value\":[{\"Uid\":1,\"Title\":\"In Closet Scare\",\"ProjectDetails\":[{\"Uid\":1,\"Comment\":\"The original scare\"},{\"Uid\":2,\"Comment\":\"Leaving the door open is the worst mistake any employee can make\"}],\"ProjectDetails@odata.nextLink\":\"http://localhost/odata/MonstersInc/Projects/1/ProjectDetails?$skip=2\"},{\"Uid\":2,\"Title\":\"Under Bed Scare\",\"ProjectDetails\":[{\"Uid\":5,\"Comment\":\"Tried and true\"},{\"Uid\":6,\"Comment\":\"Tip: grab a foot\"}]}],\"@odata.nextLink\":\"http://localhost/odata/MonstersInc/Projects?$skip=2\"}";
+ Assert.Equal(expectedResult, result);
+ }
+
[Fact]
public async Task NotCountable()
{
diff --git a/test/Microsoft.AspNetCore.OData.E2E.Tests/Singleton/UmbrellaController.cs b/test/Microsoft.AspNetCore.OData.E2E.Tests/Singleton/UmbrellaController.cs
index d925da21d..d596f04b4 100644
--- a/test/Microsoft.AspNetCore.OData.E2E.Tests/Singleton/UmbrellaController.cs
+++ b/test/Microsoft.AspNetCore.OData.E2E.Tests/Singleton/UmbrellaController.cs
@@ -41,6 +41,7 @@ private static void InitData()
Category = CompanyCategory.Communication,
Partners = new List(),
Branches = new List(),
+ Projects = new List(),
};
}
From be1611ed4a8636d502a8e82f94b136e97fd1e3ad Mon Sep 17 00:00:00 2001
From: Lisi Case <49213376+lisicase@users.noreply.github.com>
Date: Thu, 29 Sep 2022 23:08:31 -0700
Subject: [PATCH 09/17] Removed E2E test based on client's repro
---
.../Singleton/SampleController.cs | 83 -------------------
.../Singleton/SingletonDataModel.cs | 36 +-------
.../Singleton/SingletonEdmModel.cs | 4 -
.../Singleton/SingletonTest.cs | 21 +----
4 files changed, 2 insertions(+), 142 deletions(-)
delete mode 100644 test/Microsoft.AspNetCore.OData.E2E.Tests/Singleton/SampleController.cs
diff --git a/test/Microsoft.AspNetCore.OData.E2E.Tests/Singleton/SampleController.cs b/test/Microsoft.AspNetCore.OData.E2E.Tests/Singleton/SampleController.cs
deleted file mode 100644
index b32ecec16..000000000
--- a/test/Microsoft.AspNetCore.OData.E2E.Tests/Singleton/SampleController.cs
+++ /dev/null
@@ -1,83 +0,0 @@
-using Microsoft.AspNetCore.Mvc;
-using Microsoft.AspNetCore.OData.Routing.Controllers;
-using Microsoft.AspNetCore.OData.Query;
-using System.Collections.Generic;
-
-namespace Microsoft.AspNetCore.OData.E2E.Tests.Singleton
-{
- ///
- /// Present a sample situation for Issue #701
- ///
- [Route("odata/Sample")]
- public class SampleController : ODataController
- {
- public static Sample _sample;
-
- static SampleController()
- {
- InitData();
- }
-
- private static void InitData()
- {
- // Create sample item guides
- SampleItemGuide si1 = CreateSIG(1);
- SampleItemGuide si2 = CreateSIG(2);
- SampleItemGuide si3 = CreateSIG(3);
-
- // Create sample items
- SampleItems sampleItems1 = new SampleItems
- {
- Uid = "sampleitems1"
- };
- var items1 = new List();
- items1.Add(si1);
- items1.Add(si2);
- items1.Add(si3);
- sampleItems1.SampleItem_guide = items1;
-
- SampleItems sampleItems2 = new SampleItems
- {
- Uid = "sampleitems2"
- };
- var items2 = new List();
- items2.Add(si3);
- sampleItems2.SampleItem_guide = items2;
-
- // Create sample
- _sample = new Sample();
- var SItems = new List();
- SItems.Add(sampleItems1);
- SItems.Add(sampleItems2);
- SItems.Add(sampleItems2);
- SItems.Add(sampleItems2);
- _sample.SItems = SItems;
- }
-
- private static SampleItemGuide CreateSIG(int num)
- {
- SampleItemGuide sig = new SampleItemGuide
- {
- Uid = "sampleuid" + num,
- Type = "sampletype" + num
- };
- return sig;
- }
-
- #region Query
- [HttpGet("")]
- [EnableQuery]
- public IActionResult GetSampleAsync()
- {
- return Ok(_sample);
- }
-
- [HttpGet("SItems")]
- [EnableQuery(PageSize = 2)]
- public IActionResult GetCatalogExamsAsync()
- {
- return Ok(_sample.SItems);
- }
- #endregion
- }
-}
diff --git a/test/Microsoft.AspNetCore.OData.E2E.Tests/Singleton/SingletonDataModel.cs b/test/Microsoft.AspNetCore.OData.E2E.Tests/Singleton/SingletonDataModel.cs
index 539d42104..5c906dc36 100644
--- a/test/Microsoft.AspNetCore.OData.E2E.Tests/Singleton/SingletonDataModel.cs
+++ b/test/Microsoft.AspNetCore.OData.E2E.Tests/Singleton/SingletonDataModel.cs
@@ -101,38 +101,4 @@ public class SubCompany : Company
public string Description { get; set; }
public Office Office { get; set; }
}
-
- // Sample case for Issue #701
- public class BestCompaniesEver
- {
- [Contained]
- [ForeignKey("Uid")]
- [AutoExpand]
- public IEnumerable BestCompanies { get; set; } = Enumerable.Empty();
- }
-
- public class Sample
- {
- [Contained]
- [ForeignKey("Uid")]
- [AutoExpand]
- public IEnumerable SItems { get; set; } = Enumerable.Empty();
- }
-
- public class SampleItems
- {
- [Key]
- public string Uid { get; set; }
-
- [AutoExpand]
- [Contained]
- public IEnumerable SampleItem_guide { get; set; } = System.Array.Empty();
- }
-
- public class SampleItemGuide
- {
- [Key]
- public string Uid { get; set; }
- public string Type { get; set; }
- }
-}
+}
\ No newline at end of file
diff --git a/test/Microsoft.AspNetCore.OData.E2E.Tests/Singleton/SingletonEdmModel.cs b/test/Microsoft.AspNetCore.OData.E2E.Tests/Singleton/SingletonEdmModel.cs
index 1fbd4bad9..bfc112dcd 100644
--- a/test/Microsoft.AspNetCore.OData.E2E.Tests/Singleton/SingletonEdmModel.cs
+++ b/test/Microsoft.AspNetCore.OData.E2E.Tests/Singleton/SingletonEdmModel.cs
@@ -110,10 +110,6 @@ public static IEdmModel GetEdmModel()
monstersIncConfiguration.EntityType.Function("GetPartnersCount").Returns();
builder.EntityType();
- // Singleton "Sample"
- builder.Singleton("Sample");
- builder.EntityType();
-
builder.Namespace = typeof(Company).Namespace;
return builder.GetEdmModel();
}
diff --git a/test/Microsoft.AspNetCore.OData.E2E.Tests/Singleton/SingletonTest.cs b/test/Microsoft.AspNetCore.OData.E2E.Tests/Singleton/SingletonTest.cs
index 8e350b203..cf04f3c8f 100644
--- a/test/Microsoft.AspNetCore.OData.E2E.Tests/Singleton/SingletonTest.cs
+++ b/test/Microsoft.AspNetCore.OData.E2E.Tests/Singleton/SingletonTest.cs
@@ -31,8 +31,7 @@ protected static void UpdateConfigureServices(IServiceCollection services)
typeof(UmbrellaController),
typeof(MonstersIncController),
typeof(PartnersController),
- typeof(ODataEndpointController),
- typeof(SampleController)
+ typeof(ODataEndpointController)
};
services.ConfigureControllers(controllers);
@@ -74,24 +73,6 @@ public async Task TestRoutes()
string contentOfString = await response.Content.ReadAsStringAsync();
}
- [Fact]
- public async Task SingletonContainerSample()
- {
- // Arrange
- string requestUri = "odata/Sample/SItems"; // TODO: utilize/modify existing EDM classes instead
- HttpClient client = CreateClient();
-
- // Act
- HttpResponseMessage response = await client.GetAsync(requestUri);
-
- // Assert
- string result = await response.Content.ReadAsStringAsync();
- response.EnsureSuccessStatusCode();
-
- string expectedResult = "{\"@odata.context\":\"http://localhost/odata/$metadata#Sample/SItems(SampleItem_guide())\",\"value\":[{\"Uid\":\"sampleitems1\",\"SampleItem_guide\":[{\"Uid\":\"sampleuid1\",\"Type\":\"sampletype1\"},{\"Uid\":\"sampleuid2\",\"Type\":\"sampletype2\"}],\"SampleItem_guide@odata.nextLink\":\"http://localhost/odata/Sample/SItems/sampleitems1/SampleItem_guide?$skip=2\"},{\"Uid\":\"sampleitems2\",\"SampleItem_guide\":[{\"Uid\":\"sampleuid3\",\"Type\":\"sampletype3\"}]}],\"@odata.nextLink\":\"http://localhost/odata/Sample/SItems?$skip=2\"}";
- Assert.Equal(expectedResult, result);
- }
-
[Fact]
public async Task SingletonContainerGeneratesCorrectNestedNextLink()
{
From 23218a2fe5c543845504fe301d5f68de6ba6084a Mon Sep 17 00:00:00 2001
From: Lisi Case <49213376+lisicase@users.noreply.github.com>
Date: Thu, 29 Sep 2022 23:10:00 -0700
Subject: [PATCH 10/17] Removed client's repro
---
AspNetCoreOData.sln | 11 +--
.../Controllers/SampleController.cs | 28 --------
sample/Issue701_Repro/Issue701_Repro.csproj | 11 ---
sample/Issue701_Repro/Models/DataSource.cs | 70 -------------------
sample/Issue701_Repro/Models/Sample.cs | 33 ---------
sample/Issue701_Repro/Program.cs | 18 -----
.../Properties/launchSettings.json | 31 --------
sample/Issue701_Repro/Startup.cs | 59 ----------------
.../appsettings.Development.json | 9 ---
sample/Issue701_Repro/appsettings.json | 10 ---
10 files changed, 2 insertions(+), 278 deletions(-)
delete mode 100644 sample/Issue701_Repro/Controllers/SampleController.cs
delete mode 100644 sample/Issue701_Repro/Issue701_Repro.csproj
delete mode 100644 sample/Issue701_Repro/Models/DataSource.cs
delete mode 100644 sample/Issue701_Repro/Models/Sample.cs
delete mode 100644 sample/Issue701_Repro/Program.cs
delete mode 100644 sample/Issue701_Repro/Properties/launchSettings.json
delete mode 100644 sample/Issue701_Repro/Startup.cs
delete mode 100644 sample/Issue701_Repro/appsettings.Development.json
delete mode 100644 sample/Issue701_Repro/appsettings.json
diff --git a/AspNetCoreOData.sln b/AspNetCoreOData.sln
index b691f705b..a2771489b 100644
--- a/AspNetCoreOData.sln
+++ b/AspNetCoreOData.sln
@@ -1,7 +1,7 @@
Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio Version 16
-VisualStudioVersion = 16.0.32901.82
+# Visual Studio Version 17
+VisualStudioVersion = 17.3.32901.215
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{2F0E102B-EB33-4025-BE56-7B8F9D2C4B8A}"
EndProject
@@ -27,8 +27,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ODataSampleCommon", "sample
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ODataAlternateKeySample", "sample\ODataAlternateKeySample\ODataAlternateKeySample.csproj", "{7B153669-A42F-4511-8BDB-587B3B27B2F3}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Issue701_Repro", "sample\Issue701_Repro\Issue701_Repro.csproj", "{C1070485-57AD-4904-B4C2-45F7F683565E}"
-EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -71,10 +69,6 @@ Global
{7B153669-A42F-4511-8BDB-587B3B27B2F3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7B153669-A42F-4511-8BDB-587B3B27B2F3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7B153669-A42F-4511-8BDB-587B3B27B2F3}.Release|Any CPU.Build.0 = Release|Any CPU
- {C1070485-57AD-4904-B4C2-45F7F683565E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {C1070485-57AD-4904-B4C2-45F7F683565E}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {C1070485-57AD-4904-B4C2-45F7F683565E}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {C1070485-57AD-4904-B4C2-45F7F683565E}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -89,7 +83,6 @@ Global
{CE04E38B-547F-46C0-ABE4-F981E3A1874F} = {B1F86961-6958-4617-ACA4-C231F95AE099}
{647EFCFA-55A7-4F0A-AD40-4B6EB1BFCFFA} = {B1F86961-6958-4617-ACA4-C231F95AE099}
{7B153669-A42F-4511-8BDB-587B3B27B2F3} = {B1F86961-6958-4617-ACA4-C231F95AE099}
- {C1070485-57AD-4904-B4C2-45F7F683565E} = {B1F86961-6958-4617-ACA4-C231F95AE099}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {540C9752-AAC0-49EA-BA60-78490C90FF86}
diff --git a/sample/Issue701_Repro/Controllers/SampleController.cs b/sample/Issue701_Repro/Controllers/SampleController.cs
deleted file mode 100644
index 09f98900f..000000000
--- a/sample/Issue701_Repro/Controllers/SampleController.cs
+++ /dev/null
@@ -1,28 +0,0 @@
-using Microsoft.AspNetCore.Mvc;
-using Microsoft.AspNetCore.OData.Routing.Controllers;
-using Issue701_Repro.Models;
-using Microsoft.AspNetCore.OData.Query;
-
-namespace Issue701_nextLink.Tests.Controllers
-{
- public class SampleController : ODataController
- {
- //[ApiVersion("1.0")]
- //[HttpGet("v{v:apiVersion}/sample")]
- [HttpGet("sample")]
- [EnableQuery]
- public IActionResult GetSampleAsync(/*[FromQuery] QueryStringParameters queryString*/)
- {
- return this.Ok(DataSource.GetSample());
- }
-
- //[ApiVersion("1.0")]
- //[HttpGet("v{v:apiVersion}/sample/sampleitems")]
- [HttpGet("sample/SItems")]
- [EnableQuery(PageSize = 2)]
- public IActionResult GetCatalogExamsAsync(/*[FromQuery] QueryStringParameters queryString*/)
- {
- return this.Ok(DataSource.GetSampleItems());
- }
- }
-}
\ No newline at end of file
diff --git a/sample/Issue701_Repro/Issue701_Repro.csproj b/sample/Issue701_Repro/Issue701_Repro.csproj
deleted file mode 100644
index c2648027b..000000000
--- a/sample/Issue701_Repro/Issue701_Repro.csproj
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
-
- net6.0
-
-
-
-
-
-
-
diff --git a/sample/Issue701_Repro/Models/DataSource.cs b/sample/Issue701_Repro/Models/DataSource.cs
deleted file mode 100644
index 082345f2a..000000000
--- a/sample/Issue701_Repro/Models/DataSource.cs
+++ /dev/null
@@ -1,70 +0,0 @@
-using System.Collections.Generic;
-
-namespace Issue701_Repro.Models
-{
- public static class DataSource
- {
- private static Sample _sample { get; set; }
-
- public static Sample GetSample()
- {
- EnsureData();
- return _sample;
- }
-
- public static IEnumerable GetSampleItems()
- {
- EnsureData();
- return _sample.SItems;
- }
-
- private static void EnsureData()
- {
- if (_sample == null)
- {
- // Create sample item guides
- SampleItemGuide si1 = CreateSIG(1);
- SampleItemGuide si2 = CreateSIG(2);
- SampleItemGuide si3 = CreateSIG(3);
-
- // Create sample items
- SampleItems sampleItems1 = new SampleItems
- {
- Uid = "sampleitems1"
- };
- var items1 = new List();
- items1.Add(si1);
- items1.Add(si2);
- items1.Add(si3);
- sampleItems1.SampleItem_guide = items1;
-
- SampleItems sampleItems2 = new SampleItems
- {
- Uid = "sampleitems2"
- };
- var items2 = new List();
- items2.Add(si3);
- sampleItems2.SampleItem_guide = items2;
-
- // Create sample
- _sample = new Sample();
- var SItems = new List();
- SItems.Add(sampleItems1);
- SItems.Add(sampleItems2);
- SItems.Add(sampleItems2);
- SItems.Add(sampleItems2);
- _sample.SItems = SItems;
- }
- }
-
- private static SampleItemGuide CreateSIG(int num)
- {
- SampleItemGuide sig = new SampleItemGuide
- {
- Uid = "sampleuid" + num,
- Type = "sampletype" + num
- };
- return sig;
- }
- }
-}
\ No newline at end of file
diff --git a/sample/Issue701_Repro/Models/Sample.cs b/sample/Issue701_Repro/Models/Sample.cs
deleted file mode 100644
index d89ca0688..000000000
--- a/sample/Issue701_Repro/Models/Sample.cs
+++ /dev/null
@@ -1,33 +0,0 @@
-using Microsoft.OData.ModelBuilder;
-using System.ComponentModel.DataAnnotations.Schema;
-using System.ComponentModel.DataAnnotations;
-using System.Collections.Generic;
-using System.Linq;
-
-namespace Issue701_Repro.Models
-{
- public class Sample
- {
- [Contained]
- [ForeignKey("Uid")]
- [AutoExpand]
- public IEnumerable SItems { get; set; } = Enumerable.Empty();
- }
-
- public class SampleItems
- {
- [Key]
- public string Uid { get; set; }
-
- [AutoExpand]
- [Contained]
- public IEnumerable SampleItem_guide { get; set; } = System.Array.Empty();
- }
-
- public class SampleItemGuide
- {
- [Key]
- public string Uid { get; set; }
- public string Type { get; set; }
- }
-}
diff --git a/sample/Issue701_Repro/Program.cs b/sample/Issue701_Repro/Program.cs
deleted file mode 100644
index 413e93d97..000000000
--- a/sample/Issue701_Repro/Program.cs
+++ /dev/null
@@ -1,18 +0,0 @@
-using Issue701_Repro;
-using Microsoft.AspNetCore;
-using Microsoft.AspNetCore.Hosting;
-
-namespace Issue701_Repro
-{
- public class Program
- {
- public static void Main(string[] args)
- {
- CreateWebHostBuilder(args).Build().Run();
- }
-
- public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
- WebHost.CreateDefaultBuilder(args)
- .UseStartup();
- }
-}
\ No newline at end of file
diff --git a/sample/Issue701_Repro/Properties/launchSettings.json b/sample/Issue701_Repro/Properties/launchSettings.json
deleted file mode 100644
index 271df4752..000000000
--- a/sample/Issue701_Repro/Properties/launchSettings.json
+++ /dev/null
@@ -1,31 +0,0 @@
-{
- "$schema": "http://json.schemastore.org/launchsettings.json",
- "iisSettings": {
- "windowsAuthentication": false,
- "anonymousAuthentication": true,
- "iisExpress": {
- "applicationUrl": "http://localhost:51579",
- "sslPort": 44377
- }
- },
- "profiles": {
- "IIS Express": {
- "commandName": "IISExpress",
- "launchBrowser": true,
- "launchUrl": "sample",
- "environmentVariables": {
- "ASPNETCORE_ENVIRONMENT": "Development"
- }
- },
- "Issue701_Repro": {
- "commandName": "Project",
- "dotnetRunMessages": "true",
- "launchBrowser": true,
- "launchUrl": "sample",
- "applicationUrl": "https://localhost:5001;http://localhost:5000",
- "environmentVariables": {
- "ASPNETCORE_ENVIRONMENT": "Development"
- }
- }
- }
-}
diff --git a/sample/Issue701_Repro/Startup.cs b/sample/Issue701_Repro/Startup.cs
deleted file mode 100644
index f8de6ba0c..000000000
--- a/sample/Issue701_Repro/Startup.cs
+++ /dev/null
@@ -1,59 +0,0 @@
-using Microsoft.AspNetCore.OData;
-using Microsoft.AspNetCore.Builder;
-using Microsoft.AspNetCore.Hosting;
-using Microsoft.Extensions.Configuration;
-using Microsoft.Extensions.DependencyInjection;
-using Microsoft.OData.Edm;
-using Microsoft.OData.ModelBuilder;
-using Issue701_Repro.Models;
-
-namespace Issue701_Repro
-{
- public class Startup
- {
- public Startup(IConfiguration configuration)
- {
- Configuration = configuration;
- }
-
- public IConfiguration Configuration { get; }
-
- // This method gets called by the runtime to add services to the container.
- public void ConfigureServices(IServiceCollection services)
- {
- services.AddControllers().AddOData(opt => opt.Count().Filter().Expand().Select().OrderBy().SetMaxTop(5)
- .AddRouteComponents(GetSampleEntityDataModel()));
- }
-
- // This method gets called by the runtime to configure the HTTP request pipeline.
- public void Configure(IApplicationBuilder app, IHostingEnvironment env)
- {
- if (env.IsDevelopment())
- {
- app.UseDeveloperExceptionPage();
- }
- app.UseRouting();
- app.UseEndpoints(endpoints =>
- {
- endpoints.MapControllers();
- });
- }
-
- public IEdmModel GetSampleEntityDataModel()
- {
- var builder = new ODataConventionModelBuilder()
- {
- Namespace = "Samples",
- ContainerName = "SamplesContainer"
- };
-
- //Catalog endpoint
- builder.Singleton("sample");
- builder.EntityType();
- builder.EnableLowerCamelCase();
- IEdmModel model = builder.GetEdmModel();
-
- return model;
- }
- }
-}
\ No newline at end of file
diff --git a/sample/Issue701_Repro/appsettings.Development.json b/sample/Issue701_Repro/appsettings.Development.json
deleted file mode 100644
index 8983e0fc1..000000000
--- a/sample/Issue701_Repro/appsettings.Development.json
+++ /dev/null
@@ -1,9 +0,0 @@
-{
- "Logging": {
- "LogLevel": {
- "Default": "Information",
- "Microsoft": "Warning",
- "Microsoft.Hosting.Lifetime": "Information"
- }
- }
-}
diff --git a/sample/Issue701_Repro/appsettings.json b/sample/Issue701_Repro/appsettings.json
deleted file mode 100644
index d9d9a9bff..000000000
--- a/sample/Issue701_Repro/appsettings.json
+++ /dev/null
@@ -1,10 +0,0 @@
-{
- "Logging": {
- "LogLevel": {
- "Default": "Information",
- "Microsoft": "Warning",
- "Microsoft.Hosting.Lifetime": "Information"
- }
- },
- "AllowedHosts": "*"
-}
From a4ed6e9fde42396a5a734891a4a9771824b761b5 Mon Sep 17 00:00:00 2001
From: Lisi Case <49213376+lisicase@users.noreply.github.com>
Date: Fri, 30 Sep 2022 15:02:44 -0700
Subject: [PATCH 11/17] Cleaned up code based on PR review feedback
---
.../Formatter/LinkGenerationHelpers.cs | 6 ++--
.../Singleton/MonstersIncController.cs | 18 +++++-----
.../Singleton/SingletonDataModel.cs | 8 ++---
.../Singleton/SingletonEdmModel.cs | 1 -
.../Singleton/SingletonTest.cs | 13 ++++++-
.../Formatter/LinkGenerationHelpersTest.cs | 35 ++++++++++++++-----
6 files changed, 53 insertions(+), 28 deletions(-)
diff --git a/src/Microsoft.AspNetCore.OData/Formatter/LinkGenerationHelpers.cs b/src/Microsoft.AspNetCore.OData/Formatter/LinkGenerationHelpers.cs
index 2a5948810..0cb5b4539 100644
--- a/src/Microsoft.AspNetCore.OData/Formatter/LinkGenerationHelpers.cs
+++ b/src/Microsoft.AspNetCore.OData/Formatter/LinkGenerationHelpers.cs
@@ -349,7 +349,7 @@ internal static IList GenerateBaseODataPathSegments(this Resou
return odataPath;
}
- private static void GenerateBaseODataPathSegmentsForContainedNavProperties(
+ private static void GenerateBaseODataPathSegments(
ODataPath path,
IEdmNavigationSource navigationSource,
IList odataPath)
@@ -450,7 +450,7 @@ private static void GenerateBaseODataPathSegmentsForEntity(
{
// If the navigation is a contained property, we need to walk all of the path segments
// to generate a contextually accurate URI.
- GenerateBaseODataPathSegmentsForContainedNavProperties(
+ GenerateBaseODataPathSegments(
resourceContext.SerializerContext.Path, resourceContext.NavigationSource, odataPath);
odataPath.Add(new KeySegment(ConventionsHelpers.GetEntityKey(resourceContext), resourceContext.StructuredType as IEdmEntityType,
@@ -461,7 +461,7 @@ private static void GenerateBaseODataPathSegmentsForFeed(
this ResourceSetContext feedContext,
IList odataPath)
{
- GenerateBaseODataPathSegmentsForContainedNavProperties(feedContext.Request.ODataFeature().Path,
+ GenerateBaseODataPathSegments(feedContext.Request.ODataFeature().Path,
feedContext.EntitySetBase,
odataPath);
}
diff --git a/test/Microsoft.AspNetCore.OData.E2E.Tests/Singleton/MonstersIncController.cs b/test/Microsoft.AspNetCore.OData.E2E.Tests/Singleton/MonstersIncController.cs
index 4c226f991..250774e00 100644
--- a/test/Microsoft.AspNetCore.OData.E2E.Tests/Singleton/MonstersIncController.cs
+++ b/test/Microsoft.AspNetCore.OData.E2E.Tests/Singleton/MonstersIncController.cs
@@ -46,28 +46,28 @@ private static void InitData()
{
new Project
{
- Uid = 1,
+ Id = 1,
Title = "In Closet Scare",
ProjectDetails = new List()
{
- new ProjectDetail { Uid = 1, Comment = "The original scare" },
- new ProjectDetail { Uid = 2, Comment = "Leaving the door open is the worst mistake any employee can make" },
- new ProjectDetail { Uid = 3, Comment = "Leaving the door open could let it not only a draft, but a child" },
- new ProjectDetail { Uid = 4, Comment = "Has led to the intrusion of a young girl, Boo" }
+ new ProjectDetail { Id = 1, Comment = "The original scare" },
+ new ProjectDetail { Id = 2, Comment = "Leaving the door open is the worst mistake any employee can make" },
+ new ProjectDetail { Id = 3, Comment = "Leaving the door open could let it not only a draft, but a child" },
+ new ProjectDetail { Id = 4, Comment = "Has led to the intrusion of a young girl, Boo" }
},
},
new Project
{
- Uid = 2,
+ Id = 2,
Title = "Under Bed Scare",
ProjectDetails = new List() {
- new ProjectDetail { Uid = 5, Comment = "Tried and true" },
- new ProjectDetail { Uid = 6, Comment = "Tip: grab a foot"}
+ new ProjectDetail { Id = 5, Comment = "Tried and true" },
+ new ProjectDetail { Id = 6, Comment = "Tip: grab a foot"}
},
},
new Project
{
- Uid = 3,
+ Id = 3,
Title = "Midnight Snack in Kitchen Scare",
ProjectDetails= new List(),
},
diff --git a/test/Microsoft.AspNetCore.OData.E2E.Tests/Singleton/SingletonDataModel.cs b/test/Microsoft.AspNetCore.OData.E2E.Tests/Singleton/SingletonDataModel.cs
index 5c906dc36..05ab7e1d9 100644
--- a/test/Microsoft.AspNetCore.OData.E2E.Tests/Singleton/SingletonDataModel.cs
+++ b/test/Microsoft.AspNetCore.OData.E2E.Tests/Singleton/SingletonDataModel.cs
@@ -53,7 +53,6 @@ public class Company
public IList Branches { get; set; }
[Contained]
- [ForeignKey("Uid")]
[AutoExpand]
public IList Projects { get; set; }
}
@@ -63,7 +62,6 @@ public class Company
///
public class Office
{
- public string Uid { get; set; }
public string City { get; set; }
public string Address { get; set; }
}
@@ -73,8 +71,7 @@ public class Office
///
public class Project
{
- [Key]
- public int Uid { get; set; }
+ public int Id { get; set; }
public string Title { get; set; }
[AutoExpand]
@@ -87,8 +84,7 @@ public class Project
///
public class ProjectDetail
{
- [Key]
- public int Uid { get; set; }
+ public int Id { get; set; }
public string Comment { get; set; }
}
diff --git a/test/Microsoft.AspNetCore.OData.E2E.Tests/Singleton/SingletonEdmModel.cs b/test/Microsoft.AspNetCore.OData.E2E.Tests/Singleton/SingletonEdmModel.cs
index bfc112dcd..83859e030 100644
--- a/test/Microsoft.AspNetCore.OData.E2E.Tests/Singleton/SingletonEdmModel.cs
+++ b/test/Microsoft.AspNetCore.OData.E2E.Tests/Singleton/SingletonEdmModel.cs
@@ -108,7 +108,6 @@ public static IEdmModel GetEdmModel()
SingletonConfiguration monstersIncConfiguration = builder.Singleton("MonstersInc");
//monstersIncConfiguration.EntityType.Action("ResetDataSource");
monstersIncConfiguration.EntityType.Function("GetPartnersCount").Returns();
- builder.EntityType();
builder.Namespace = typeof(Company).Namespace;
return builder.GetEdmModel();
diff --git a/test/Microsoft.AspNetCore.OData.E2E.Tests/Singleton/SingletonTest.cs b/test/Microsoft.AspNetCore.OData.E2E.Tests/Singleton/SingletonTest.cs
index cf04f3c8f..8682e4747 100644
--- a/test/Microsoft.AspNetCore.OData.E2E.Tests/Singleton/SingletonTest.cs
+++ b/test/Microsoft.AspNetCore.OData.E2E.Tests/Singleton/SingletonTest.cs
@@ -87,7 +87,18 @@ public async Task SingletonContainerGeneratesCorrectNestedNextLink()
string result = await response.Content.ReadAsStringAsync();
response.EnsureSuccessStatusCode();
- string expectedResult = "{\"@odata.context\":\"http://localhost/odata/$metadata#MonstersInc/Projects(ProjectDetails())\",\"value\":[{\"Uid\":1,\"Title\":\"In Closet Scare\",\"ProjectDetails\":[{\"Uid\":1,\"Comment\":\"The original scare\"},{\"Uid\":2,\"Comment\":\"Leaving the door open is the worst mistake any employee can make\"}],\"ProjectDetails@odata.nextLink\":\"http://localhost/odata/MonstersInc/Projects/1/ProjectDetails?$skip=2\"},{\"Uid\":2,\"Title\":\"Under Bed Scare\",\"ProjectDetails\":[{\"Uid\":5,\"Comment\":\"Tried and true\"},{\"Uid\":6,\"Comment\":\"Tip: grab a foot\"}]}],\"@odata.nextLink\":\"http://localhost/odata/MonstersInc/Projects?$skip=2\"}";
+ string expectedResult =
+ "{\"@odata.context\":\"http://localhost/odata/$metadata#MonstersInc/Projects(ProjectDetails())\"," +
+ "\"value\":[" +
+ "{\"Id\":1,\"Title\":\"In Closet Scare\",\"ProjectDetails\":[" +
+ "{\"Id\":1,\"Comment\":\"The original scare\"}," +
+ "{\"Id\":2,\"Comment\":\"Leaving the door open is the worst mistake any employee can make\"}]," +
+ "\"ProjectDetails@odata.nextLink\":\"http://localhost/odata/MonstersInc/Projects/1/ProjectDetails?$skip=2\"}," +
+ "{\"Id\":2,\"Title\":\"Under Bed Scare\",\"ProjectDetails\":[" +
+ "{\"Id\":5,\"Comment\":\"Tried and true\"}," +
+ "{\"Id\":6,\"Comment\":\"Tip: grab a foot\"}]}]," +
+ "\"@odata.nextLink\":\"http://localhost/odata/MonstersInc/Projects?$skip=2\"" +
+ "}";
Assert.Equal(expectedResult, result);
}
diff --git a/test/Microsoft.AspNetCore.OData.Tests/Formatter/LinkGenerationHelpersTest.cs b/test/Microsoft.AspNetCore.OData.Tests/Formatter/LinkGenerationHelpersTest.cs
index 3b22d1c15..5a8af8ef8 100644
--- a/test/Microsoft.AspNetCore.OData.Tests/Formatter/LinkGenerationHelpersTest.cs
+++ b/test/Microsoft.AspNetCore.OData.Tests/Formatter/LinkGenerationHelpersTest.cs
@@ -10,6 +10,7 @@
using System.Linq;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.OData.Formatter;
+using Microsoft.AspNetCore.OData.Formatter.Serialization;
using Microsoft.AspNetCore.OData.Tests.Commons;
using Microsoft.AspNetCore.OData.Tests.Edm;
using Microsoft.AspNetCore.OData.Tests.Extensions;
@@ -110,38 +111,56 @@ public void GenerateNavigationLink_WorksToGenerateExpectedNavigationLink_ForSing
Assert.Equal(expectedNavigationLink, uri.AbsoluteUri);
}
- [Fact]
- public void GenerateNavigationLink_WorksToGenerateExpectedBasePathSegments_ForSingletonContainer()
+ private ResourceContext GetOrderLineResourceForNewSingletonContainer()
{
// Arrange
- IEdmSingleton myVipOrder = _myOrderModel.FindDeclaredSingleton("VipOrder");
+ IEdmSingleton myVipOrder = _myOrderModel.FindDeclaredSingleton("VipOrder");
IEdmEntityType vipOrderType = (IEdmEntityType)myVipOrder.Type;
IEdmNavigationProperty orderLinesProperty = vipOrderType.NavigationProperties().Single(x => x.ContainsTarget && x.Name == "OrderLines");
IEdmContainedEntitySet orderLines = (IEdmContainedEntitySet)myVipOrder.FindNavigationTarget(orderLinesProperty);
IEdmEntityType orderLine = _myOrderModel.SchemaElements.OfType().First(e => e.Name == "OrderLine");
IEdmNavigationProperty orderLineDetailsNav = orderLine.NavigationProperties().First();
- // Link relationships
HttpRequest request = RequestFactory.Create(_myOrderModel);
-
+
ODataPath path = new ODataPath(
new SingletonSegment(myVipOrder),
new NavigationPropertySegment(orderLinesProperty, orderLines));
- var orderLineSerializerContext = ODataSerializerContextFactory.Create(_myOrderModel, orderLines, path, request);
+ ODataSerializerContext orderLineSerializerContext = ODataSerializerContextFactory.Create(_myOrderModel, orderLines, path, request);
orderLineSerializerContext.EdmProperty = orderLineDetailsNav;
- var orderLineResource = new ResourceContext(orderLineSerializerContext, orderLine.AsReference(), new { ID = 21 });
+ ResourceContext orderLineResource = new ResourceContext(orderLineSerializerContext, orderLine.AsReference(), new { ID = 21 });
orderLineSerializerContext.ExpandedResource = orderLineResource;
+ return orderLineResource;
+ }
+
+ [Fact]
+ public void GenerateBaseODataPathSegments_WorksToGenerateExpectedPath_ForSingletonContainer()
+ {
+ // Arrange
+ ResourceContext orderLineResource = GetOrderLineResourceForNewSingletonContainer();
+
// Act
IList newPaths = orderLineResource.GenerateBaseODataPathSegments();
- Uri selfLink = orderLineResource.GenerateSelfLink(false);
// Assert
Assert.Equal(3, newPaths.Count());
Assert.Equal("Microsoft.OData.UriParser.SingletonSegment", newPaths[0].GetType().FullName); // VipOrder
Assert.Equal("Microsoft.OData.UriParser.NavigationPropertySegment", newPaths[1].GetType().FullName); // OrderLines
Assert.Equal("Microsoft.OData.UriParser.KeySegment", newPaths[2].GetType().FullName); // 21
+ }
+
+ [Fact]
+ public void GenerateSelfLink_WorksToGenerateExpectedSelfLink_ForSingletonContainer()
+ {
+ // Arrange
+ ResourceContext orderLineResource = GetOrderLineResourceForNewSingletonContainer();
+
+ // Act
+ Uri selfLink = orderLineResource.GenerateSelfLink(false);
+
+ // Assert
Assert.Equal("http://localhost/VipOrder/OrderLines(21)", selfLink.AbsoluteUri);
}
From 66007f94886dc980533038bc90e77b0f525c396b Mon Sep 17 00:00:00 2001
From: Lisi Case <49213376+lisicase@users.noreply.github.com>
Date: Mon, 3 Oct 2022 15:35:56 -0700
Subject: [PATCH 12/17] Update syntax
Co-authored-by: Garrett DeBruin
---
.../Formatter/LinkGenerationHelpersTest.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/test/Microsoft.AspNetCore.OData.Tests/Formatter/LinkGenerationHelpersTest.cs b/test/Microsoft.AspNetCore.OData.Tests/Formatter/LinkGenerationHelpersTest.cs
index 5a8af8ef8..f1d2b81bc 100644
--- a/test/Microsoft.AspNetCore.OData.Tests/Formatter/LinkGenerationHelpersTest.cs
+++ b/test/Microsoft.AspNetCore.OData.Tests/Formatter/LinkGenerationHelpersTest.cs
@@ -145,7 +145,7 @@ public void GenerateBaseODataPathSegments_WorksToGenerateExpectedPath_ForSinglet
IList newPaths = orderLineResource.GenerateBaseODataPathSegments();
// Assert
- Assert.Equal(3, newPaths.Count());
+ Assert.Equal(3, newPaths.Count);
Assert.Equal("Microsoft.OData.UriParser.SingletonSegment", newPaths[0].GetType().FullName); // VipOrder
Assert.Equal("Microsoft.OData.UriParser.NavigationPropertySegment", newPaths[1].GetType().FullName); // OrderLines
Assert.Equal("Microsoft.OData.UriParser.KeySegment", newPaths[2].GetType().FullName); // 21
From a433304809187b9965ac4b87504f4f7bae188e2c Mon Sep 17 00:00:00 2001
From: Lisi Case <49213376+lisicase@users.noreply.github.com>
Date: Tue, 4 Oct 2022 10:39:04 -0700
Subject: [PATCH 13/17] Update test syntax
---
.../Microsoft.AspNetCore.OData.xml | 27 +++++++++----------
.../Formatter/LinkGenerationHelpersTest.cs | 6 ++---
2 files changed, 15 insertions(+), 18 deletions(-)
diff --git a/src/Microsoft.AspNetCore.OData/Microsoft.AspNetCore.OData.xml b/src/Microsoft.AspNetCore.OData/Microsoft.AspNetCore.OData.xml
index 4d0e931a0..7db47627e 100644
--- a/src/Microsoft.AspNetCore.OData/Microsoft.AspNetCore.OData.xml
+++ b/src/Microsoft.AspNetCore.OData/Microsoft.AspNetCore.OData.xml
@@ -1638,21 +1638,7 @@
The list of property names that can be updated.
- When the list is modified, any modified properties that were removed from the list are no longer
- considered to be changed.
-
-
-
-
-
-
-
-
-
-
-
-
- Attempts to get the value of the nested Property called from the underlying resource.
+ When the list is modified, ttempts to get the value of the nested Property called from the underlying resource.
Only properties that exist on Entity can be retrieved.
Only modified nested properties can be retrieved.
@@ -5152,6 +5138,17 @@
The of this object.
+
+
+ Represents an with no backing CLR .
+
+
+
+
+ Initializes a new instance of the class.
+
+ The of this object.
+
Initializes a new instance of the class.
diff --git a/test/Microsoft.AspNetCore.OData.Tests/Formatter/LinkGenerationHelpersTest.cs b/test/Microsoft.AspNetCore.OData.Tests/Formatter/LinkGenerationHelpersTest.cs
index f1d2b81bc..38c5ff4b6 100644
--- a/test/Microsoft.AspNetCore.OData.Tests/Formatter/LinkGenerationHelpersTest.cs
+++ b/test/Microsoft.AspNetCore.OData.Tests/Formatter/LinkGenerationHelpersTest.cs
@@ -146,9 +146,9 @@ public void GenerateBaseODataPathSegments_WorksToGenerateExpectedPath_ForSinglet
// Assert
Assert.Equal(3, newPaths.Count);
- Assert.Equal("Microsoft.OData.UriParser.SingletonSegment", newPaths[0].GetType().FullName); // VipOrder
- Assert.Equal("Microsoft.OData.UriParser.NavigationPropertySegment", newPaths[1].GetType().FullName); // OrderLines
- Assert.Equal("Microsoft.OData.UriParser.KeySegment", newPaths[2].GetType().FullName); // 21
+ Assert.IsType(newPaths[0]); // VipOrder
+ Assert.IsType(newPaths[1]); // OrderLines
+ Assert.IsType(newPaths[2]); // 21
}
[Fact]
From 7818913824601c3a211bc3ae2724c286fb3c6517 Mon Sep 17 00:00:00 2001
From: Lisi Case <49213376+lisicase@users.noreply.github.com>
Date: Tue, 4 Oct 2022 10:40:14 -0700
Subject: [PATCH 14/17] Update creation of disposable objects
---
.../Microsoft.AspNetCore.OData.xml | 133 ++++++++++--------
.../Singleton/SingletonTest.cs | 46 +++---
2 files changed, 100 insertions(+), 79 deletions(-)
diff --git a/src/Microsoft.AspNetCore.OData/Microsoft.AspNetCore.OData.xml b/src/Microsoft.AspNetCore.OData/Microsoft.AspNetCore.OData.xml
index 7db47627e..aa91edf67 100644
--- a/src/Microsoft.AspNetCore.OData/Microsoft.AspNetCore.OData.xml
+++ b/src/Microsoft.AspNetCore.OData/Microsoft.AspNetCore.OData.xml
@@ -1094,20 +1094,6 @@
The type to test.
True if the type is a DateTime; false otherwise.
-
-
- Determine if a type is a .
-
- The type to test.
- True if the type is a DateOnly; false otherwise.
-
-
-
- Determine if a type is a .
-
- The type to test.
- True if the type is a TimeOnly; false otherwise.
-
Determine if a type is a TimeSpan.
@@ -1638,7 +1624,21 @@
The list of property names that can be updated.
- When the list is modified, ttempts to get the value of the nested Property called from the underlying resource.
+ When the list is modified, any modified properties that were removed from the list are no longer
+ considered to be changed.
+
+
+
+
+
+
+
+
+
+
+
+
+ Attempts to get the value of the nested Property called from the underlying resource.
Only properties that exist on Entity can be retrieved.
Only modified nested properties can be retrieved.
@@ -1775,6 +1775,19 @@
allows and tracks changes to an delta deleted link.
+ The type of the Property
+ Returns true if the Property was found and false if not.
+
+
+
+ Clears the .
+
+
+
+
+ allows and tracks changes to an delta deleted link.
+
+
allows and tracks changes to a deleted resource.
@@ -2033,18 +2046,7 @@
Figures out if the given clr type is nonstandard edm primitive like uint, ushort, char[] etc.
- and returns the corresponding clr type to which we map like uint => long.
-
- The Edm model.
- The potential non-standard CLR type.
- A boolean value out to indicate whether the input CLR type is standard OData primitive type.
- The standard CLR type or the input CLR type itself.
-
-
-
- Gets the Edm type reference from the CLR type.
-
- The Edm model.
+ and returns the corresponding clr type to whic
The given CLR type.
null or the Edm type reference.
@@ -2109,6 +2111,17 @@
The test type reference.
Element type or itself.
+
+
+ Converts the to .
+
+ The given Edm type.
+
+ Get the elementType if it's collection or return itself's type
+
+ The test type reference.
+ Element type or itself.
+
Converts the to .
@@ -2356,15 +2369,7 @@
The containing the navigation source.
The navigation source.
- The to set.
-
-
-
- Gets the to be used while generating operation links for the given action.
-
- The containing the operation.
- The operation for which the link builder is needed.
- The for the given operation if one is set; otherwise, a new
+ The that generates operation links following OData URL conventions.
@@ -2431,6 +2436,24 @@
Provides the mapping between CLR type and Edm type.
+ s the navigation property resolved from this path.
+
+
+
+
+ Gets the navigation property path, it doesn't include the navigation property.
+
+
+
+
+ Gets the whole expand path.
+
+
+
+
+ Provides the mapping between CLR type and Edm type.
+
+
Gets the corresponding Edm primitive type for a given .
@@ -2657,16 +2680,7 @@
This is how formatters create links to invoke bound actions or functions.
-
-
- Create a new based on an entity link factory.
-
- The link factory this should use when building links.
-
- A value indicating whether the link factory generates links that follow OData conventions.
-
-
-
+
Create a new based on a feed link factory.
@@ -5138,17 +5152,6 @@
The of this object.
-
-
- Represents an with no backing CLR .
-
-
-
-
- Initializes a new instance of the class.
-
- The of this object.
-
Initializes a new instance of the class.
@@ -14414,3 +14417,19 @@
+ummary>
+ The value segment.
+
+
+
+ Gets the value segment.
+
+
+
+
+
+
+
+
+
+
diff --git a/test/Microsoft.AspNetCore.OData.E2E.Tests/Singleton/SingletonTest.cs b/test/Microsoft.AspNetCore.OData.E2E.Tests/Singleton/SingletonTest.cs
index 8682e4747..9a239f769 100644
--- a/test/Microsoft.AspNetCore.OData.E2E.Tests/Singleton/SingletonTest.cs
+++ b/test/Microsoft.AspNetCore.OData.E2E.Tests/Singleton/SingletonTest.cs
@@ -78,28 +78,30 @@ public async Task SingletonContainerGeneratesCorrectNestedNextLink()
{
// Arrange
string requestUri = "odata/MonstersInc/Projects";
- HttpClient client = CreateClient();
-
- // Act
- HttpResponseMessage response = await client.GetAsync(requestUri);
-
- // Assert
- string result = await response.Content.ReadAsStringAsync();
- response.EnsureSuccessStatusCode();
-
- string expectedResult =
- "{\"@odata.context\":\"http://localhost/odata/$metadata#MonstersInc/Projects(ProjectDetails())\"," +
- "\"value\":[" +
- "{\"Id\":1,\"Title\":\"In Closet Scare\",\"ProjectDetails\":[" +
- "{\"Id\":1,\"Comment\":\"The original scare\"}," +
- "{\"Id\":2,\"Comment\":\"Leaving the door open is the worst mistake any employee can make\"}]," +
- "\"ProjectDetails@odata.nextLink\":\"http://localhost/odata/MonstersInc/Projects/1/ProjectDetails?$skip=2\"}," +
- "{\"Id\":2,\"Title\":\"Under Bed Scare\",\"ProjectDetails\":[" +
- "{\"Id\":5,\"Comment\":\"Tried and true\"}," +
- "{\"Id\":6,\"Comment\":\"Tip: grab a foot\"}]}]," +
- "\"@odata.nextLink\":\"http://localhost/odata/MonstersInc/Projects?$skip=2\"" +
- "}";
- Assert.Equal(expectedResult, result);
+ using (HttpClient client = CreateClient())
+ {
+ // Act
+ using (HttpResponseMessage response = await client.GetAsync(requestUri))
+ {
+ // Assert
+ string result = await response.Content.ReadAsStringAsync();
+ response.EnsureSuccessStatusCode();
+
+ string expectedResult =
+ "{\"@odata.context\":\"http://localhost/odata/$metadata#MonstersInc/Projects(ProjectDetails())\"," +
+ "\"value\":[" +
+ "{\"Id\":1,\"Title\":\"In Closet Scare\",\"ProjectDetails\":[" +
+ "{\"Id\":1,\"Comment\":\"The original scare\"}," +
+ "{\"Id\":2,\"Comment\":\"Leaving the door open is the worst mistake any employee can make\"}]," +
+ "\"ProjectDetails@odata.nextLink\":\"http://localhost/odata/MonstersInc/Projects/1/ProjectDetails?$skip=2\"}," +
+ "{\"Id\":2,\"Title\":\"Under Bed Scare\",\"ProjectDetails\":[" +
+ "{\"Id\":5,\"Comment\":\"Tried and true\"}," +
+ "{\"Id\":6,\"Comment\":\"Tip: grab a foot\"}]}]," +
+ "\"@odata.nextLink\":\"http://localhost/odata/MonstersInc/Projects?$skip=2\"" +
+ "}";
+ Assert.Equal(expectedResult, result);
+ }
+ }
}
[Fact]
From aeb8e3941d23921915eae1e68f7e590c5c3d77f8 Mon Sep 17 00:00:00 2001
From: Lisi Case <49213376+lisicase@users.noreply.github.com>
Date: Wed, 5 Oct 2022 13:56:30 -0700
Subject: [PATCH 15/17] Test validity of E2E next link
---
.../Singleton/SingletonTest.cs | 35 +++++++++++++------
1 file changed, 24 insertions(+), 11 deletions(-)
diff --git a/test/Microsoft.AspNetCore.OData.E2E.Tests/Singleton/SingletonTest.cs b/test/Microsoft.AspNetCore.OData.E2E.Tests/Singleton/SingletonTest.cs
index 9a239f769..a3ade8fd1 100644
--- a/test/Microsoft.AspNetCore.OData.E2E.Tests/Singleton/SingletonTest.cs
+++ b/test/Microsoft.AspNetCore.OData.E2E.Tests/Singleton/SingletonTest.cs
@@ -74,20 +74,17 @@ public async Task TestRoutes()
}
[Fact]
- public async Task SingletonContainerGeneratesCorrectNestedNextLink()
+ public async Task SingletonContainerGeneratesCorrectNextLink()
{
// Arrange
string requestUri = "odata/MonstersInc/Projects";
+ string nextLinkUri = "odata/MonstersInc/Projects?$skip=2";
+ //string nestedNextLinkUri = "odata/MonstersInc/Projects/1/ProjectDetails?$skip=2";
+
using (HttpClient client = CreateClient())
{
- // Act
- using (HttpResponseMessage response = await client.GetAsync(requestUri))
- {
- // Assert
- string result = await response.Content.ReadAsStringAsync();
- response.EnsureSuccessStatusCode();
-
- string expectedResult =
+ // Act & Assert
+ string expectedOriginalResult =
"{\"@odata.context\":\"http://localhost/odata/$metadata#MonstersInc/Projects(ProjectDetails())\"," +
"\"value\":[" +
"{\"Id\":1,\"Title\":\"In Closet Scare\",\"ProjectDetails\":[" +
@@ -99,8 +96,24 @@ public async Task SingletonContainerGeneratesCorrectNestedNextLink()
"{\"Id\":6,\"Comment\":\"Tip: grab a foot\"}]}]," +
"\"@odata.nextLink\":\"http://localhost/odata/MonstersInc/Projects?$skip=2\"" +
"}";
- Assert.Equal(expectedResult, result);
- }
+ await RequestYieldsExpectedResult(client, requestUri, expectedOriginalResult);
+
+ string expectedNextResult =
+ "{\"@odata.context\":\"http://localhost/odata/$metadata#MonstersInc/Projects(ProjectDetails())\"," +
+ "\"value\":[{\"Id\":3,\"Title\":\"Midnight Snack in Kitchen Scare\",\"ProjectDetails\":[]}]}";
+ await RequestYieldsExpectedResult(client, nextLinkUri, expectedNextResult);
+ }
+ }
+
+ private async Task RequestYieldsExpectedResult(HttpClient client, string requestUri, string expectedResult)
+ {
+ // Act
+ using (HttpResponseMessage response = await client.GetAsync(requestUri))
+ {
+ // Assert
+ string result = await response.Content.ReadAsStringAsync();
+ response.EnsureSuccessStatusCode();
+ Assert.Equal(expectedResult, result);
}
}
From 05a450e2cb6f99f2502b77a5d41d89d460e039c7 Mon Sep 17 00:00:00 2001
From: Lisi Case <49213376+lisicase@users.noreply.github.com>
Date: Wed, 5 Oct 2022 14:31:44 -0700
Subject: [PATCH 16/17] Validate nested next link in E2E test
---
.../Singleton/MonstersIncController.cs | 13 +++++++++++++
.../Singleton/SingletonTest.cs | 13 ++++++++++---
2 files changed, 23 insertions(+), 3 deletions(-)
diff --git a/test/Microsoft.AspNetCore.OData.E2E.Tests/Singleton/MonstersIncController.cs b/test/Microsoft.AspNetCore.OData.E2E.Tests/Singleton/MonstersIncController.cs
index 250774e00..e033540cd 100644
--- a/test/Microsoft.AspNetCore.OData.E2E.Tests/Singleton/MonstersIncController.cs
+++ b/test/Microsoft.AspNetCore.OData.E2E.Tests/Singleton/MonstersIncController.cs
@@ -107,6 +107,19 @@ public IActionResult GetProjects()
return Ok(MonstersInc.Projects);
}
+ [HttpGet("Projects/{key}/ProjectDetails")]
+ [EnableQuery]
+ public IActionResult GetProjectDetails(int key)
+ {
+ var project = MonstersInc.Projects.FirstOrDefault(a => a.Id == key);
+ if (project == null)
+ {
+ return NotFound($"Project with given key {key} does not exist.");
+ }
+
+ return Ok(project.ProjectDetails);
+ }
+
[HttpGet("Branches/$count")]
public IActionResult GetBranchesCount(ODataQueryOptions options)
{
diff --git a/test/Microsoft.AspNetCore.OData.E2E.Tests/Singleton/SingletonTest.cs b/test/Microsoft.AspNetCore.OData.E2E.Tests/Singleton/SingletonTest.cs
index a3ade8fd1..341f7f405 100644
--- a/test/Microsoft.AspNetCore.OData.E2E.Tests/Singleton/SingletonTest.cs
+++ b/test/Microsoft.AspNetCore.OData.E2E.Tests/Singleton/SingletonTest.cs
@@ -74,12 +74,12 @@ public async Task TestRoutes()
}
[Fact]
- public async Task SingletonContainerGeneratesCorrectNextLink()
+ public async Task SingletonContainerGeneratesCorrectNextLinks()
{
// Arrange
string requestUri = "odata/MonstersInc/Projects";
string nextLinkUri = "odata/MonstersInc/Projects?$skip=2";
- //string nestedNextLinkUri = "odata/MonstersInc/Projects/1/ProjectDetails?$skip=2";
+ string nestedNextLinkUri = "odata/MonstersInc/Projects/1/ProjectDetails?$skip=2";
using (HttpClient client = CreateClient())
{
@@ -100,8 +100,15 @@ public async Task SingletonContainerGeneratesCorrectNextLink()
string expectedNextResult =
"{\"@odata.context\":\"http://localhost/odata/$metadata#MonstersInc/Projects(ProjectDetails())\"," +
- "\"value\":[{\"Id\":3,\"Title\":\"Midnight Snack in Kitchen Scare\",\"ProjectDetails\":[]}]}";
+ "\"value\":[{\"Id\":3,\"Title\":\"Midnight Snack in Kitchen Scare\",\"ProjectDetails\":[]}]}";
await RequestYieldsExpectedResult(client, nextLinkUri, expectedNextResult);
+
+ string expectedNestedNextResult =
+ "{\"@odata.context\":\"http://localhost/odata/$metadata#MonstersInc/Projects(1)/ProjectDetails\"," +
+ "\"value\":[" +
+ "{\"Id\":3,\"Comment\":\"Leaving the door open could let it not only a draft, but a child\"}," +
+ "{\"Id\":4,\"Comment\":\"Has led to the intrusion of a young girl, Boo\"}]}";
+ await RequestYieldsExpectedResult(client, nestedNextLinkUri, expectedNestedNextResult);
}
}
From 012d13c7f750beba3e0c1ad40c44084fba4b310d Mon Sep 17 00:00:00 2001
From: Lisi Case <49213376+lisicase@users.noreply.github.com>
Date: Wed, 5 Oct 2022 15:34:02 -0700
Subject: [PATCH 17/17] Build update
---
.../Microsoft.AspNetCore.OData.xml | 106 ++++++++----------
1 file changed, 45 insertions(+), 61 deletions(-)
diff --git a/src/Microsoft.AspNetCore.OData/Microsoft.AspNetCore.OData.xml b/src/Microsoft.AspNetCore.OData/Microsoft.AspNetCore.OData.xml
index aa91edf67..4d0e931a0 100644
--- a/src/Microsoft.AspNetCore.OData/Microsoft.AspNetCore.OData.xml
+++ b/src/Microsoft.AspNetCore.OData/Microsoft.AspNetCore.OData.xml
@@ -1094,6 +1094,20 @@
The type to test.
True if the type is a DateTime; false otherwise.
+
+
+ Determine if a type is a .
+
+ The type to test.
+ True if the type is a DateOnly; false otherwise.
+
+
+
+ Determine if a type is a .
+
+ The type to test.
+ True if the type is a TimeOnly; false otherwise.
+
Determine if a type is a TimeSpan.
@@ -1775,19 +1789,6 @@
allows and tracks changes to an delta deleted link.
- The type of the Property
- Returns true if the Property was found and false if not.
-
-
-
- Clears the .
-
-
-
-
- allows and tracks changes to an delta deleted link.
-
-
allows and tracks changes to a deleted resource.
@@ -2046,7 +2047,18 @@
Figures out if the given clr type is nonstandard edm primitive like uint, ushort, char[] etc.
- and returns the corresponding clr type to whic
+ and returns the corresponding clr type to which we map like uint => long.
+
+ The Edm model.
+ The potential non-standard CLR type.
+ A boolean value out to indicate whether the input CLR type is standard OData primitive type.
+ The standard CLR type or the input CLR type itself.
+
+
+
+ Gets the Edm type reference from the CLR type.
+
+ The Edm model.
The given CLR type.
null or the Edm type reference.
@@ -2111,17 +2123,6 @@
The test type reference.
Element type or itself.
-
-
- Converts the to .
-
- The given Edm type.
-
- Get the elementType if it's collection or return itself's type
-
- The test type reference.
- Element type or itself.
-
Converts the to .
@@ -2369,7 +2370,15 @@
The containing the navigation source.
The navigation source.
- The The to set.
+
+
+
+ Gets the to be used while generating operation links for the given action.
+
+ The containing the operation.
+ The operation for which the link builder is needed.
+ The for the given operation if one is set; otherwise, a new
that generates operation links following OData URL conventions.
@@ -2436,24 +2445,6 @@
Provides the mapping between CLR type and Edm type.
- s the navigation property resolved from this path.
-
-
-
-
- Gets the navigation property path, it doesn't include the navigation property.
-
-
-
-
- Gets the whole expand path.
-
-
-
-
- Provides the mapping between CLR type and Edm type.
-
-
Gets the corresponding Edm primitive type for a given .
@@ -2680,7 +2671,16 @@
This is how formatters create links to invoke bound actions or functions.
-
+
+
+ Create a new based on an entity link factory.
+
+ The link factory this should use when building links.
+
+ A value indicating whether the link factory generates links that follow OData conventions.
+
+
+
Create a new based on a feed link factory.
@@ -14417,19 +14417,3 @@
-ummary>
- The value segment.
-
-
-
- Gets the value segment.
-
-
-
-
-
-
-
-
-
-