From b9f64e3dd1b3e8770eec63fe26b0557e772a13cf Mon Sep 17 00:00:00 2001 From: Arnaud Boussaer Date: Fri, 7 Feb 2025 00:15:59 +0100 Subject: [PATCH 01/53] Remove not supported TFMs --- .../ODataSampleCommon.csproj | 2 +- .../Microsoft.AspNetCore.OData.csproj | 3 +- .../Microsoft.AspNetCore.OData.xml | 207 +++++++++++++++++- ...icrosoft.AspNetCore.OData.E2E.Tests.csproj | 2 +- ...crosoft.AspNetCore.OData.TestCommon.csproj | 2 +- .../Microsoft.AspNetCore.OData.Tests.csproj | 4 +- 6 files changed, 203 insertions(+), 17 deletions(-) diff --git a/sample/ODataSampleCommon/ODataSampleCommon.csproj b/sample/ODataSampleCommon/ODataSampleCommon.csproj index 3d4bc25fd..3e0a50a8d 100644 --- a/sample/ODataSampleCommon/ODataSampleCommon.csproj +++ b/sample/ODataSampleCommon/ODataSampleCommon.csproj @@ -1,7 +1,7 @@  - net5.0 + net6.0 Library true diff --git a/src/Microsoft.AspNetCore.OData/Microsoft.AspNetCore.OData.csproj b/src/Microsoft.AspNetCore.OData/Microsoft.AspNetCore.OData.csproj index b526c3f2a..5e7dbe8fa 100644 --- a/src/Microsoft.AspNetCore.OData/Microsoft.AspNetCore.OData.csproj +++ b/src/Microsoft.AspNetCore.OData/Microsoft.AspNetCore.OData.csproj @@ -1,8 +1,7 @@  - netcoreapp3.1;net5.0 - $(TargetFrameworks);net6.0 + net6.0 Microsoft.AspNetCore.OData $(OutputPath)$(AssemblyName).xml true diff --git a/src/Microsoft.AspNetCore.OData/Microsoft.AspNetCore.OData.xml b/src/Microsoft.AspNetCore.OData/Microsoft.AspNetCore.OData.xml index 390df02a6..2f3bf1d9b 100644 --- a/src/Microsoft.AspNetCore.OData/Microsoft.AspNetCore.OData.xml +++ b/src/Microsoft.AspNetCore.OData/Microsoft.AspNetCore.OData.xml @@ -1102,6 +1102,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. @@ -14974,21 +14988,194 @@ - - -ummary> - The value segment. + + + Entry class + Hier zo veel mogelijk originele referenties gebruiken + - + - Gets the value segment. + The simplified options. - - + + + Enable applying OData specific functions to query + + + The query. + + + The configuration action. + + + The edm model. + + + The query type param + + + The OData aware query. + - - + + + Apply select and expand query options. + + + The OData aware query. + + + The $select parameter text. + + + The $expand parameter text. + + + The entity set name. + + + The query type param + + + The selection result in specific format. + + + + + Apply select and expand query options to query. Warning! not all query providers support it. + + + The OData aware query. + + + The $select parameter text. + + + The $expand parameter text. + + + The entity set name. + + + The query type param + + + The selection result in specific format. + + + + + Apply $top and $skip parameters to query. + + The type param + The OData aware query. + $top parameter value + $skip parameter value + The entity set name. + The query with applied $top and $skip parameters. + + + + Apply OData query options except $select and $expand parameters. + + The type param. + The OData aware query. + The query options. + The entity set name. + The query with applied OData parameters. + + + + Apply OData query options and execute query. + + The type param. + The OData aware query. + The query options. + The entity set name. + The enumeration of query results . + + + + Apply OData query options. Warning! not all providers support it. + + The type param. + The OData aware query. + The query options. + The entity set name. + The query with special type of results . + + + + Apply $filter parameter to query. + + + The OData aware query. + + + The $filter parameter text. + + + The entity set name. + + + The query type param + + + The query with applied filter parameter. + + + Argument Null Exception + + + + + Apply $orderby parameter to query. + + + The OData aware query. + + + The $orderby parameter text. + + + The entity set name. + + + The query type param + + + The query with applied order by parameter. + + + Argument Null Exception + + + + + Ook gebruikt + + + + + + Ook gebruikt + + + + + Sets the action which will be used to initialize every instance of . + + The action which will be used to initialize every instance of . + initializer + SetInitializer + + + + Added code + + diff --git a/test/Microsoft.AspNetCore.OData.E2E.Tests/Microsoft.AspNetCore.OData.E2E.Tests.csproj b/test/Microsoft.AspNetCore.OData.E2E.Tests/Microsoft.AspNetCore.OData.E2E.Tests.csproj index b4073ca90..ea54445ad 100644 --- a/test/Microsoft.AspNetCore.OData.E2E.Tests/Microsoft.AspNetCore.OData.E2E.Tests.csproj +++ b/test/Microsoft.AspNetCore.OData.E2E.Tests/Microsoft.AspNetCore.OData.E2E.Tests.csproj @@ -1,7 +1,7 @@  - netcoreapp3.1;net6.0 + net6.0 Microsoft.AspNetCore.OData.E2E.Tests Microsoft.AspNetCore.OData.E2E.Tests diff --git a/test/Microsoft.AspNetCore.OData.TestCommon/Microsoft.AspNetCore.OData.TestCommon.csproj b/test/Microsoft.AspNetCore.OData.TestCommon/Microsoft.AspNetCore.OData.TestCommon.csproj index a6922e9bd..54252380b 100644 --- a/test/Microsoft.AspNetCore.OData.TestCommon/Microsoft.AspNetCore.OData.TestCommon.csproj +++ b/test/Microsoft.AspNetCore.OData.TestCommon/Microsoft.AspNetCore.OData.TestCommon.csproj @@ -1,7 +1,7 @@  - netcoreapp3.1;net6.0 + net6.0 Microsoft.AspNetCore.OData.TestCommon Microsoft.AspNetCore.OData.TestCommon diff --git a/test/Microsoft.AspNetCore.OData.Tests/Microsoft.AspNetCore.OData.Tests.csproj b/test/Microsoft.AspNetCore.OData.Tests/Microsoft.AspNetCore.OData.Tests.csproj index 6bafc50cc..88ad04d2d 100644 --- a/test/Microsoft.AspNetCore.OData.Tests/Microsoft.AspNetCore.OData.Tests.csproj +++ b/test/Microsoft.AspNetCore.OData.Tests/Microsoft.AspNetCore.OData.Tests.csproj @@ -1,7 +1,7 @@  - netcoreapp3.1;net6.0 + net6.0 Microsoft.AspNetCore.OData.Tests Microsoft.AspNetCore.OData.Tests @@ -42,5 +42,5 @@ - + \ No newline at end of file From 07e3e10fb9d25e7a05b7be3b181da5aa227c3949 Mon Sep 17 00:00:00 2001 From: Arnaud Boussaer Date: Fri, 7 Feb 2025 00:16:56 +0100 Subject: [PATCH 02/53] Add extension methods and unit tests from the Community.OData.Linq nuget --- .../Linq/ODataLinqExtensions.cs | 553 ++++++++++++++++++ .../Linq/ODataQuery.cs | 49 ++ .../Linq/ODataQueryOrdered.cs | 12 + .../Linq/ODataSettings.cs | 82 +++ .../Linq/SelectExpandHelper.cs | 226 +++++++ .../Linq/TopSkipHelper.cs | 82 +++ .../Linq/DateTimeFilterTests.cs | 185 ++++++ .../Linq/ExpandTests.cs | 206 +++++++ .../Linq/FilterDataContractTests.cs | 45 ++ .../Linq/FilterFunctionsTests.cs | 29 + .../Linq/FilterNavigationCollectionTests.cs | 21 + .../Linq/FilterNavigationLinkTests.cs | 29 + .../Linq/FilterTests.cs | 127 ++++ .../Linq/HashTests.cs | 49 ++ .../Linq/JsonTests.cs | 28 + .../Linq/ODataTests.cs | 45 ++ .../Linq/OpenTypesTests.cs | 124 ++++ .../Linq/OrderByTests.cs | 59 ++ .../Linq/SampleData/ClassWithCollection.cs | 25 + .../SampleData/ClassWithDeepNavigation.cs | 28 + .../Linq/SampleData/ClassWithLink.cs | 34 ++ .../Linq/SampleData/OpenType.cs | 48 ++ .../Linq/SampleData/SampleWithCustomKey.cs | 66 +++ .../Linq/SampleData/SampleWithoutKey.cs | 23 + .../Linq/SampleData/SimpleClass.cs | 67 +++ .../SampleData/SimpleClassDataContract.cs | 31 + .../Linq/SampleData/TestEnum.cs | 11 + .../Linq/SampleData/TestItem.cs | 15 + .../Linq/SelectTests.cs | 128 ++++ .../Linq/TopSkipTests.cs | 70 +++ 30 files changed, 2497 insertions(+) create mode 100644 src/Microsoft.AspNetCore.OData/Linq/ODataLinqExtensions.cs create mode 100644 src/Microsoft.AspNetCore.OData/Linq/ODataQuery.cs create mode 100644 src/Microsoft.AspNetCore.OData/Linq/ODataQueryOrdered.cs create mode 100644 src/Microsoft.AspNetCore.OData/Linq/ODataSettings.cs create mode 100644 src/Microsoft.AspNetCore.OData/Linq/SelectExpandHelper.cs create mode 100644 src/Microsoft.AspNetCore.OData/Linq/TopSkipHelper.cs create mode 100644 test/Microsoft.AspNetCore.OData.Tests/Linq/DateTimeFilterTests.cs create mode 100644 test/Microsoft.AspNetCore.OData.Tests/Linq/ExpandTests.cs create mode 100644 test/Microsoft.AspNetCore.OData.Tests/Linq/FilterDataContractTests.cs create mode 100644 test/Microsoft.AspNetCore.OData.Tests/Linq/FilterFunctionsTests.cs create mode 100644 test/Microsoft.AspNetCore.OData.Tests/Linq/FilterNavigationCollectionTests.cs create mode 100644 test/Microsoft.AspNetCore.OData.Tests/Linq/FilterNavigationLinkTests.cs create mode 100644 test/Microsoft.AspNetCore.OData.Tests/Linq/FilterTests.cs create mode 100644 test/Microsoft.AspNetCore.OData.Tests/Linq/HashTests.cs create mode 100644 test/Microsoft.AspNetCore.OData.Tests/Linq/JsonTests.cs create mode 100644 test/Microsoft.AspNetCore.OData.Tests/Linq/ODataTests.cs create mode 100644 test/Microsoft.AspNetCore.OData.Tests/Linq/OpenTypesTests.cs create mode 100644 test/Microsoft.AspNetCore.OData.Tests/Linq/OrderByTests.cs create mode 100644 test/Microsoft.AspNetCore.OData.Tests/Linq/SampleData/ClassWithCollection.cs create mode 100644 test/Microsoft.AspNetCore.OData.Tests/Linq/SampleData/ClassWithDeepNavigation.cs create mode 100644 test/Microsoft.AspNetCore.OData.Tests/Linq/SampleData/ClassWithLink.cs create mode 100644 test/Microsoft.AspNetCore.OData.Tests/Linq/SampleData/OpenType.cs create mode 100644 test/Microsoft.AspNetCore.OData.Tests/Linq/SampleData/SampleWithCustomKey.cs create mode 100644 test/Microsoft.AspNetCore.OData.Tests/Linq/SampleData/SampleWithoutKey.cs create mode 100644 test/Microsoft.AspNetCore.OData.Tests/Linq/SampleData/SimpleClass.cs create mode 100644 test/Microsoft.AspNetCore.OData.Tests/Linq/SampleData/SimpleClassDataContract.cs create mode 100644 test/Microsoft.AspNetCore.OData.Tests/Linq/SampleData/TestEnum.cs create mode 100644 test/Microsoft.AspNetCore.OData.Tests/Linq/SampleData/TestItem.cs create mode 100644 test/Microsoft.AspNetCore.OData.Tests/Linq/SelectTests.cs create mode 100644 test/Microsoft.AspNetCore.OData.Tests/Linq/TopSkipTests.cs diff --git a/src/Microsoft.AspNetCore.OData/Linq/ODataLinqExtensions.cs b/src/Microsoft.AspNetCore.OData/Linq/ODataLinqExtensions.cs new file mode 100644 index 000000000..d1b7730f2 --- /dev/null +++ b/src/Microsoft.AspNetCore.OData/Linq/ODataLinqExtensions.cs @@ -0,0 +1,553 @@ +namespace Community.OData.Linq +{ + using Community.OData.Linq.OData; + using Community.OData.Linq.OData.Query; + using Microsoft.AspNetCore.OData.Query; + using Microsoft.AspNetCore.OData.Query.Expressions; + using Microsoft.AspNetCore.OData.Query.Validator; + using Microsoft.AspNetCore.OData.Query.Wrapper; + //using Community.OData.Linq.Builder; + //using Community.OData.Linq.Builder.Validators; + //using Community.OData.Linq.Common; + //using Community.OData.Linq.OData; + //using Community.OData.Linq.OData.Query; + //using Community.OData.Linq.OData.Query.Expressions; + //using Community.OData.Linq.Properties; + + using Microsoft.Extensions.DependencyInjection; + using Microsoft.OData; + using Microsoft.OData.Edm; + using Microsoft.OData.ModelBuilder; + using Microsoft.OData.ModelBuilder.Config; + using Microsoft.OData.UriParser; + using System; + using System.Collections.Concurrent; + using System.Collections.Generic; + using System.ComponentModel.Design; + using System.Diagnostics.Contracts; + using System.Linq; + + /// + /// Entry class + /// Hier zo veel mogelijk originele referenties gebruiken + /// + public static class ODataLinqExtensions + { + /// + /// The simplified options. + /// + private static readonly ODataSimplifiedOptions SimplifiedOptions = new ODataSimplifiedOptions(); + + private static readonly ConcurrentDictionary Models = new ConcurrentDictionary(); + + private static readonly ConcurrentDictionary Containers = new ConcurrentDictionary(); + + /// + /// Enable applying OData specific functions to query + /// + /// + /// The query. + /// + /// + /// The configuration action. + /// + /// + /// The edm model. + /// + /// + /// The query type param + /// + /// + /// The OData aware query. + /// + public static ODataQuery OData(this IQueryable query, Action configuration = null, IEdmModel edmModel = null) + { + if (query == null) throw new ArgumentNullException(nameof(query)); + + ODataSettings settings = new ODataSettings(); + configuration?.Invoke(settings); + + if (edmModel == null) + { + edmModel = Models.GetOrAdd(typeof(T), t => + { + ODataConventionModelBuilder builder = new ODataConventionModelBuilder(); + //builder.ODataSettings = settings; + builder.AddEntityType(t); + builder.AddEntitySet(t.Name, new EntityTypeConfiguration(builder, t)); + return builder.GetEdmModel(); + }); + } + else + { + if (edmModel.SchemaElements.Count(e => e.SchemaElementKind == EdmSchemaElementKind.EntityContainer) == 0) + { + throw new ArgumentException("Provided Entity Model have no IEdmEntityContainer", nameof(edmModel)); + } + } + + int settingsHash = HashCode.Combine( + settings.QuerySettings, + settings.DefaultQuerySettings, + settings.ParserSettings.MaximumExpansionCount, + settings.ParserSettings.MaximumExpansionDepth, + settings.AllowRecursiveLoopOfComplexTypes); + + ServiceContainer baseContainer = Containers.GetOrAdd(settingsHash, i => + { + ServiceContainer c = new ServiceContainer(); + + c.AddService(typeof(ODataQuerySettings), settings.QuerySettings); + c.AddService(typeof(DefaultQuerySettings), settings.DefaultQuerySettings); + c.AddService(typeof(SelectExpandQueryValidator), new SelectExpandQueryValidator());// settings.DefaultQuerySettings)); + c.AddService(typeof(ODataSimplifiedOptions), SimplifiedOptions); + c.AddService(typeof(ODataUriParserSettings), settings.ParserSettings); + + return c; + }); + + ServiceContainer container = new ServiceContainer(baseContainer); + container.AddService(typeof(IEdmModel), edmModel); + container.AddService(typeof(FilterBinder), new FilterBinder());// container)); + container.AddService(typeof(ODataUriResolver), settings.Resolver ?? ODataSettings.DefaultResolver); + container.AddService(typeof(ODataSettings), settings); + + container.AddService(typeof(DefaultQueryConfigurations), settings.DefaultQueryConfigurations); + + ODataQuery dataQuery = new ODataQuery(query, container); + + return dataQuery; + } + + /// + /// Apply select and expand query options. + /// + /// + /// The OData aware query. + /// + /// + /// The $select parameter text. + /// + /// + /// The $expand parameter text. + /// + /// + /// The entity set name. + /// + /// + /// The query type param + /// + /// + /// The selection result in specific format. + /// + public static IEnumerable SelectExpand( + this ODataQuery query, + string selectText = null, + string expandText = null, + string entitySetName = null) + { + var result = SelectExpandInternal(query, selectText, expandText, entitySetName); + + return Enumerate(result); + } + + /// + /// Apply select and expand query options to query. Warning! not all query providers support it. + /// + /// + /// The OData aware query. + /// + /// + /// The $select parameter text. + /// + /// + /// The $expand parameter text. + /// + /// + /// The entity set name. + /// + /// + /// The query type param + /// + /// + /// The selection result in specific format. + /// + public static IQueryable SelectExpandAsQueryable( + this ODataQuery query, + string selectText = null, + string expandText = null, + string entitySetName = null) + { + IQueryable result = SelectExpandInternal(query, selectText, expandText, entitySetName); + return query.Provider.CreateQuery(result.Expression); + } + + private static IQueryable SelectExpandInternal(ODataQuery query, string selectText, string expandText, + string entitySetName) + { + SelectExpandHelper helper = new SelectExpandHelper( + new ODataRawQueryOptions { Select = selectText, Expand = expandText }, + query, + entitySetName); + + helper.AddAutoSelectExpandProperties(); + + var result = helper.Apply(query); + + // In case of SelectExpand ,method was called to convert to ISelectExpandWrapper without actually applying $select and $expand params + if (result == query && selectText == null && expandText == null) + { + return SelectExpandInternal(query, "*", expandText, entitySetName); + } + + return result; + } + + /// + /// Apply $top and $skip parameters to query. + /// + /// The type param + /// The OData aware query. + /// $top parameter value + /// $skip parameter value + /// The entity set name. + /// The query with applied $top and $skip parameters. + public static ODataQuery TopSkip(this ODataQuery query, string topText = null, string skipText = null, string entitySetName = null) + { + if (query == null) throw new ArgumentNullException(nameof(query)); + + ODataSettings settings = query.ServiceProvider.GetRequiredService(); + + Dictionary dictionary = new Dictionary(); + + if (topText != null) + { + dictionary.Add("$top", topText); + } + + if (skipText != null) + { + dictionary.Add("$skip", skipText); + } + + ODataQueryOptionParser queryOptionParser = GetParser( + query, + entitySetName, + dictionary); + + long? skip = queryOptionParser.ParseSkip(); + long? top = queryOptionParser.ParseTop(); + + if (skip.HasValue || top.HasValue || settings.QuerySettings.PageSize.HasValue) + { + IQueryable result = TopSkipHelper.ApplySkipWithValidation(query, skip, settings); + if (top.HasValue) + { + result = TopSkipHelper.ApplyTopWithValidation(result, top, settings); + } + else + { + result = TopSkipHelper.ApplyTopWithValidation(result, settings.QuerySettings.PageSize, settings); + } + + return new ODataQuery(result, query.ServiceProvider); + } + + return query; + } + + /// + /// Apply OData query options except $select and $expand parameters. + /// + /// The type param. + /// The OData aware query. + /// The query options. + /// The entity set name. + /// The query with applied OData parameters. + public static IQueryable ApplyQueryOptionsWithoutSelectExpand( + this ODataQuery query, + ODataRawQueryOptions rawQueryOptions, + string entitySetName = null) + { + return ApplyQueryOptionsInternal(query, rawQueryOptions, entitySetName); + } + + /// + /// Apply OData query options and execute query. + /// + /// The type param. + /// The OData aware query. + /// The query options. + /// The entity set name. + /// The enumeration of query results . + public static IEnumerable ApplyQueryOptions( + this ODataQuery query, + ODataRawQueryOptions rawQueryOptions, + string entitySetName = null) + { + return ApplyQueryOptionsInternal(query, rawQueryOptions, entitySetName).SelectExpand( + rawQueryOptions.Select, + rawQueryOptions.Expand, + entitySetName); + } + + /// + /// Apply OData query options. Warning! not all providers support it. + /// + /// The type param. + /// The OData aware query. + /// The query options. + /// The entity set name. + /// The query with special type of results . + public static IQueryable ApplyQueryOptionsAsQueryable( + this ODataQuery query, + ODataRawQueryOptions rawQueryOptions, + string entitySetName = null) + { + return ApplyQueryOptionsInternal(query, rawQueryOptions, entitySetName).SelectExpandAsQueryable( + rawQueryOptions.Select, + rawQueryOptions.Expand, + entitySetName); + } + + private static ODataQuery ApplyQueryOptionsInternal(ODataQuery query, ODataRawQueryOptions rawQueryOptions, string entitySetName) + { + if (query == null) throw new ArgumentNullException(nameof(query)); + if (rawQueryOptions == null) throw new ArgumentNullException(nameof(rawQueryOptions)); + + if (rawQueryOptions.Filter != null) + { + //foreach (string filter in rawQueryOptions.Filters) + //{ + query = query.Filter(rawQueryOptions.Filter, entitySetName); + //} + } + + if (rawQueryOptions.OrderBy != null) + { + query = query.OrderBy(rawQueryOptions.OrderBy, entitySetName); + } + + query = query.TopSkip(rawQueryOptions.Top, rawQueryOptions.Skip); + + return query; + } + + /// + /// Apply $filter parameter to query. + /// + /// + /// The OData aware query. + /// + /// + /// The $filter parameter text. + /// + /// + /// The entity set name. + /// + /// + /// The query type param + /// + /// + /// The query with applied filter parameter. + /// + /// + /// Argument Null Exception + /// + public static ODataQuery Filter(this ODataQuery query, string filterText, string entitySetName = null) + { + if (query == null) throw new ArgumentNullException(nameof(query)); + if (filterText == null) throw new ArgumentNullException(nameof(filterText)); + + IEdmModel edmModel = query.EdmModel; + + ODataQueryOptionParser queryOptionParser = GetParser( + query, + entitySetName, + new Dictionary { { "$filter", filterText } }); + + ODataSettings settings = query.ServiceProvider.GetRequiredService(); + + FilterClause filterClause = queryOptionParser.ParseFilter(); + SingleValueNode filterExpression = filterClause.Expression.Accept( + new ParameterAliasNodeTranslator(queryOptionParser.ParameterAliasNodes)) as SingleValueNode; + filterExpression = filterExpression ?? new ConstantNode(null); + filterClause = new FilterClause(filterExpression, filterClause.RangeVariable); + Contract.Assert(filterClause != null); + + //OLD + //var validator = new FilterQueryValidator(settings.DefaultQuerySettings); + //validator.Validate(filterClause, settings.ValidationSettings, edmModel); + + //Expression filter = FilterBinder.Bind(query, filterClause, typeof(T), query.ServiceProvider); + //var result = ExpressionHelpers.Where(query, filter, typeof(T)); + // + + //NEW + var queryContext = new ODataQueryContext(edmModel, query.ElementType) + { + RequestContainer = query.ServiceProvider + }; + var validator2 = new FilterQueryValidator(); + var option = new FilterQueryOption(queryContext, filterClause); + validator2.Validate(option, settings.ValidationSettings); + + //var binder = query.ServiceProvider.GetRequiredService(); + //QueryBinderContext context = new QueryBinderContext(edmModel, query.ServiceProvider.GetRequiredService(), query.ElementType) + //{ + // AssembliesResolver = AssemblyResolverHelper.Default, + // //Source = query.Expression + //}; + //var filter2 = binder.BindFilter(filterClause, context); + //var result2 = ExpressionHelpers.Where(query, filter2, typeof(T)); + + var result3 = option.ApplyTo(query, query.ServiceProvider.GetRequiredService()); + + return new ODataQuery(result3, query.ServiceProvider); + } + + /// + /// Apply $orderby parameter to query. + /// + /// + /// The OData aware query. + /// + /// + /// The $orderby parameter text. + /// + /// + /// The entity set name. + /// + /// + /// The query type param + /// + /// + /// The query with applied order by parameter. + /// + /// + /// Argument Null Exception + /// + public static ODataQueryOrdered OrderBy(this ODataQuery query, string orderbyText, string entitySetName = null) + { + if (query == null) throw new ArgumentNullException(nameof(query)); + if (orderbyText == null) throw new ArgumentNullException(nameof(orderbyText)); + + IEdmModel edmModel = query.EdmModel; + + ODataQueryOptionParser queryOptionParser = GetParser( + query, + entitySetName, + new Dictionary { { "$orderby", orderbyText } }); + + ODataSettings settings = query.ServiceProvider.GetRequiredService(); + + var orderByClause = queryOptionParser.ParseOrderBy(); + + orderByClause = TranslateParameterAlias(orderByClause, queryOptionParser); + + ICollection nodes = OrderByNode.CreateCollection(orderByClause); + + //OLD + //var validator = new OrderByQueryValidator(settings.DefaultQuerySettings); + //validator.Validate(nodes, settings.ValidationSettings, edmModel); + //IOrderedQueryable result = (IOrderedQueryable)OrderByBinder.OrderApplyToCore(query, settings.QuerySettings, nodes, edmModel); + + //NEW + var queryContext = new ODataQueryContext(edmModel, query.ElementType) + { + RequestContainer = query.ServiceProvider + }; + var validator2 = new OrderByQueryValidator(); + var option = new OrderByQueryOption(orderbyText, queryContext, queryOptionParser); + validator2.Validate(option, settings.ValidationSettings); + + //var binder = new OrderByBinder(); + //QueryBinderContext context = new QueryBinderContext(edmModel, query.ServiceProvider.GetRequiredService(), typeof(T)) + //{ + // AssembliesResolver = AssemblyResolverHelper.Default, + // Source = query.Expression + //}; + //var orderbyResult = binder.BindOrderBy(option.OrderByClause, context); + var result2 = option.ApplyTo(query, query.ServiceProvider.GetRequiredService()); + + //return result2; + + return new ODataQueryOrdered(result2, query.ServiceProvider); + } + + private static IEnumerable Enumerate(IQueryable queryable) where T : class + { + var enumerator = queryable.GetEnumerator(); + + while (enumerator.MoveNext()) + { + yield return enumerator.Current as T; + } + } + + private static OrderByClause TranslateParameterAlias(OrderByClause orderBy, ODataQueryOptionParser queryOptionParser) + { + if (orderBy == null) + { + return null; + } + + SingleValueNode orderByExpression = orderBy.Expression.Accept( + new ParameterAliasNodeTranslator(queryOptionParser.ParameterAliasNodes)) as SingleValueNode; + orderByExpression = orderByExpression ?? new ConstantNode(null, "null"); + + return new OrderByClause( + TranslateParameterAlias(orderBy.ThenBy, queryOptionParser), + orderByExpression, + orderBy.Direction, + orderBy.RangeVariable); + } + + public static ODataQueryOptionParser GetParser(ODataQuery query, string entitySetName, IDictionary raws) + { + IEdmModel edmModel = query.EdmModel; + + if (entitySetName == null) + { + entitySetName = typeof(T).Name; + } + + IEdmEntityContainer[] containers = + edmModel.SchemaElements.Where( + e => e.SchemaElementKind == EdmSchemaElementKind.EntityContainer && + (e as IEdmEntityContainer).FindEntitySet(entitySetName) != null) + .OfType() + .ToArray(); + + if (containers.Length == 0) + { + throw new ArgumentException($"Unable to find {entitySetName} entity set in the model.", + nameof(entitySetName)); + } + + if (containers.Length > 1) + { + throw new ArgumentException($"Entity Set {entitySetName} found more that 1 time", + nameof(entitySetName)); + } + + IEdmEntitySet entitySet = containers.Single().FindEntitySet(entitySetName); + + if (entitySet == null) + { + + } + + ODataPath path = new ODataPath(new EntitySetSegment(entitySet)); + + ODataQueryOptionParser parser = new ODataQueryOptionParser(edmModel, path, raws, query.ServiceProvider); + + ODataSettings settings = query.ServiceProvider.GetRequiredService(); + + // Workaround for strange behavior in QueryOptionsParserConfiguration constructor which set it to false always + parser.Resolver.EnableCaseInsensitive = settings.EnableCaseInsensitive; + + return parser; + } + } +} diff --git a/src/Microsoft.AspNetCore.OData/Linq/ODataQuery.cs b/src/Microsoft.AspNetCore.OData/Linq/ODataQuery.cs new file mode 100644 index 000000000..46d393004 --- /dev/null +++ b/src/Microsoft.AspNetCore.OData/Linq/ODataQuery.cs @@ -0,0 +1,49 @@ +namespace Community.OData.Linq +{ + using Microsoft.Extensions.DependencyInjection; + using Microsoft.OData.Edm; + using System; + using System.Collections; + using System.Collections.Generic; + using System.Linq; + using System.Linq.Expressions; + + /// + /// Ook gebruikt + /// + /// + public class ODataQuery : IQueryable + { + internal ODataQuery(IQueryable inner, IServiceProvider serviceProvider) + { + this.Inner = inner ?? throw new ArgumentNullException(nameof(inner)); + this.ServiceProvider = serviceProvider; + } + + public IEdmModel EdmModel => this.ServiceProvider.GetRequiredService(); + + public Type ElementType => this.Inner.ElementType; + + public Expression Expression => this.Inner.Expression; + + public IQueryProvider Provider => this.Inner.Provider; + + public IQueryable Inner { get; } + public IServiceProvider ServiceProvider { get; } + + public IEnumerator GetEnumerator() + { + return this.Inner.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return (IEnumerator)this.Inner.GetEnumerator(); + } + + public IQueryable ToOriginalQuery() + { + return (IQueryable)this.Inner; + } + } +} diff --git a/src/Microsoft.AspNetCore.OData/Linq/ODataQueryOrdered.cs b/src/Microsoft.AspNetCore.OData/Linq/ODataQueryOrdered.cs new file mode 100644 index 000000000..d0f8e88dc --- /dev/null +++ b/src/Microsoft.AspNetCore.OData/Linq/ODataQueryOrdered.cs @@ -0,0 +1,12 @@ +namespace Community.OData.Linq +{ + using System; + using System.Linq; + + public class ODataQueryOrdered : ODataQuery, IOrderedQueryable + { + internal ODataQueryOrdered(IOrderedQueryable inner, IServiceProvider serviceProvider):base(inner,serviceProvider) + { + } + } +} diff --git a/src/Microsoft.AspNetCore.OData/Linq/ODataSettings.cs b/src/Microsoft.AspNetCore.OData/Linq/ODataSettings.cs new file mode 100644 index 000000000..fa082d4ca --- /dev/null +++ b/src/Microsoft.AspNetCore.OData/Linq/ODataSettings.cs @@ -0,0 +1,82 @@ +namespace Community.OData.Linq +{ + using Microsoft.AspNetCore.OData.Query; + using Microsoft.AspNetCore.OData.Query.Validator; + using Microsoft.OData.ModelBuilder.Config; + //using Community.OData.Linq.OData.Query; + using Microsoft.OData.UriParser; + using System; + + /// + /// Ook gebruikt + /// + public class ODataSettings + { + private static readonly object SyncObj = new object(); + + private static Action Initializer = null; + + /// + /// Sets the action which will be used to initialize every instance of . + /// + /// The action which will be used to initialize every instance of . + /// initializer + /// SetInitializer + public static void SetInitializer(Action initializer) + { + if (initializer == null) throw new ArgumentNullException(nameof(initializer)); + + if (Initializer == null) + { + lock (SyncObj) + { + if (Initializer == null) + { + Initializer = initializer; + return; + } + } + } + + throw new InvalidOperationException($"{nameof(SetInitializer)} method can be invoked only once"); + } + + internal static ODataUriResolver DefaultResolver = new StringAsEnumResolver { EnableCaseInsensitive = true }; + + public ODataQuerySettings QuerySettings { get; } = new ODataQuerySettings { PageSize = 20 }; + + public ODataValidationSettings ValidationSettings { get; } = new ODataValidationSettings(); + + public ODataUriParserSettings ParserSettings { get; } = new ODataUriParserSettings(); + + public ODataUriResolver Resolver { get; set; } = DefaultResolver; + + public bool EnableCaseInsensitive { get; set; } = true; + + [Obsolete("Not supported anymore")] + public bool AllowRecursiveLoopOfComplexTypes { get; set; } = false; + + public DefaultQuerySettings DefaultQuerySettings { get; } = + new DefaultQuerySettings + { + EnableFilter = true, + EnableOrderBy = true, + EnableExpand = true, + EnableSelect = true, + MaxTop = 100 + }; + public DefaultQueryConfigurations DefaultQueryConfigurations { get; } = new DefaultQueryConfigurations + { + EnableFilter = true, + EnableOrderBy = true, + EnableExpand = true, + EnableSelect = true, + MaxTop = 100 + }; + + public ODataSettings() + { + Initializer?.Invoke(this); + } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.OData/Linq/SelectExpandHelper.cs b/src/Microsoft.AspNetCore.OData/Linq/SelectExpandHelper.cs new file mode 100644 index 000000000..b0f8544d6 --- /dev/null +++ b/src/Microsoft.AspNetCore.OData/Linq/SelectExpandHelper.cs @@ -0,0 +1,226 @@ +namespace Community.OData.Linq.OData +{ + using Microsoft.AspNetCore.OData.Edm; + using Microsoft.AspNetCore.OData.Query; + //using Community.OData.Linq.OData.Formatter; + //using Community.OData.Linq.OData.Query; + + using Microsoft.Extensions.DependencyInjection; + using Microsoft.OData.Edm; + using Microsoft.OData.UriParser; + using System; + using System.Collections.Generic; + using System.Globalization; + using System.Linq; + + /// + /// Added code + /// + /// + public class SelectExpandHelper + { + private readonly ODataQuery query; + + private readonly string entitySetName; + + private ODataRawQueryOptions RawValues { get; } + private ODataQueryContext Context { get; } + private SelectExpandQueryOption SelectExpand { get; set; } + + public SelectExpandHelper(ODataRawQueryOptions rawQueryOptions, ODataQuery query, string entitySetName) + { + this.Context = new ODataQueryContext(query.EdmModel, query.ElementType, null); + this.Context.RequestContainer = query.ServiceProvider; + this.query = query; + this.entitySetName = entitySetName; + this.RawValues = rawQueryOptions ?? throw new ArgumentNullException(nameof(rawQueryOptions)); + if (this.RawValues.Select != null || this.RawValues.Expand != null) + { + Dictionary raws = new Dictionary(); + if (this.RawValues.Select != null) + { + raws["$select"] = this.RawValues.Select; + } + + if (this.RawValues.Expand != null) + { + raws["$expand"] = this.RawValues.Expand; + } + + ODataQueryOptionParser parser = ODataLinqExtensions.GetParser(this.query, this.entitySetName, raws); + + this.SelectExpand = new SelectExpandQueryOption( + this.RawValues.Select, + this.RawValues.Expand, + this.Context, + parser); + } + } + public void AddAutoSelectExpandProperties() + { + bool containsAutoSelectExpandProperties = false; + var autoExpandRawValue = GetAutoExpandRawValue(); + var autoSelectRawValue = GetAutoSelectRawValue(); + + IDictionary queryParameters = new Dictionary(); + + if (!string.IsNullOrEmpty(autoExpandRawValue) && !autoExpandRawValue.Equals(RawValues.Expand)) + { + queryParameters["$expand"] = autoExpandRawValue; + containsAutoSelectExpandProperties = true; + } + else + { + autoExpandRawValue = RawValues.Expand; + } + + if (!string.IsNullOrEmpty(autoSelectRawValue) && !autoSelectRawValue.Equals(RawValues.Select)) + { + queryParameters["$select"] = autoSelectRawValue; + containsAutoSelectExpandProperties = true; + } + else + { + autoSelectRawValue = RawValues.Select; + + if (autoSelectRawValue != null) + { + queryParameters["$select"] = autoSelectRawValue; + } + } + + if (containsAutoSelectExpandProperties) + { + ODataQueryOptionParser parser = ODataLinqExtensions.GetParser( + this.query, + this.entitySetName, + queryParameters); + var originalSelectExpand = this.SelectExpand; + this.SelectExpand = new SelectExpandQueryOption( + autoSelectRawValue, + autoExpandRawValue, + this.Context, + parser); + if (originalSelectExpand != null && originalSelectExpand.LevelsMaxLiteralExpansionDepth > 0) + { + this.SelectExpand.LevelsMaxLiteralExpansionDepth = + originalSelectExpand.LevelsMaxLiteralExpansionDepth; + } + } + } + + public IQueryable Apply(ODataQuery query) + { + IQueryable result = query; + if (this.SelectExpand != null) + { + var tempResult = this.ApplySelectExpand( + result, + (ODataQuerySettings)query.ServiceProvider.GetService(typeof(ODataQuerySettings))); + if (tempResult != default(IQueryable)) + { + result = tempResult; + } + } + + return result; + } + + private TSelect ApplySelectExpand(TSelect entity, ODataQuerySettings settings) + { + var result = default(TSelect); + + SelectExpandClause processedClause = this.SelectExpand.ProcessLevels(); + SelectExpandQueryOption newSelectExpand = new SelectExpandQueryOption( + this.SelectExpand.RawSelect, + this.SelectExpand.RawExpand, + this.SelectExpand.Context, + processedClause); + + ODataSettings qsettings = this.Context.RequestContainer.GetRequiredService(); + + newSelectExpand.Validate(qsettings.ValidationSettings); + + var type = typeof(TSelect); + if (type == typeof(IQueryable)) + { + result = (TSelect)newSelectExpand.ApplyTo((IQueryable)entity, settings); + } + else if (type == typeof(object)) + { + result = (TSelect)newSelectExpand.ApplyTo(entity, settings); + } + + + return result; + } + + private string GetAutoSelectRawValue() + { + var selectRawValue = RawValues.Select; + IEdmEntityType baseEntityType = Context.TargetStructuredType as IEdmEntityType; + if (string.IsNullOrEmpty(selectRawValue)) + { + IList autoSelectProperties = null; + + if (Context.TargetStructuredType != null && Context.TargetProperty != null) + { + autoSelectProperties = Context.Model.GetAutoSelectPaths(Context.TargetStructuredType, Context.TargetProperty); + } + else if (Context.ElementType is IEdmStructuredType structuredType) + { + autoSelectProperties = Context.Model.GetAutoSelectPaths(structuredType, null); + } + + string autoSelectRawValue = autoSelectProperties == null ? null : string.Join(",", autoSelectProperties.Select(a => a.SelectPath)); + + if (!string.IsNullOrEmpty(autoSelectRawValue)) + { + if (!string.IsNullOrEmpty(selectRawValue)) + { + selectRawValue = string.Format(CultureInfo.InvariantCulture, "{0},{1}", + autoSelectRawValue, selectRawValue); + } + else + { + selectRawValue = autoSelectRawValue; + } + } + } + + return selectRawValue; + } + + private string GetAutoExpandRawValue() + { + var expandRawValue = RawValues.Expand; + + IList autoExpandNavigationProperties = null; + if (Context.TargetStructuredType != null && Context.TargetProperty != null) + { + autoExpandNavigationProperties = Context.Model.GetAutoExpandPaths(Context.TargetStructuredType, Context.TargetProperty, !string.IsNullOrEmpty(RawValues.Select)); + } + else if (Context.ElementType is IEdmStructuredType elementType) + { + autoExpandNavigationProperties = Context.Model.GetAutoExpandPaths(elementType, null, !string.IsNullOrEmpty(RawValues.Select)); + } + + string autoExpandRawValue = autoExpandNavigationProperties == null ? null : string.Join(",", autoExpandNavigationProperties.Select(c => c.ExpandPath)); + + + if (!string.IsNullOrEmpty(autoExpandRawValue)) + { + if (!string.IsNullOrEmpty(expandRawValue)) + { + expandRawValue = string.Format(CultureInfo.InvariantCulture, "{0},{1}", + autoExpandRawValue, expandRawValue); + } + else + { + expandRawValue = autoExpandRawValue; + } + } + return expandRawValue; + } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.OData/Linq/TopSkipHelper.cs b/src/Microsoft.AspNetCore.OData/Linq/TopSkipHelper.cs new file mode 100644 index 000000000..fa48d6af5 --- /dev/null +++ b/src/Microsoft.AspNetCore.OData/Linq/TopSkipHelper.cs @@ -0,0 +1,82 @@ +namespace Community.OData.Linq.OData.Query +{ + using Microsoft.AspNetCore.OData; + using Microsoft.AspNetCore.OData.Query; + using Microsoft.OData; + using System.Linq; + + public class TopSkipHelper + { + public static IQueryable ApplyTopWithValidation(IQueryable query, long? top, ODataSettings settings) + { + if (top.HasValue) + { + if (top.Value > int.MaxValue) + { + throw new ODataException( + Error.Format( + SRResources.SkipTopLimitExceeded, + int.MaxValue, + AllowedQueryOptions.Top, + top.Value)); + } + + if (top.Value > settings.ValidationSettings.MaxTop) + { + throw new ODataException( + Error.Format( + SRResources.SkipTopLimitExceeded, + settings.ValidationSettings.MaxTop, + AllowedQueryOptions.Top, + top.Value)); + } + + IQueryable result = ExpressionHelpers.Take( + query, + (int)top.Value, + typeof(T), + settings.QuerySettings.EnableConstantParameterization) as IQueryable; + + return result; + } + + return query; + } + + public static IQueryable ApplySkipWithValidation(IQueryable query, long? skip, ODataSettings settings) + { + if (skip.HasValue) + { + if (skip.Value > int.MaxValue) + { + throw new ODataException( + Error.Format( + SRResources.SkipTopLimitExceeded, + int.MaxValue, + AllowedQueryOptions.Skip, + skip.Value)); + } + + if (skip.Value > settings.ValidationSettings.MaxSkip) + { + throw new ODataException( + Error.Format( + SRResources.SkipTopLimitExceeded, + settings.ValidationSettings.MaxSkip, + AllowedQueryOptions.Skip, + skip.Value)); + } + + IQueryable result = ExpressionHelpers.Skip( + query, + (int)skip.Value, + typeof(T), + settings.QuerySettings.EnableConstantParameterization) as IQueryable; + + return result; + } + + return query; + } + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNetCore.OData.Tests/Linq/DateTimeFilterTests.cs b/test/Microsoft.AspNetCore.OData.Tests/Linq/DateTimeFilterTests.cs new file mode 100644 index 000000000..29a67b2b5 --- /dev/null +++ b/test/Microsoft.AspNetCore.OData.Tests/Linq/DateTimeFilterTests.cs @@ -0,0 +1,185 @@ +using Community.OData.Linq.xTests.SampleData; + +using System; +using System.Linq; + +using Xunit; +using Xunit.Abstractions; + +namespace Community.OData.Linq.xTests +{ + public class DateTimeFilterTests + { + private readonly ITestOutputHelper output; + private static readonly DateTime dtUtc = new DateTime(2018, 1, 26, 0, 0, 0, DateTimeKind.Utc); + private static readonly DateTime dtLocal = new DateTime(2018, 1, 26, 0, 0, 0, DateTimeKind.Local); + private static readonly DateTime dt = new DateTime(2018, 1, 26, 0, 0, 0); + private static readonly DateTimeOffset dto = new DateTimeOffset(dt, TimeZoneInfo.Local.BaseUtcOffset); + private static readonly DateOnly do1 = new(2018, 1, 26); + //private readonly DateTimeOffset dtoUtc = new DateTimeOffset(new DateTime(2018, 1, 26).ToUniversalTime()); + + public DateTimeFilterTests(ITestOutputHelper output) + { + this.output = output ?? throw new ArgumentNullException(nameof(output)); + } + + [Fact] + public void LocalDateTimeEqualCorrectUtcOffset() + { + string value = dto.ToString("u").Replace(" ", "T"); + string filter = $"DateTime eq {value}"; + output.WriteLine(filter); + + var result = SimpleClass.CreateQuery().OData().Filter(filter).ToArray(); + Assert.Single(result); + output.WriteLine(result.Single().DateTime.ToString()); + } + + [Fact] + public void LocalDateTimeEqualCorrectLocalOffset() + { + string value = dto.ToString("s") + dto.ToString("zzz"); + string filter = $"DateTime eq {value}"; + output.WriteLine(filter); + + var result = SimpleClass.CreateQuery().OData().Filter(filter).ToArray(); + Assert.Single(result); + output.WriteLine(result.Single().DateTime.ToString()); + } + + [Fact] + public void LocalDateTimetNotEquaIncorrectUtcOffset() + { + string value = dto.ToString("s") + "Z"; + string filter = $"DateTime eq {value}"; + output.WriteLine(filter); + + var result = SimpleClass.CreateQuery().OData().Filter(filter).ToArray(); + Assert.Empty(result); + } + + [Fact] + public void DateTimeOffsetEqualCorrectUtcOffset() + { + string value = dto.ToString("u").Replace(" ", "T"); + string filter = $"DateTimeOffset eq {value}"; + output.WriteLine(filter); + + var result = SimpleClass.CreateQuery().OData().Filter(filter).ToArray(); + Assert.Single(result); + output.WriteLine(result.Single().DateTimeOffset.ToString()); + + } + + [Fact] + public void DateTimeOffsetEqualCorrectLocalOffset() + { + string value = dto.ToString("s") + dto.ToString("zzz"); + string filter = $"DateTimeOffset eq {value}"; + output.WriteLine(filter); + + var result = SimpleClass.CreateQuery().OData().Filter(filter).ToArray(); + Assert.Single(result); + output.WriteLine(result.Single().DateTimeOffset.ToString()); + } + + [Fact] + public void DateTimeOffsetNotEqualIncorrectUtcOffset() + { + string value = dto.ToString("s") + "Z"; + string filter = $"DateTimeOffset eq {value}"; + output.WriteLine(filter); + + var result = SimpleClass.CreateQuery().OData().Filter(filter).ToArray(); + Assert.Empty(result); + } + + [Fact] + public void UtcDateTimeEqualCorrectUtcOffset() + { + string value = dto.ToString("s") + "Z"; + //string value = dto.ToString("u").Replace(" ", "T"); + string filter = $"DateTime eq {value}"; + output.WriteLine(filter); + + var result = SimpleClass.CreateQuery().OData(c => c.QuerySettings.TimeZone = TimeZoneInfo.Utc).Filter(filter).ToArray(); + Assert.Single(result); + output.WriteLine(result.Single().DateTime.ToString() + "UTC"); + + } + + [Fact] + public void UtcDateTimeEqualCorrectLocalOffset() + { + string value = dto.DateTime.ToLocalTime().ToString("s").Replace(" ", "T") + dto.ToString("zzz"); + string filter = $"DateTime eq {value}"; + output.WriteLine(filter); + + var result = SimpleClass.CreateQuery().OData(c => c.QuerySettings.TimeZone = TimeZoneInfo.Utc).Filter(filter).ToArray(); + Assert.Single(result); + output.WriteLine(result.Single().DateTime.ToString() + "UTC"); + } + + [Fact] + public void UtcDateTimeNotEqualIncorrectLocalOffset() + { + string value = dto.ToString("u").Replace(" ", "T").Replace("Z", dto.ToString("zzz")); + string filter = $"DateTime eq {value}"; + output.WriteLine(filter); + + var result = SimpleClass.CreateQuery().OData(c => c.QuerySettings.TimeZone = TimeZoneInfo.Utc).Filter(filter).ToArray(); + Assert.Empty(result); + } + + [Fact] + public void UtcDateTimeNotEqualIncorrectLocalOffset2() + { + string value = dto.ToString("s") + dto.ToString("zzz"); + string filter = $"DateTime eq {value}"; + output.WriteLine(filter); + + var result = SimpleClass.CreateQuery().OData(c => c.QuerySettings.TimeZone = TimeZoneInfo.Utc).Filter(filter).ToArray(); + Assert.Empty(result); + } + + [Fact] + public void UtcDateTimetNotEqualIncorrectUtcOffset() + { + string value = dto.ToString("u").Replace(" ", "T"); + string filter = $"DateTime eq {value}"; + output.WriteLine(filter); + + var result = SimpleClass.CreateQuery().OData(c => c.QuerySettings.TimeZone = TimeZoneInfo.Utc).Filter(filter).ToArray(); + Assert.Empty(result); + } + + [Fact] + public void DateTimeCompare() + { + // Compare method compares the Ticks property of t1 and t2 but ignores their Kind property. + // Before comparing DateTime objects, ensure that the objects represent times in the same time zone. + Assert.Equal(dt, dtLocal); + Assert.Equal(dt, dtUtc); + Assert.Equal(dtUtc, dtLocal); + + Assert.NotEqual(TimeZoneInfo.Local, TimeZoneInfo.Utc); + Assert.NotEqual(TimeZoneInfo.Local.GetHashCode(), TimeZoneInfo.Utc.GetHashCode()); + + Assert.Equal(TimeZoneInfo.Utc, TimeZoneInfo.Utc); + Assert.Equal(TimeZoneInfo.Utc.GetHashCode(), TimeZoneInfo.Utc.GetHashCode()); + } + + [Fact] + public void DateOnlyFilterWorks() + { + string value = do1.ToString("yyyy-MM-dd"); + string filter = $"{nameof(SimpleClass.DateOnly)} eq {value}"; + output.WriteLine(filter); + + var result = SimpleClass.CreateQuery().OData().Filter(filter).ToArray(); + Assert.Single(result); + Assert.True(result.First().DateOnly == do1); + } + + } +} diff --git a/test/Microsoft.AspNetCore.OData.Tests/Linq/ExpandTests.cs b/test/Microsoft.AspNetCore.OData.Tests/Linq/ExpandTests.cs new file mode 100644 index 000000000..8667b1b1a --- /dev/null +++ b/test/Microsoft.AspNetCore.OData.Tests/Linq/ExpandTests.cs @@ -0,0 +1,206 @@ +namespace Community.OData.Linq.xTests +{ + using Community.OData.Linq.xTests.SampleData; + using Microsoft.AspNetCore.OData.Query.Wrapper; + using Microsoft.OData; + using System.Collections.Generic; + using System.Linq; + using Xunit; + + public class ExpandTests + { + [Fact] + public void EmptyExpand() + { + ISelectExpandWrapper[] result = ClassWithLink.CreateQuery().OData().SelectExpand().ToArray(); + + IDictionary metadata = result[0].ToDictionary(); + + // Not expanded by default except auto expand attribute + Assert.Equal(2, metadata.Count); + } + + [Fact] + public void EmptyExpandSelectAll() + { + ISelectExpandWrapper[] result = ClassWithLink.CreateQuery().OData().SelectExpand("*").ToArray(); + + IDictionary metadata = result[0].ToDictionary(); + + // Not expanded by default except auto expand attribute + Assert.Equal(2, metadata.Count); + } + + [Fact] + public void ExpandLink() + { + ISelectExpandWrapper[] result = ClassWithLink.CreateQuery().OData().SelectExpand(null, "Link1").ToArray(); + + IDictionary metadata = result[0].ToDictionary(); + + // Not expanded by default + Assert.Equal(3, metadata.Count); + + Assert.Equal(7, (metadata["Link1"] as ISelectExpandWrapper).ToDictionary().Count); + } + + [Fact] + public void ExpandSelect() + { + ISelectExpandWrapper[] result = ClassWithLink.CreateQuery().OData().SelectExpand("Name,Link1", "Link1").ToArray(); + + IDictionary metadata = result[0].ToDictionary(); + + // Not expanded by default + Assert.Equal(2, metadata.Count); + Assert.Equal(7, (metadata["Link1"] as ISelectExpandWrapper).ToDictionary().Count); + } + + [Fact] + public void ExpandLinkSelect() + { + ISelectExpandWrapper[] result = ClassWithLink.CreateQuery().OData().SelectExpand("Name", "Link1($select=Name)").ToArray(); + + IDictionary metadata = result[0].ToDictionary(); + + // Not expanded by default + Assert.Equal(2, metadata.Count); + Assert.Equal(1, (metadata["Link1"] as ISelectExpandWrapper).ToDictionary().Count); + } + + [Fact] + public void ExpandCollection() + { + ISelectExpandWrapper[] result = ClassWithCollection.CreateQuery().OData().SelectExpand("Name", "Link2").ToArray(); + + IDictionary metadata = result[0].ToDictionary(); + + // Not expanded by default + Assert.Equal(2, metadata.Count); + Assert.Equal(2, (metadata["Link2"] as IEnumerable).Count()); + } + + [Fact] + public void ExpandCollectionWithTop() + { + ISelectExpandWrapper[] result = ClassWithCollection.CreateQuery().OData().SelectExpand("Name", "Link2($top=1)").ToArray(); + + IDictionary metadata = result[0].ToDictionary(); + + // Not expanded by default + Assert.Equal(2, metadata.Count); + Assert.Single((metadata["Link2"] as IEnumerable)); + } + + [Fact] + public void ExpandCollectionWithTopDefaultPageSize() + { + ISelectExpandWrapper[] result = ClassWithCollection.CreateQuery().OData(s => s.QuerySettings.PageSize = 1).SelectExpand("Name", "Link2").ToArray(); + + IDictionary metadata = result[0].ToDictionary(); + + // Not expanded by default + Assert.Equal(2, metadata.Count); + Assert.Single((metadata["Link2"] as IEnumerable)); + } + + [Fact] + public void ExpandCollectionWithTop21() + { + ISelectExpandWrapper[] result = ClassWithCollection.CreateQuery().OData().SelectExpand("Name", "Link2($top=21)").ToArray(); + + IDictionary metadata = result[0].ToDictionary(); + + // Not expanded by default + Assert.Equal(2, metadata.Count); + Assert.Equal(2, (metadata["Link2"] as IEnumerable).Count()); + } + + [Fact] + public void ExpandCollectionWithTopExceedLimit() + { + Assert.Throws( + () => ClassWithCollection.CreateQuery().OData().SelectExpand("Name", "Link2($top=101)")); + } + + [Fact] + public void ExpandCollectionWithFilterAndSelect() + { + ISelectExpandWrapper[] result = ClassWithCollection.CreateQuery().OData().SelectExpand("Name", "Link2($filter=Id eq 311;$select=Name)").ToArray(); + + IDictionary metadata = result[0].ToDictionary(); + + // Not expanded by default + Assert.Equal(2, metadata.Count); + IEnumerable collection = metadata["Link2"] as IEnumerable; + Assert.Single(collection); + + Assert.Equal(1, collection.Single().ToDictionary().Count); + } + + [Fact] + public void ExpandCollectionWithNotExpandable() + { + Assert.Throws( + () => SampleWithCustomKey.CreateQuery().OData().SelectExpand("Id", "NotExpandableLink")); + } + + [Fact] + public void ExpandWithAttributes() + { + ISelectExpandWrapper[] result = SampleWithCustomKey.CreateQuery().OData().SelectExpand().ToArray(); + + IDictionary metadata = result[0].ToDictionary(); + + // Not expanded by default + Assert.Equal(4, metadata.Count); + + Assert.Equal(7, (metadata["AutoExpandLink"] as ISelectExpandWrapper).ToDictionary().Count); + Assert.Equal(7, (metadata["AutoExpandAndSelectLink"] as ISelectExpandWrapper).ToDictionary().Count); + } + + [Fact] + public void ExpandWithAttributesAndExplicit() + { + ISelectExpandWrapper[] result = SampleWithCustomKey.CreateQuery().OData().SelectExpand("*", "AutoExpandAndSelectLink").ToArray(); + + IDictionary metadata = result[0].ToDictionary(); + + // Not expanded by default + Assert.Equal(3, metadata.Count); + + Assert.Equal(7, (metadata["AutoExpandAndSelectLink"] as ISelectExpandWrapper).ToDictionary().Count); + } + + [Fact] + public void ExpandMaxDeepNotExceed() + { + ISelectExpandWrapper[] result = SampleWithCustomKey.CreateQuery().OData().SelectExpand(null, "RecursiveLink($expand=RecursiveLink)").ToArray(); + + IDictionary metadata = result[0].ToDictionary(); + + // Not expanded by default + Assert.Equal(5, metadata.Count); + + Assert.NotNull(metadata["RecursiveLink"]); + } + + [Fact] + public void ExpandMaxDeepExceed() + { + Assert.Throws( + () => SampleWithCustomKey.CreateQuery().OData().SelectExpand(null, "RecursiveLink($expand=RecursiveLink($expand=RecursiveLink))")); + } + + [Fact] + public void ExpandMaxDeepSetInValidationSettings() + { + ISelectExpandWrapper[] result = ClassWithDeepNavigation.CreateQuery().OData(settings => settings.ValidationSettings.MaxExpansionDepth = 3).SelectExpand(null, "D1($expand=D2($expand=D3))").ToArray(); + + IDictionary metadata = result[0].ToDictionary(); + + Assert.Equal(3, metadata.Count); + Assert.Equal("n1123", (((metadata["D1"] as ISelectExpandWrapper).ToDictionary()["D2"] as ISelectExpandWrapper).ToDictionary()["D3"] as ISelectExpandWrapper).ToDictionary()["Name"]); + } + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNetCore.OData.Tests/Linq/FilterDataContractTests.cs b/test/Microsoft.AspNetCore.OData.Tests/Linq/FilterDataContractTests.cs new file mode 100644 index 000000000..7c407fa80 --- /dev/null +++ b/test/Microsoft.AspNetCore.OData.Tests/Linq/FilterDataContractTests.cs @@ -0,0 +1,45 @@ + + +using Microsoft.OData; + +namespace Community.OData.Linq.xTests +{ + using Community.OData.Linq.xTests.SampleData; + using Xunit; + using System.Linq; + + public class FilterDataContractTests + { + [Fact] + public void WhereById() + { + var result = SimpleClassDataContract.CreateQuery().OData().Filter($"{nameof(SimpleClassDataContract.Id)} eq 1").ToArray(); + + Assert.Single(result); + Assert.Equal(1, result[0].Id); + } + + [Fact] + public void WhereByChangedName() + { + var result = SimpleClassDataContract.CreateQuery().OData().Filter("nameChanged eq 'n1'").ToArray(); + + Assert.Single(result); + Assert.Equal(1, result[0].Id); + } + + [Fact] + public void WhereByIgnoredMemberThrowException() + { + Assert.Throws(() => SimpleClassDataContract.CreateQuery().OData() + .Filter($"{nameof(SimpleClassDataContract.NameToIgnore)} eq 'ign1'")); + } + + [Fact] + public void WhereByNotMarkedThrowException() + { + Assert.Throws(() => SimpleClassDataContract.CreateQuery().OData() + .Filter($"{nameof(SimpleClassDataContract.NameNotMarked)} eq 'nm1'")); + } + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNetCore.OData.Tests/Linq/FilterFunctionsTests.cs b/test/Microsoft.AspNetCore.OData.Tests/Linq/FilterFunctionsTests.cs new file mode 100644 index 000000000..9d743a406 --- /dev/null +++ b/test/Microsoft.AspNetCore.OData.Tests/Linq/FilterFunctionsTests.cs @@ -0,0 +1,29 @@ + + +namespace Community.OData.Linq.xTests +{ + using Community.OData.Linq.xTests.SampleData; + using Xunit; + using System.Linq; + + public class FilterFunctionsTests + { + [Fact] + public void Contains() + { + var result = SimpleClass.CreateQuery().OData().Filter("contains(Name,'1')").ToArray(); + + Assert.Single(result); + Assert.Equal("n1", result[0].Name); + } + + [Fact] + public void Substring() + { + var result = SimpleClass.CreateQuery().OData().Filter("substring(Name, 1) eq '2'").ToArray(); + + Assert.Single(result); + Assert.Equal("n2", result[0].Name); + } + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNetCore.OData.Tests/Linq/FilterNavigationCollectionTests.cs b/test/Microsoft.AspNetCore.OData.Tests/Linq/FilterNavigationCollectionTests.cs new file mode 100644 index 000000000..9e8f92d28 --- /dev/null +++ b/test/Microsoft.AspNetCore.OData.Tests/Linq/FilterNavigationCollectionTests.cs @@ -0,0 +1,21 @@ +namespace Community.OData.Linq.xTests +{ + using System.Collections; + using System.Linq; + + using Community.OData.Linq.xTests.SampleData; + + using Xunit; + + public class FilterNavigationCollectionTests + { + [Fact] + public void WhereCol1() + { + var result = ClassWithCollection.CreateQuery().OData().Filter("Link2/any(s: s/Id eq 311)").ToArray(); + + Assert.Single((IEnumerable)result); + Assert.Equal(31, result[0].Id); + } + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNetCore.OData.Tests/Linq/FilterNavigationLinkTests.cs b/test/Microsoft.AspNetCore.OData.Tests/Linq/FilterNavigationLinkTests.cs new file mode 100644 index 000000000..553d8adfb --- /dev/null +++ b/test/Microsoft.AspNetCore.OData.Tests/Linq/FilterNavigationLinkTests.cs @@ -0,0 +1,29 @@ +namespace Community.OData.Linq.xTests +{ + using System.Collections; + using System.Linq; + + using Community.OData.Linq.xTests.SampleData; + + using Microsoft.OData; + + using Xunit; + + public class FilterNavigationLinkTests + { + [Fact] + public void WhereNav1() + { + var result = ClassWithLink.CreateQuery().OData().Filter("Link1/Id eq 211").ToArray(); + + Assert.Single((IEnumerable) result); + Assert.Equal(21, result[0].Id); + } + + [Fact] + public void WhereNavThrowException() + { + Assert.Throws(() => ClassWithLink.CreateQuery().OData().Filter("Link2/Id eq 211")); + } + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNetCore.OData.Tests/Linq/FilterTests.cs b/test/Microsoft.AspNetCore.OData.Tests/Linq/FilterTests.cs new file mode 100644 index 000000000..e39ff8bef --- /dev/null +++ b/test/Microsoft.AspNetCore.OData.Tests/Linq/FilterTests.cs @@ -0,0 +1,127 @@ +namespace Community.OData.Linq.xTests +{ + using Community.OData.Linq.xTests.SampleData; + using Microsoft.OData; + using System.Linq; + using Xunit; + + public class FilterTests + { + [Fact] + public void WhereById() + { + var result = SimpleClass.CreateQuery().OData().Filter("Id eq 1").ToArray(); + + Assert.Single(result); + Assert.Equal(1, result[0].Id); + } + + [Fact] + public void WhereByName() + { + var result = SimpleClass.CreateQuery().OData().Filter("Name eq 'n1'").ToArray(); + + Assert.Single(result); + Assert.Equal("n1", result[0].Name); + } + + [Fact] + public void WhereByNameCaseInsensitiveKeyByDefault() + { + var result = SimpleClass.CreateQuery().OData().Filter("name eq 'n1'").ToArray(); + + Assert.Single(result); + Assert.Equal("n1", result[0].Name); + } + + [Fact] + public void WhereByNameCaseSensitiveKeyByConfigThowException() + { + Assert.Throws( + () => SimpleClass.CreateQuery().OData(s => s.EnableCaseInsensitive = false).Filter("name eq 'n1'")); + } + + [Fact] + public void WhereByRandomStringThrowException() + { + Assert.Throws( + () => SimpleClass.CreateQuery().OData().Filter("qwe")); + } + + [Fact] + public void WhereByEnumString() + { + var result = SimpleClass.CreateQuery().OData().Filter("TestEnum eq 'Item2'").ToArray(); + + Assert.Single(result); + Assert.Equal("n2", result[0].Name); + } + + [Fact] + public void WhereByEnumNumber() + { + var result = SimpleClass.CreateQuery() + .OData() + .Filter("TestEnum eq '2'").ToArray(); + + Assert.Single(result); + Assert.Equal("n2", result[0].Name); + } + + [Fact] + public void WhereByEnumStringCaseInsensitiveValueByDefault() + { + var result = SimpleClass.CreateQuery().OData().Filter("TestEnum eq 'item2'").ToArray(); + + Assert.Single(result); + Assert.Equal("n2", result[0].Name); + } + + [Fact] + public void WhereByEnumStringCaseInsensitiveKeyByDefault() + { + var result = SimpleClass.CreateQuery().OData().Filter("testEnum eq 'item2'").ToArray(); + + Assert.Single(result); + Assert.Equal("n2", result[0].Name); + } + + [Fact] + public void WhereByNameNotNull() + { + var result = SimpleClass.CreateQuery().OData().Filter("Name ne null").ToArray(); + + Assert.Equal("n1", result[0].Name); + } + + [Fact] + public void WhereByEnumStringWithInKeyword() + { + var result = SimpleClass.CreateQuery().OData().Filter($"{nameof(SimpleClass.TestEnum)} in ('{nameof(TestEnum.Item2)}')").ToArray(); + + Assert.Single(result); + Assert.Equal("n2", result[0].Name); + } + + [Fact] + public void WhereByNonFilterableThrowException() + { + Assert.Throws( + () => SimpleClass.CreateQuery().OData(s => s.EnableCaseInsensitive = false).Filter($"{nameof(SimpleClass.NameNotFilter)} eq 'nf1'")); + } + + [Fact] + public void WhereByNotMappedThrow() + { + Assert.Throws( + () => SimpleClass.CreateQuery().OData(s => s.EnableCaseInsensitive = false).Filter($"{nameof(SimpleClass.NotMapped)} eq 'nf1'")); + } + + [Fact] + public void WhereByIgnoreDataMemberThrowException() + { + Assert.Throws( + () => SimpleClass.CreateQuery().OData(s => s.EnableCaseInsensitive = false).Filter($"{nameof(SimpleClass.NameToIgnore)} eq 'ni1'")); + } + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNetCore.OData.Tests/Linq/HashTests.cs b/test/Microsoft.AspNetCore.OData.Tests/Linq/HashTests.cs new file mode 100644 index 000000000..83504f23c --- /dev/null +++ b/test/Microsoft.AspNetCore.OData.Tests/Linq/HashTests.cs @@ -0,0 +1,49 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +using Xunit; + +namespace Community.OData.Linq.xTests +{ + public class HashTests + { + [Fact] + public void Hashes() + { + ODataSettings s1 = new ODataSettings(); + ODataSettings s2 = new ODataSettings(); + + Assert.Equal(s1.QuerySettings.GetHashCode(), s2.QuerySettings.GetHashCode()); + Assert.Equal(s1.DefaultQuerySettings.GetHashCode(), s2.DefaultQuerySettings.GetHashCode()); + Assert.Equal(HashCode.Combine(s1.QuerySettings, s1.DefaultQuerySettings), HashCode.Combine(s2.QuerySettings, s2.DefaultQuerySettings)); + + s1.QuerySettings.EnsureStableOrdering = false; + Assert.NotEqual(s1.QuerySettings.GetHashCode(), s2.QuerySettings.GetHashCode()); + Assert.Equal(s1.DefaultQuerySettings.GetHashCode(), s2.DefaultQuerySettings.GetHashCode()); + Assert.NotEqual(HashCode.Combine(s1.QuerySettings, s1.DefaultQuerySettings), HashCode.Combine(s2.QuerySettings, s2.DefaultQuerySettings)); + + s1.QuerySettings.EnsureStableOrdering = true; + Assert.Equal(s1.QuerySettings.GetHashCode(), s2.QuerySettings.GetHashCode()); + Assert.Equal(s1.DefaultQuerySettings.GetHashCode(), s2.DefaultQuerySettings.GetHashCode()); + Assert.Equal(HashCode.Combine(s1.QuerySettings, s1.DefaultQuerySettings), HashCode.Combine(s2.QuerySettings, s2.DefaultQuerySettings)); + + s1.DefaultQuerySettings.EnableExpand = false; + Assert.Equal(s1.QuerySettings.GetHashCode(), s2.QuerySettings.GetHashCode()); + Assert.NotEqual(s1.DefaultQuerySettings.GetHashCode(), s2.DefaultQuerySettings.GetHashCode()); + Assert.NotEqual(HashCode.Combine(s1.QuerySettings, s1.DefaultQuerySettings), HashCode.Combine(s2.QuerySettings, s2.DefaultQuerySettings)); + + s1.DefaultQuerySettings.EnableExpand = true; + Assert.Equal(s1.QuerySettings.GetHashCode(), s2.QuerySettings.GetHashCode()); + Assert.Equal(s1.DefaultQuerySettings.GetHashCode(), s2.DefaultQuerySettings.GetHashCode()); + Assert.Equal(HashCode.Combine(s1.QuerySettings, s1.DefaultQuerySettings), HashCode.Combine(s2.QuerySettings, s2.DefaultQuerySettings)); + + // Microsoft classes public properties + Assert.Equal(HashCode.Combine(s1.ParserSettings.MaximumExpansionCount, s1.ParserSettings.MaximumExpansionDepth), HashCode.Combine(s2.ParserSettings.MaximumExpansionCount, s2.ParserSettings.MaximumExpansionDepth)); + s1.ParserSettings.MaximumExpansionCount = 1; + Assert.NotEqual(HashCode.Combine(s1.ParserSettings.MaximumExpansionCount, s1.ParserSettings.MaximumExpansionDepth), HashCode.Combine(s2.ParserSettings.MaximumExpansionCount, s2.ParserSettings.MaximumExpansionDepth)); + } + } +} diff --git a/test/Microsoft.AspNetCore.OData.Tests/Linq/JsonTests.cs b/test/Microsoft.AspNetCore.OData.Tests/Linq/JsonTests.cs new file mode 100644 index 000000000..8a62a33d0 --- /dev/null +++ b/test/Microsoft.AspNetCore.OData.Tests/Linq/JsonTests.cs @@ -0,0 +1,28 @@ +namespace Community.OData.Linq.xTests +{ + using Newtonsoft.Json; + using Newtonsoft.Json.Linq; + + using Xunit; + + public class JsonTests + { + [Fact] + public void SerializeSelectExpand() + { + JToken token = null;// ClassWithCollection.CreateQuery().OData().SelectExpand("Name", "Link2($filter=Id eq 311;$select=Name)").ToJson(); + Assert.NotNull(token); + + Assert.DoesNotContain("ModelID", token.ToString(Formatting.None)); + } + + [Fact] + public void SerializeSelectExpand2() + { + JToken token = null;// ClassWithCollection.CreateQuery().OData().SelectExpand("Name", "Link2($filter=Id eq 311;$select=Name)").ToJson(); + Assert.NotNull(token); + + Assert.DoesNotContain("ModelID", token.ToString(Formatting.None)); + } + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNetCore.OData.Tests/Linq/ODataTests.cs b/test/Microsoft.AspNetCore.OData.Tests/Linq/ODataTests.cs new file mode 100644 index 000000000..b0ff38463 --- /dev/null +++ b/test/Microsoft.AspNetCore.OData.Tests/Linq/ODataTests.cs @@ -0,0 +1,45 @@ +namespace Community.OData.Linq.xTests +{ + using System; + using System.Collections.Generic; + using System.Linq; + + using Community.OData.Linq.xTests.SampleData; + + using Microsoft.OData; + + using Xunit; + + public class ODataTests + { + [Fact] + public void CustomKey() + { + var result = SampleWithCustomKey.CreateQuery().OData().Filter("Name eq 'n1'").ToArray(); + + Assert.Single(result); + Assert.Equal("n1", result[0].Name); + } + + [Fact] + public void WithoutKeyThrowException() + { + Assert.Throws(() => SampleWithoutKey.CreateQuery().OData()); + } + + [Fact] + public void Disposable() + { + // Generate the list of items to query + var items = new List(); + items.Add(new TestItem() { Id = Guid.NewGuid(), Name = "Test", Number = 1 }); + items.Add(new TestItem() { Id = Guid.NewGuid(), Name = "Another", Number = 2 }); + + var odata = items.AsQueryable().OData(); + var filteredItems = odata.Filter("Number eq 2"); + //odata.Dispose(); // Test that the OData object is being torn down + + Assert.Equal(1, filteredItems.Count()); + } + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNetCore.OData.Tests/Linq/OpenTypesTests.cs b/test/Microsoft.AspNetCore.OData.Tests/Linq/OpenTypesTests.cs new file mode 100644 index 000000000..4c077a743 --- /dev/null +++ b/test/Microsoft.AspNetCore.OData.Tests/Linq/OpenTypesTests.cs @@ -0,0 +1,124 @@ + +namespace Community.OData.Linq.xTests +{ + using Community.OData.Linq.xTests.SampleData; + using Microsoft.AspNetCore.OData.Query; + using System; + using System.Linq; + using Xunit; + + public class OpenTypesTests + { + // Dynamic properties dictionary is null for one item + + [Fact] + public void FilterDefault() + { + Assert.Throws(() => OpenType.CreateQuery().OData().Filter("Name1 eq 'n1'").ToArray()); + } + + [Fact] + public void FilterNullPropagationTrue() + { + var result = OpenType.CreateQuery().OData(c => c.QuerySettings.HandleNullPropagation = HandleNullPropagationOption.True).Filter("Name1 eq 'n1'").ToArray(); + + Assert.Single(result); + } + + [Fact] + public void FilterNullPropagationFalse() + { + Assert.Throws(() => OpenType.CreateQuery().OData(c => c.QuerySettings.HandleNullPropagation = HandleNullPropagationOption.False).Filter("Name1 eq 'n1'").ToArray()); + } + + // Dynamic properties dictionary is not null, however properties with different names + + [Fact] + public void FilterDefaultWithNotNullDictionary() + { + var result = OpenType.CreateQueryWithNotNullDictionary().OData().Filter("Name1 eq 'n1'").ToArray(); + + Assert.Single(result); + } + + [Fact] + public void FilterNullPropagationTrueWithNotNullDictionary() + { + var result = OpenType.CreateQueryWithNotNullDictionary().OData(c => c.QuerySettings.HandleNullPropagation = HandleNullPropagationOption.True).Filter("Name1 eq 'n1'").ToArray(); + + Assert.Single(result); + } + + [Fact] + public void FilterNullPropagationFalseWithNotNullDictionary() + { + var result = OpenType.CreateQueryWithNotNullDictionary().OData(c => c.QuerySettings.HandleNullPropagation = HandleNullPropagationOption.False).Filter("Name1 eq 'n1'").ToArray(); + + Assert.Single(result); + } + + [Fact] + public void FilterNullPropagationFalseWithNotNullDictionaryPropertyNotExists() + { + var result = OpenType.CreateQueryWithNotNullDictionary().OData(c => c.QuerySettings.HandleNullPropagation = HandleNullPropagationOption.False).Filter("Name3 eq 'n1'").ToArray(); + + Assert.Empty(result); + } + + // Dynamic properties dictionary is not null, all properties with same name + + [Fact] + public void FilterDefaultAllProperties() + { + var result = OpenType.CreateQueryWithAllProperties().OData().Filter("Name1 eq 'n1'").ToArray(); + + Assert.Single(result); + } + + [Fact] + public void FilterNullPropagationTrueAllProperties() + { + var result = OpenType.CreateQueryWithAllProperties().OData(c => c.QuerySettings.HandleNullPropagation = HandleNullPropagationOption.True).Filter("Name1 eq 'n1'").ToArray(); + + Assert.Single(result); + } + + [Fact] + public void FilterNullPropagationFalseAllProperties() + { + var result = OpenType.CreateQueryWithAllProperties().OData(c => c.QuerySettings.HandleNullPropagation = HandleNullPropagationOption.False).Filter("Name1 eq 'n1'").ToArray(); + + Assert.Single(result); + } + + [Fact] + public void FilterNullPropagationFalseAllPropertiesNotExistingProperty() + { + var result = OpenType.CreateQueryWithAllProperties().OData(c => c.QuerySettings.HandleNullPropagation = HandleNullPropagationOption.False).Filter("Name3 eq 'n3'").ToArray(); + + Assert.Empty(result); + } + + //[Fact] + //public void SelectDefaultWithNotNullDictionary() + //{ + // var result = OpenType.CreateQueryWithNotNullDictionary().OData().Filter("Name1 eq 'n1'").SelectExpand("Name1").Single(); + + // var metadata = result.ToDictionary(); + + // Assert.Single(metadata); + + // Assert.Equal("DynamicProperties", metadata.Single().Key); + // Assert.Equal("Name1", (metadata.Single().Value as IDictionary).Single().Key); + // Assert.Equal("n1", (metadata.Single().Value as IDictionary).Single().Value); + //} + + //[Fact] + //public void SelectJsonDefaultWithNotNullDictionary() + //{ + // var result = OpenType.CreateQueryWithNotNullDictionary().OData().SelectExpandJsonToken("Name1"); + + // Assert.Equal("{Nmae1:n1}", result.ToString()); + //} + } +} diff --git a/test/Microsoft.AspNetCore.OData.Tests/Linq/OrderByTests.cs b/test/Microsoft.AspNetCore.OData.Tests/Linq/OrderByTests.cs new file mode 100644 index 000000000..346e9a7fa --- /dev/null +++ b/test/Microsoft.AspNetCore.OData.Tests/Linq/OrderByTests.cs @@ -0,0 +1,59 @@ +using System.Linq; +using Community.OData.Linq.xTests.SampleData; +using Microsoft.OData; +using Xunit; + +namespace Community.OData.Linq.xTests +{ + using System; + + public class OrderByTests + { + [Fact] + public void OrderByIdAsc() + { + var result = SimpleClass.CreateQuery().OData().OrderBy("Id asc").First(); + + Assert.Equal(1, result.Id); + } + + [Fact] + public void OrderByIdDesc() + { + var result = SimpleClass.CreateQuery().Take(2).OData().OrderBy("Id desc").First(); + + Assert.Equal(2, result.Id); + } + + [Fact] + public void OrderByIdDefault() + { + var result = SimpleClass.CreateQuery().OData().OrderBy("Id,Name").First(); + + Assert.Equal(1, result.Id); + } + + [Fact] + public void OrderByIdCaseInsensitiveDefault() + { + var result = SimpleClass.CreateQuery().OData().OrderBy("id desc,name").First(); + + Assert.Equal(2, result.Id); + } + + [Fact] + public void OrderByIdCaseSensitiveConfig() + { + Assert.Throws( + () => SimpleClass.CreateQuery().OData(s => s.EnableCaseInsensitive = false).OrderBy("id desc,name")); + } + + + [Fact, Trait("Category", "A")] + public void OrderByNotSortable() + { + Assert.Throws( + () => SimpleClass.CreateQuery().OData().OrderBy($"{nameof(SimpleClass.NotOrderable)}")); + } + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNetCore.OData.Tests/Linq/SampleData/ClassWithCollection.cs b/test/Microsoft.AspNetCore.OData.Tests/Linq/SampleData/ClassWithCollection.cs new file mode 100644 index 000000000..af15c2abe --- /dev/null +++ b/test/Microsoft.AspNetCore.OData.Tests/Linq/SampleData/ClassWithCollection.cs @@ -0,0 +1,25 @@ +namespace Community.OData.Linq.xTests.SampleData +{ + using System.Collections.Generic; + using System.Linq; + + public class ClassWithCollection + { + private static readonly ClassWithCollection[] items = new[] + { + new ClassWithCollection {Id = 31, Name = "n31", Link2 = new[] { new SimpleClass {Id = 311, Name = "n311"},new SimpleClass {Id = 312, Name = "n312"} }}, + new ClassWithCollection {Id = 32, Name = "n32", Link2 = new[] { new SimpleClass {Id = 321, Name = "n321"} }} + }; + + public static IQueryable CreateQuery() + { + return items.AsQueryable(); + } + + public long Id { get; set; } + + public string Name { get; set; } + + public virtual ICollection Link2 { get; set; } + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNetCore.OData.Tests/Linq/SampleData/ClassWithDeepNavigation.cs b/test/Microsoft.AspNetCore.OData.Tests/Linq/SampleData/ClassWithDeepNavigation.cs new file mode 100644 index 000000000..629553a2d --- /dev/null +++ b/test/Microsoft.AspNetCore.OData.Tests/Linq/SampleData/ClassWithDeepNavigation.cs @@ -0,0 +1,28 @@ +using Microsoft.OData.ModelBuilder; +using System.Linq; + +namespace Community.OData.Linq.xTests.SampleData +{ + public class ClassWithDeepNavigation + { + private static readonly ClassWithDeepNavigation[] items = new[] + { + new ClassWithDeepNavigation { D1 = new ClassWithDeepNavigation {D2 = new ClassWithDeepNavigation{D3 = new ClassWithDeepNavigation{Id = 11, Name = "n1123",}} } }, + new ClassWithDeepNavigation { D1 = new ClassWithDeepNavigation {D2 = new ClassWithDeepNavigation{D3 = new ClassWithDeepNavigation{Id = 22, Name = "n2123"} } } } + }; + + public static IQueryable CreateQuery() + { + return items.AsQueryable(); + } + + public long Id { get; set; } + + public string Name { get; set; } + + [Expand(MaxDepth = 3)] + public virtual ClassWithDeepNavigation D1 { get; set; } + public virtual ClassWithDeepNavigation D2 { get; set; } + public virtual ClassWithDeepNavigation D3 { get; set; } + } +} diff --git a/test/Microsoft.AspNetCore.OData.Tests/Linq/SampleData/ClassWithLink.cs b/test/Microsoft.AspNetCore.OData.Tests/Linq/SampleData/ClassWithLink.cs new file mode 100644 index 000000000..6ebedbdf2 --- /dev/null +++ b/test/Microsoft.AspNetCore.OData.Tests/Linq/SampleData/ClassWithLink.cs @@ -0,0 +1,34 @@ +namespace Community.OData.Linq.xTests.SampleData +{ + using Microsoft.OData.ModelBuilder; + using System.Linq; + + public class ClassWithLink + { + private static readonly ClassWithLink[] items = new[] + { + new ClassWithLink {Id = 21, Name = "n21", Link1 = new SimpleClass {Id = 211, Name = "n211"}}, + new ClassWithLink {Id = 22, Name = "n22", Link1 = new SimpleClass {Id = 221, Name = "n221"}} + }; + + public static IQueryable CreateQuery() + { + return items.AsQueryable(); + } + + public long Id { get; set; } + + public string Name { get; set; } + + public virtual SimpleClass Link1 { get; set; } + + [NotNavigable] + public virtual SimpleClass Link2 { get; set; } + + [Select(SelectType = SelectExpandType.Automatic)] + public virtual SimpleClass Link3 { get; set; } + + [Select(SelectType = SelectExpandType.Disabled)] + public virtual SimpleClass Link4 { get; set; } + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNetCore.OData.Tests/Linq/SampleData/OpenType.cs b/test/Microsoft.AspNetCore.OData.Tests/Linq/SampleData/OpenType.cs new file mode 100644 index 000000000..394ae5f11 --- /dev/null +++ b/test/Microsoft.AspNetCore.OData.Tests/Linq/SampleData/OpenType.cs @@ -0,0 +1,48 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Community.OData.Linq.xTests.SampleData +{ + public class OpenType + { + public int Id { get; set; } + + public IDictionary DynamicProperties { get; set; } + + private static readonly OpenType[] items = new[] + { + new OpenType {Id = 1, DynamicProperties = new Dictionary(){ {"Name1","n1" } } }, + new OpenType {Id = 2, DynamicProperties = new Dictionary(){ {"Name2","n2" } } }, + new OpenType {Id = 3}, + }; + + private static readonly OpenType[] itemsWithNotNullDictionary = new[] + { + new OpenType {Id = 1, DynamicProperties = new Dictionary(){ {"Name1","n1" } } }, + new OpenType {Id = 2, DynamicProperties = new Dictionary(){ {"Name2","n2" } } }, + }; + + private static readonly OpenType[] itemsWithAllProperties = new[] + { + new OpenType {Id = 1, DynamicProperties = new Dictionary(){ {"Name1","n1" } } }, + new OpenType {Id = 2, DynamicProperties = new Dictionary(){ {"Name1","n2" } } }, + }; + + public static IQueryable CreateQuery() + { + return items.AsQueryable(); + } + + public static IQueryable CreateQueryWithNotNullDictionary() + { + return itemsWithNotNullDictionary.AsQueryable(); + } + + public static IQueryable CreateQueryWithAllProperties() + { + return itemsWithAllProperties.AsQueryable(); + } + } +} diff --git a/test/Microsoft.AspNetCore.OData.Tests/Linq/SampleData/SampleWithCustomKey.cs b/test/Microsoft.AspNetCore.OData.Tests/Linq/SampleData/SampleWithCustomKey.cs new file mode 100644 index 000000000..cc08ddc85 --- /dev/null +++ b/test/Microsoft.AspNetCore.OData.Tests/Linq/SampleData/SampleWithCustomKey.cs @@ -0,0 +1,66 @@ +namespace Community.OData.Linq.xTests.SampleData +{ + using Microsoft.OData.ModelBuilder; + using System; + using System.Collections.Generic; + using System.ComponentModel.DataAnnotations; + using System.Linq; + + public class SampleWithCustomKey + { + private static readonly SampleWithCustomKey[] items = + { + new SampleWithCustomKey { + Name = "n1", DateTime = new DateTime(2018, 1, 26), + ExpandableLink = new SimpleClass() { Id=1 }, + SelectableLink = new SimpleClass() { Id=2 }, + AutoExpandLink = new SimpleClass() { Id=3 }, + AutoExpandAndSelectLink = new SimpleClass() { Id=4 }, + ExpandAndSelectLink = new SimpleClass() { Id=5 }, + RecursiveLink = new SampleWithCustomKey(){RecursiveLink=new SampleWithCustomKey{RecursiveLink=new SampleWithCustomKey{Name="qwe"}} } + }, + new SampleWithCustomKey { + Name = "n2", DateTime = new DateTime(2001, 1, 26), + ExpandableLink = new SimpleClass() { Id=2 }, + SelectableLink = new SimpleClass() { Id=2 }, + AutoExpandLink = new SimpleClass() { Id=3 }, + AutoExpandAndSelectLink = new SimpleClass() { Id=4 }, + ExpandAndSelectLink = new SimpleClass() { Id=5 }, + RecursiveLink = new SampleWithCustomKey(){RecursiveLink=new SampleWithCustomKey{RecursiveLink=new SampleWithCustomKey{Name="qwe"}} } + }, + }; + + public static IQueryable CreateQuery() + { + return items.AsQueryable(); + } + + [Key] + public string Name { get; set; } + + public DateTime DateTime { get; set; } + + [NotExpandable] + public ICollection NotExpandableLink { get; set; } + + [Expand(ExpandType = SelectExpandType.Automatic, MaxDepth = 2)] + public SimpleClass ExpandableLink { get; set; } + + [Select(SelectType = SelectExpandType.Automatic)] + public SimpleClass SelectableLink { get; set; } + + [AutoExpand(DisableWhenSelectPresent = true)] + public SimpleClass AutoExpandLink { get; set; } + + [AutoExpand(DisableWhenSelectPresent = true)] + [Select(SelectType = SelectExpandType.Automatic)] + public SimpleClass AutoExpandAndSelectLink { get; set; } + + [Expand(ExpandType = SelectExpandType.Automatic, MaxDepth = 2)] + [Select(SelectType = SelectExpandType.Automatic)] + public SimpleClass ExpandAndSelectLink { get; set; } + + [Expand(ExpandType = SelectExpandType.Automatic, MaxDepth = 2)] + public SampleWithCustomKey RecursiveLink { get; set; } + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNetCore.OData.Tests/Linq/SampleData/SampleWithoutKey.cs b/test/Microsoft.AspNetCore.OData.Tests/Linq/SampleData/SampleWithoutKey.cs new file mode 100644 index 000000000..4890344a4 --- /dev/null +++ b/test/Microsoft.AspNetCore.OData.Tests/Linq/SampleData/SampleWithoutKey.cs @@ -0,0 +1,23 @@ +namespace Community.OData.Linq.xTests.SampleData +{ + using System; + using System.Linq; + + public class SampleWithoutKey + { + private static readonly SampleWithoutKey[] items = + { + new SampleWithoutKey { Name = "n1", DateTime = new DateTime(2018, 1, 26)}, + new SampleWithoutKey { Name = "n2", DateTime = new DateTime(2001, 1, 26)} + }; + + public static IQueryable CreateQuery() + { + return items.AsQueryable(); + } + + public string Name { get; set; } + + public DateTime DateTime { get; set; } + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNetCore.OData.Tests/Linq/SampleData/SimpleClass.cs b/test/Microsoft.AspNetCore.OData.Tests/Linq/SampleData/SimpleClass.cs new file mode 100644 index 000000000..921d5e6c0 --- /dev/null +++ b/test/Microsoft.AspNetCore.OData.Tests/Linq/SampleData/SimpleClass.cs @@ -0,0 +1,67 @@ +using System.Runtime.Serialization; + +namespace Community.OData.Linq.xTests.SampleData +{ + using Microsoft.OData.ModelBuilder; + using System; + using System.ComponentModel.DataAnnotations.Schema; + using System.Linq; + + public class SimpleClass + { + private static readonly SimpleClass[] items = + { + new SimpleClass { + Id = 1, + Name = "n1", + DateTimeOffset = new DateTimeOffset(new DateTime(2018, 1, 26), TimeZoneInfo.Local.BaseUtcOffset), + DateTime = new DateTime(2018, 1, 26), + DateOnly = new DateOnly(2018, 1, 26), + TimeOnly = new TimeOnly(12, 34, 56), + TestEnum = TestEnum.Item1, + NameToIgnore = "ni1", + NameNotFilter="nf1"}, + new SimpleClass { + Id = 2, + Name = "n2", + DateTimeOffset = new DateTimeOffset(new DateTime(2001, 1, 26), TimeZoneInfo.Local.BaseUtcOffset), + DateTime = new DateTime(2001, 1, 26), + DateOnly = new DateOnly(2001, 1, 26), + TimeOnly = new TimeOnly(1, 2, 3), + TestEnum = TestEnum.Item2, + NameToIgnore = "ni1", + NameNotFilter="nf2"} + }; + + public static IQueryable CreateQuery() + { + return items.AsQueryable(); + } + + public int Id { get; set; } + + public string Name { get; set; } + + public DateTime DateTime { get; set; } + + public DateTimeOffset DateTimeOffset { get; set; } + + public DateOnly DateOnly { get; set; } + + public TimeOnly TimeOnly { get; set; } + + public TestEnum TestEnum { get; set; } + + [IgnoreDataMember] + public string NameToIgnore { get; set; } + + [NonFilterable] + public string NameNotFilter { get; set; } + + [NotSortable] + public int NotOrderable { get; set; } + + [NotMapped] + public int NotMapped { get; set; } + } +} diff --git a/test/Microsoft.AspNetCore.OData.Tests/Linq/SampleData/SimpleClassDataContract.cs b/test/Microsoft.AspNetCore.OData.Tests/Linq/SampleData/SimpleClassDataContract.cs new file mode 100644 index 000000000..59f9da344 --- /dev/null +++ b/test/Microsoft.AspNetCore.OData.Tests/Linq/SampleData/SimpleClassDataContract.cs @@ -0,0 +1,31 @@ +namespace Community.OData.Linq.xTests.SampleData +{ + using System.Linq; + using System.Runtime.Serialization; + + [DataContract] + public class SimpleClassDataContract + { + private static readonly SimpleClassDataContract[] items = + { + new SimpleClassDataContract {Id = 1, Name = "n1", NameToIgnore = "ign1", NameNotMarked = "nm1"}, + new SimpleClassDataContract {Id = 2, Name = "n2", NameToIgnore = "ign2", NameNotMarked = "nm2"} + }; + + public static IQueryable CreateQuery() + { + return items.AsQueryable(); + } + + [DataMember] + public int Id { get; set; } + + [DataMember(Name = "nameChanged")] + public string Name { get; set; } + + [IgnoreDataMember] + public string NameToIgnore { get; set; } + + public string NameNotMarked { get; set; } + } +} diff --git a/test/Microsoft.AspNetCore.OData.Tests/Linq/SampleData/TestEnum.cs b/test/Microsoft.AspNetCore.OData.Tests/Linq/SampleData/TestEnum.cs new file mode 100644 index 000000000..08d52d452 --- /dev/null +++ b/test/Microsoft.AspNetCore.OData.Tests/Linq/SampleData/TestEnum.cs @@ -0,0 +1,11 @@ +namespace Community.OData.Linq.xTests.SampleData +{ + public enum TestEnum + { + None = 0, + + Item1 = 1, + + Item2 = 2 + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNetCore.OData.Tests/Linq/SampleData/TestItem.cs b/test/Microsoft.AspNetCore.OData.Tests/Linq/SampleData/TestItem.cs new file mode 100644 index 000000000..a709dc1d1 --- /dev/null +++ b/test/Microsoft.AspNetCore.OData.Tests/Linq/SampleData/TestItem.cs @@ -0,0 +1,15 @@ +using System; + +namespace Community.OData.Linq.xTests.SampleData +{ + /// + /// A utility class for use in ODataTests + /// + public class TestItem + { + public Guid Id { get; set; } + public string Name { get; set; } + public int Number { get; set; } + } + +} \ No newline at end of file diff --git a/test/Microsoft.AspNetCore.OData.Tests/Linq/SelectTests.cs b/test/Microsoft.AspNetCore.OData.Tests/Linq/SelectTests.cs new file mode 100644 index 000000000..d79bd2b3b --- /dev/null +++ b/test/Microsoft.AspNetCore.OData.Tests/Linq/SelectTests.cs @@ -0,0 +1,128 @@ +namespace Community.OData.Linq.xTests +{ + using Community.OData.Linq.xTests.SampleData; + using Microsoft.AspNetCore.OData.Query.Wrapper; + using Microsoft.OData; + using System; + using System.Collections.Generic; + using System.Linq; + using Xunit; + + public class SelectTests + { + [Fact] + public void SelectDefault() + { + ISelectExpandWrapper[] result = SimpleClass.CreateQuery().OData().SelectExpand().ToArray(); + + IDictionary metadata = result[0].ToDictionary(); + + Assert.Equal(7, metadata.Count); + } + + [Fact] + public void SelectAllt() + { + ISelectExpandWrapper[] result = SimpleClass.CreateQuery().OData().SelectExpand("*").ToArray(); + + IDictionary metadata = result[0].ToDictionary(); + + Assert.Equal(7, metadata.Count); + } + + [Fact] + public void SelectName() + { + ISelectExpandWrapper[] result = SimpleClass.CreateQuery().OData().SelectExpand("Name").ToArray(); + + IDictionary metadata = result[0].ToDictionary(); + + // Expect Name to be selected + Assert.Equal(1, metadata.Count); + Assert.Equal("Name", metadata.Single().Key); + Assert.Equal("n1", metadata.Single().Value); + Assert.IsType(metadata.Single().Value); + } + + [Fact] + public void SelectDataMember() + { + ISelectExpandWrapper[] result = SimpleClassDataContract.CreateQuery().OData().SelectExpand("nameChanged").ToArray(); + + IDictionary metadata = result[0].ToDictionary(); + + // Expect Name to be selected + Assert.Equal(1, metadata.Count); + Assert.Equal("nameChanged", metadata.Single().Key); + Assert.Equal("n1", metadata.Single().Value); + Assert.IsType(metadata.Single().Value); + } + + [Fact] + public void SelectId() + { + ISelectExpandWrapper[] result = SimpleClass.CreateQuery().OData().SelectExpand("Id").ToArray(); + + IDictionary metadata = result[0].ToDictionary(); + + Assert.Equal(1, metadata.Count); + Assert.Equal("Id", metadata.Single().Key); + Assert.IsType(metadata.Single().Value); + Assert.Equal(1, metadata.Single().Value); + } + + [Fact] + public void SelectIdCaseInsensitive() + { + ISelectExpandWrapper[] result = SimpleClass.CreateQuery().OData().SelectExpand("id").ToArray(); + + IDictionary metadata = result[0].ToDictionary(); + + Assert.Equal(1, metadata.Count); + Assert.Equal("Id", metadata.Single().Key, StringComparer.Ordinal); + Assert.IsType(metadata.Single().Value); + Assert.Equal(1, metadata.Single().Value); + } + + [Fact] + public void SelectCaseSensitiveOnDemand() + { + Assert.Throws( + () => SimpleClass.CreateQuery().OData(s => s.EnableCaseInsensitive = false).SelectExpand("id")); + } + + [Fact] + public void SelectNotExistingProperty() + { + Assert.Throws( + () => SimpleClass.CreateQuery().OData().SelectExpand("asdcaefacfawrcfwrfaw4")); + } + + [Fact] + public void SelectNameToIgnore() + { + Assert.Throws( + () => SimpleClass.CreateQuery().OData().SelectExpand("NameToIgnore")); + } + + [Fact] + public void SelectDisabled() + { + ISelectExpandWrapper[] result = ClassWithLink.CreateQuery().OData().SelectExpand("Link4").ToArray(); + + IDictionary metadata = result[0].ToDictionary(); + + Assert.Equal(0, metadata.Count); + } + + [Fact] + public void SelectLinkWithoutExpandNotWorking() + { + ISelectExpandWrapper[] result = ClassWithLink.CreateQuery().OData().SelectExpand("Link1").ToArray(); + + IDictionary metadata = result[0].ToDictionary(); + + Assert.Equal(0, metadata.Count); + } + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNetCore.OData.Tests/Linq/TopSkipTests.cs b/test/Microsoft.AspNetCore.OData.Tests/Linq/TopSkipTests.cs new file mode 100644 index 000000000..355cb35eb --- /dev/null +++ b/test/Microsoft.AspNetCore.OData.Tests/Linq/TopSkipTests.cs @@ -0,0 +1,70 @@ +namespace Community.OData.Linq.xTests +{ + using System.Collections; + using System.Linq; + + using Community.OData.Linq.xTests.SampleData; + + using Microsoft.OData; + + using Xunit; + + public class TopSkipTests + { + [Theory] + [InlineData("1", null, 1, "n1")] + [InlineData("1", "0", 1, "n1")] + [InlineData(null, "1", 1, "n2")] + [InlineData(null, null, 2, "n1")] + [InlineData("0", "1", 0, null)] + [InlineData("0", "0", 0, null)] + public void TopSkip(string top, string skip, int count, string expectedName) + { + var result = SimpleClass.CreateQuery().OData().TopSkip(top, skip).ToArray(); + + Assert.Equal(count, result.Length); + if (count > 0) + { + Assert.Equal(expectedName, result[0].Name); + } + } + + [Theory] + [InlineData(null, 20)] + [InlineData(10, 10)] + [InlineData(30, 30)] + public void TopSkipDefaultPageSize(int? pageSize, int count) + { + var result = Enumerable.Repeat(new SimpleClass(), 1000).AsQueryable().OData(s => s.QuerySettings.PageSize = pageSize ?? s.QuerySettings.PageSize) + .TopSkip().ToArray(); + + Assert.Equal(count, result.Length); + } + + [Theory] + [InlineData("-1", "1", "Invalid value '-1' for $top query option found.")] + [InlineData("1", "-1", "Invalid value '-1' for $skip query option found.")] + [InlineData("1000000000000000", "1", "The limit of '2147483647' for Top query has been exceeded.")] + [InlineData("1", "1000000000000000", "The limit of '2147483647' for Skip query has been exceeded.")] + public void TopSkipValidation(string top, string skip, string expectedMessage) + { + var message = Assert.Throws(() => SimpleClass.CreateQuery().OData().TopSkip(top, skip)) + .Message; + Assert.Contains(expectedMessage, message); + } + + [Theory] + [InlineData("11", "1", "The limit of '10' for Top query has been exceeded.")] + [InlineData("1", "11", "The limit of '10' for Skip query has been exceeded.")] + public void TopSkipMax(string top, string skip, string expectedMessage) + { + var message = Assert.Throws(() => SimpleClass.CreateQuery().OData(s => + { + s.ValidationSettings.MaxTop = 10; + s.ValidationSettings.MaxSkip = 10; + }).TopSkip(top, skip)) + .Message; + Assert.Contains(expectedMessage, message); + } + } +} \ No newline at end of file From ba05b26eecfe7552ac6d780cbc0cc947659d447d Mon Sep 17 00:00:00 2001 From: Arnaud Boussaer Date: Fri, 7 Feb 2025 12:41:44 +0100 Subject: [PATCH 03/53] fix unit tests --- .../Linq/ExpandTests.cs | 10 +++++----- .../Microsoft.AspNetCore.OData.Tests/Linq/HashTests.cs | 6 +----- .../Linq/SampleData/SimpleClass.cs | 2 ++ .../Linq/SelectTests.cs | 4 ++-- 4 files changed, 10 insertions(+), 12 deletions(-) diff --git a/test/Microsoft.AspNetCore.OData.Tests/Linq/ExpandTests.cs b/test/Microsoft.AspNetCore.OData.Tests/Linq/ExpandTests.cs index 8667b1b1a..c32795fde 100644 --- a/test/Microsoft.AspNetCore.OData.Tests/Linq/ExpandTests.cs +++ b/test/Microsoft.AspNetCore.OData.Tests/Linq/ExpandTests.cs @@ -41,7 +41,7 @@ public void ExpandLink() // Not expanded by default Assert.Equal(3, metadata.Count); - Assert.Equal(7, (metadata["Link1"] as ISelectExpandWrapper).ToDictionary().Count); + Assert.Equal(SimpleClass.NumberOfProperties, (metadata["Link1"] as ISelectExpandWrapper).ToDictionary().Count); } [Fact] @@ -53,7 +53,7 @@ public void ExpandSelect() // Not expanded by default Assert.Equal(2, metadata.Count); - Assert.Equal(7, (metadata["Link1"] as ISelectExpandWrapper).ToDictionary().Count); + Assert.Equal(SimpleClass.NumberOfProperties, (metadata["Link1"] as ISelectExpandWrapper).ToDictionary().Count); } [Fact] @@ -155,8 +155,8 @@ public void ExpandWithAttributes() // Not expanded by default Assert.Equal(4, metadata.Count); - Assert.Equal(7, (metadata["AutoExpandLink"] as ISelectExpandWrapper).ToDictionary().Count); - Assert.Equal(7, (metadata["AutoExpandAndSelectLink"] as ISelectExpandWrapper).ToDictionary().Count); + Assert.Equal(SimpleClass.NumberOfProperties, (metadata["AutoExpandLink"] as ISelectExpandWrapper).ToDictionary().Count); + Assert.Equal(SimpleClass.NumberOfProperties, (metadata["AutoExpandAndSelectLink"] as ISelectExpandWrapper).ToDictionary().Count); } [Fact] @@ -169,7 +169,7 @@ public void ExpandWithAttributesAndExplicit() // Not expanded by default Assert.Equal(3, metadata.Count); - Assert.Equal(7, (metadata["AutoExpandAndSelectLink"] as ISelectExpandWrapper).ToDictionary().Count); + Assert.Equal(SimpleClass.NumberOfProperties, (metadata["AutoExpandAndSelectLink"] as ISelectExpandWrapper).ToDictionary().Count); } [Fact] diff --git a/test/Microsoft.AspNetCore.OData.Tests/Linq/HashTests.cs b/test/Microsoft.AspNetCore.OData.Tests/Linq/HashTests.cs index 83504f23c..b9ea33b97 100644 --- a/test/Microsoft.AspNetCore.OData.Tests/Linq/HashTests.cs +++ b/test/Microsoft.AspNetCore.OData.Tests/Linq/HashTests.cs @@ -1,8 +1,4 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using Xunit; @@ -10,7 +6,7 @@ namespace Community.OData.Linq.xTests { public class HashTests { - [Fact] + [Fact(Skip = "Fails because the custom hash methods are not present in the original OData classes")] public void Hashes() { ODataSettings s1 = new ODataSettings(); diff --git a/test/Microsoft.AspNetCore.OData.Tests/Linq/SampleData/SimpleClass.cs b/test/Microsoft.AspNetCore.OData.Tests/Linq/SampleData/SimpleClass.cs index 921d5e6c0..8f5e9b944 100644 --- a/test/Microsoft.AspNetCore.OData.Tests/Linq/SampleData/SimpleClass.cs +++ b/test/Microsoft.AspNetCore.OData.Tests/Linq/SampleData/SimpleClass.cs @@ -9,6 +9,8 @@ namespace Community.OData.Linq.xTests.SampleData public class SimpleClass { + public const int NumberOfProperties = 9; + private static readonly SimpleClass[] items = { new SimpleClass { diff --git a/test/Microsoft.AspNetCore.OData.Tests/Linq/SelectTests.cs b/test/Microsoft.AspNetCore.OData.Tests/Linq/SelectTests.cs index d79bd2b3b..b73636de7 100644 --- a/test/Microsoft.AspNetCore.OData.Tests/Linq/SelectTests.cs +++ b/test/Microsoft.AspNetCore.OData.Tests/Linq/SelectTests.cs @@ -17,7 +17,7 @@ public void SelectDefault() IDictionary metadata = result[0].ToDictionary(); - Assert.Equal(7, metadata.Count); + Assert.Equal(SimpleClass.NumberOfProperties, metadata.Count); } [Fact] @@ -27,7 +27,7 @@ public void SelectAllt() IDictionary metadata = result[0].ToDictionary(); - Assert.Equal(7, metadata.Count); + Assert.Equal(SimpleClass.NumberOfProperties, metadata.Count); } [Fact] From b7fa5293f6dff4214b8eabea61334eed0c4d01e8 Mon Sep 17 00:00:00 2001 From: Arnaud Boussaer Date: Fri, 7 Feb 2025 12:59:17 +0100 Subject: [PATCH 04/53] add unit tests linked to GH issues --- .../Linq/Issues/Issue29.cs | 133 ++++++++++++++++++ .../Linq/Issues/Issue31.cs | 19 +++ .../Linq/Issues/Issue32.cs | 105 ++++++++++++++ .../Linq/Issues/Issue33.cs | 93 ++++++++++++ 4 files changed, 350 insertions(+) create mode 100644 test/Microsoft.AspNetCore.OData.Tests/Linq/Issues/Issue29.cs create mode 100644 test/Microsoft.AspNetCore.OData.Tests/Linq/Issues/Issue31.cs create mode 100644 test/Microsoft.AspNetCore.OData.Tests/Linq/Issues/Issue32.cs create mode 100644 test/Microsoft.AspNetCore.OData.Tests/Linq/Issues/Issue33.cs diff --git a/test/Microsoft.AspNetCore.OData.Tests/Linq/Issues/Issue29.cs b/test/Microsoft.AspNetCore.OData.Tests/Linq/Issues/Issue29.cs new file mode 100644 index 000000000..f993b60b6 --- /dev/null +++ b/test/Microsoft.AspNetCore.OData.Tests/Linq/Issues/Issue29.cs @@ -0,0 +1,133 @@ +//using Newtonsoft.Json; +//using System; +//using System.Collections.Generic; +//using System.ComponentModel.DataAnnotations; +//using System.Linq; +//using System.Text; +//using Xunit; +//using Community.OData.Linq.Json; + +//namespace Community.OData.Linq.xTests.Issues29 +//{ +// using Microsoft.AspNetCore.OData.Query.Wrapper; +// using System.Diagnostics; + +// class MyClassA +// { +// [Key] +// public string String { get; set; } + +// public List Subs { get; set; } +// } + +// class MyClassB +// { +// [Key] +// public int Integer { get; set; } +// } + +// public class Issue29 +// { +// [Fact] +// public void SubLevelFilterTest() +// { +// ODataQuery odataQuery = GetSampleData().OData(); +// odataQuery = odataQuery.Filter("String eq 'A'"); + +// // This is what I used until now; good result +// string serializeCorrect = JsonConvert.SerializeObject(odataQuery); + +// IEnumerable collection = GetSampleData().OData().Filter("String eq 'A'").SelectExpand("String", "Subs($filter=Integer eq 1)"); +// string result1 = collection.ToJson().ToString(); + +// MyClassA[] array = GetSampleData().OData().Filter("String eq 'A'").ToArray(); +// foreach (MyClassA item in array) +// { +// item.Subs = item.Subs.AsQueryable().OData().Filter("Integer eq 1").ToList(); +// } + +// string result2 = JsonConvert.SerializeObject(array); +// } + +// private static IQueryable GetSampleData() +// { +// return new List() +// { +// new MyClassA() +// { +// String = "A", +// Subs = new List() {{new MyClassB() {Integer = 1}}, {new MyClassB() {Integer = 2}}, {new MyClassB() {Integer = 3}}, {new MyClassB() {Integer = 4}}} +// }, +// new MyClassA() +// { +// String = "B", +// Subs = new List() {{new MyClassB() {Integer = 1}}, {new MyClassB() {Integer = 2}}, {new MyClassB() {Integer = 3}}, {new MyClassB() {Integer = 4}}} +// } +// }.AsQueryable(); +// } + +// private static IQueryable GetBigSampleData() +// { +// var result = Enumerable.Range(1, 2000).Select( +// i => new MyClassA +// { +// String = i <= 1000 ? "A" : "B", +// Subs = Enumerable.Range(1, 1000).Select(j => new MyClassB { Integer = j }) +// .ToList() +// }); + +// return result.AsQueryable(); +// } + +// [Fact] +// public void FilterMany1() +// { +// Stopwatch stopwatch = new Stopwatch(); +// stopwatch.Start(); +// ISelectExpandWrapper[] a1000b1 = GetBigSampleData().OData().Filter("String eq 'A'").SelectExpand("String", "Subs($filter=Integer eq 1)").ToArray(); + +// Assert.True(stopwatch.ElapsedMilliseconds < 5000, "query performance"); +// Assert.Equal(1000, a1000b1.Length); + + +// string a1000b1Json = a1000b1.ToJson().ToString(); +// Assert.NotNull(a1000b1Json); + +// Assert.True(stopwatch.ElapsedMilliseconds < 10000, "to json performance"); +// stopwatch.Stop(); +// } + +// [Fact] +// public void FilterMany3() +// { +// Stopwatch stopwatch = new Stopwatch(); +// stopwatch.Start(); +// IEnumerable a1000b1 = GetBigSampleData().OData().Filter("String eq 'A'").SelectExpand("String", "Subs($filter=Integer eq 1)"); + +// Assert.True(stopwatch.ElapsedMilliseconds < 5000, "query performance"); + +// string a1000b1Json = a1000b1.ToJson().ToString(); +// Assert.NotNull(a1000b1Json); + +// Assert.True(stopwatch.ElapsedMilliseconds < 10000, "to json performance"); +// stopwatch.Stop(); +// } + +// [Fact] +// public void FilterMany2() +// { +// Stopwatch stopwatch = new Stopwatch(); +// stopwatch.Start(); +// ISelectExpandWrapper[] a1000b1000 = GetBigSampleData().OData().SelectExpand("String", "Subs").ToArray(); + +// Assert.True(stopwatch.ElapsedMilliseconds < 5000, "query performance"); +// Assert.Equal(2000, a1000b1000.Length); + + +// string a1000b1000Json = a1000b1000.ToJson().ToString(); +// Assert.NotNull(a1000b1000Json); +// Assert.True(stopwatch.ElapsedMilliseconds < 10000, "to json performance"); +// stopwatch.Stop(); +// } +// } +//} diff --git a/test/Microsoft.AspNetCore.OData.Tests/Linq/Issues/Issue31.cs b/test/Microsoft.AspNetCore.OData.Tests/Linq/Issues/Issue31.cs new file mode 100644 index 000000000..bd2817467 --- /dev/null +++ b/test/Microsoft.AspNetCore.OData.Tests/Linq/Issues/Issue31.cs @@ -0,0 +1,19 @@ + +namespace Community.OData.Linq.xTests.Issues +{ + using Community.OData.Linq.xTests.SampleData; + using System.Linq; + using Xunit; + + public class Issue31 + { + [Fact] + public void WhereWithInThrowException() + { + var result = SimpleClass.CreateQuery().OData().Filter($"{nameof(SimpleClass.Id)} in (1,100)").ToArray(); + + Assert.Collection(result, + e => Assert.InRange(e.Id, 1, 100)); + } + } +} diff --git a/test/Microsoft.AspNetCore.OData.Tests/Linq/Issues/Issue32.cs b/test/Microsoft.AspNetCore.OData.Tests/Linq/Issues/Issue32.cs new file mode 100644 index 000000000..14da4ce78 --- /dev/null +++ b/test/Microsoft.AspNetCore.OData.Tests/Linq/Issues/Issue32.cs @@ -0,0 +1,105 @@ +//namespace Community.OData.Linq.xTests.Issues29 +//{ +// using System; +// using System.Linq; +// using System.Collections.Generic; +// using Newtonsoft.Json; +// using Community.OData.Linq; // V1.4.2 +// using Community.OData.Linq.Json; // V1.4.2 +// using Xunit; + +// /*********************************************************************************************/ +// /********************************** START Model Declaration **********************************/ +// /*********************************************************************************************/ + +// public partial class URLT +// { +// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")] +// public URLT() +// { +// this.DA = new HashSet(); +// } + +// public long URLTID { get; set; } +// public string URLTN { get; set; } +// public bool isDeleted { get; set; } + +// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")] +// public virtual ICollection DA { get; set; } +// } + +// public partial class DA +// { +// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")] +// public DA() +// { +// this.PDA = new HashSet(); +// } + +// public long DAID { get; set; } +// public long URLTID { get; set; } +// public bool isDeleted { get; set; } + +// public virtual URLT URLT { get; set; } +// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")] +// public virtual ICollection PDA { get; set; } +// } + +// public partial class PDA +// { +// public long PDAID { get; set; } +// public long DAID { get; set; } +// public bool isDeleted { get; set; } +// public long? nullTest { get; set; } + +// public virtual DA DA { get; set; } +// } + +// /*********************************************************************************************/ +// /********************************** END Model Declaration ************************************/ +// /*********************************************************************************************/ + +// public class Issue32 +// { +// [Fact] +// public void Expand() +// { +// // Expand - deep level 2 +// URLT URLTitem = new URLT { URLTID = 1, URLTN = "Test", isDeleted = false }; + +// // Expand - deep level 1 +// DA DAitem = new DA { DAID = 1, URLT = URLTitem, isDeleted = false }; + +// // Root Level +// PDA[] items = +// { +// new PDA { PDAID = 1, DA = DAitem, isDeleted = false, nullTest = null }, +// new PDA { PDAID = 2, isDeleted = false, nullTest = null }, +// new PDA { PDAID = 3, isDeleted = true, nullTest = null } +// }; + +// // Filter working fine at root level +// string filter = "(PDAID eq 1 or PDAID eq 3) and isDeleted eq false and DA/URLT/isDeleted eq true"; // Filter root - Case 1 +// //string filter = "(PDAID eq 1 or PDAID eq 3) and isDeleted eq true"; // Filter root - Case 2 +// string orderBy = "PDAID desc"; +// string selectStr = "PDAID,IsDeleted,nullTest"; +// // Filter inside expand isn't working all the time +// // to replicate please use the following cases +// //string ExpandStr = "DA($select=DAID,isDeleted;$Filter=isDeleted eq false;$expand=URLT($Select=*;$filter=isDeleted eq false))"; // Case 1 - Expand Filter working fine (Aparently) +// string ExpandStr = "DA($select=DAID,isDeleted;$Filter=isDeleted eq false;$expand=URLT($Select=*;$Filter=isDeleted eq true))"; // Case 2 - Expand Filter not working +// // string ExpandStr = "DA($select=DAID,isDeleted;$Filter=isDeleted eq true;$expand=URLT($Select=*;$filter=isDeleted eq true))"; // Case 3 - Expand Filter not working fine +// // string ExpandStr = "DA($select=DAID,isDeleted;$Filter=isDeleted eq true;$expand=URLT($Select=*;$filter=URLTN eq 'a'))"; // Case 4 - Expand Filter not working fine on diferent DataTypes + +// ODataQuery query = items.AsQueryable().OData(); + +// ODataSettings settings = (ODataSettings)query.ServiceProvider.GetService(typeof(ODataSettings)); +// Assert.True(settings.EnableCaseInsensitive); + +// var result = query.OData().Filter(filter).OrderBy(orderBy).SelectExpand(selectStr, ExpandStr).ToJson(settings => settings.NullValueHandling = NullValueHandling.Ignore); +// string str = result.ToString(); +// Console.WriteLine(str); +// Console.WriteLine(Environment.NewLine); + +// } +// } +//} \ No newline at end of file diff --git a/test/Microsoft.AspNetCore.OData.Tests/Linq/Issues/Issue33.cs b/test/Microsoft.AspNetCore.OData.Tests/Linq/Issues/Issue33.cs new file mode 100644 index 000000000..bfc00a5ee --- /dev/null +++ b/test/Microsoft.AspNetCore.OData.Tests/Linq/Issues/Issue33.cs @@ -0,0 +1,93 @@ +// Issue 33 : Recursive loop of complex types + +namespace Community.OData.Linq.xTests.Issues33 +{ + using System; + using System.Linq; + using Xunit; + + public class RecursiveComplexType + { + public RecursiveComplexType SelfReference { get; set; } + } + + public class ListItem + { + public int Id { get; set; } + + public RecursiveComplexType RecursiveComplexType { get; set; } + } + + public class Issue33 + { + [Fact(Skip = "Not supported anymore")] + public void Recursive_Loops_Must_Not_Be_Allowed_By_Default() + { + // arrange + + var queryable = new ListItem[] + { + new ListItem { Id = 1, RecursiveComplexType = new RecursiveComplexType() }, + new ListItem { Id = 2, RecursiveComplexType = new RecursiveComplexType() } + }.AsQueryable(); + + // act + + var exception = Assert.Throws(() => queryable.OData().Filter("Id eq 1").ToArray()); + + // assert + + Assert.Contains("recursive loop of complex types is not allowed", exception.Message); + } + + [Fact] + public void Recursive_Loops_Must_Be_Allowed_If_Opted_Into_By_Inline_Configuration() + { + // arrange + + var queryable = new ListItem[] + { + new ListItem { Id = 1, RecursiveComplexType = new RecursiveComplexType() }, + new ListItem { Id = 2, RecursiveComplexType = new RecursiveComplexType() } + }.AsQueryable(); + + // act + + var result = queryable.OData(x => + { + x.AllowRecursiveLoopOfComplexTypes = true; + + }).Filter("Id eq 1").ToArray(); + + // assert + + Assert.Single(result); + } + + [Fact] + public void Recursive_Loops_Must_Be_Allowed_If_Opted_Into_By_Global_Configuration() + { + // arrange + + var queryable = new ListItem[] + { + new ListItem { Id = 1, RecursiveComplexType = new RecursiveComplexType() }, + new ListItem { Id = 2, RecursiveComplexType = new RecursiveComplexType() } + }.AsQueryable(); + + ODataSettings.SetInitializer(x => + { + x.AllowRecursiveLoopOfComplexTypes = true; + + }); + + // act + + var result = queryable.OData().Filter("Id eq 1").ToArray(); + + // assert + + Assert.Single(result); + } + } +} From f44020abecd42e0ba47bb57af2866b64486a8a47 Mon Sep 17 00:00:00 2001 From: Arnaud Boussaer Date: Fri, 7 Feb 2025 14:36:01 +0100 Subject: [PATCH 05/53] remove comments --- .../Linq/ODataLinqExtensions.cs | 11 ----------- src/Microsoft.AspNetCore.OData/Linq/ODataQuery.cs | 4 ---- src/Microsoft.AspNetCore.OData/Linq/ODataSettings.cs | 4 ---- .../Linq/SelectExpandHelper.cs | 3 --- 4 files changed, 22 deletions(-) diff --git a/src/Microsoft.AspNetCore.OData/Linq/ODataLinqExtensions.cs b/src/Microsoft.AspNetCore.OData/Linq/ODataLinqExtensions.cs index d1b7730f2..feee15d82 100644 --- a/src/Microsoft.AspNetCore.OData/Linq/ODataLinqExtensions.cs +++ b/src/Microsoft.AspNetCore.OData/Linq/ODataLinqExtensions.cs @@ -6,13 +6,6 @@ using Microsoft.AspNetCore.OData.Query.Expressions; using Microsoft.AspNetCore.OData.Query.Validator; using Microsoft.AspNetCore.OData.Query.Wrapper; - //using Community.OData.Linq.Builder; - //using Community.OData.Linq.Builder.Validators; - //using Community.OData.Linq.Common; - //using Community.OData.Linq.OData; - //using Community.OData.Linq.OData.Query; - //using Community.OData.Linq.OData.Query.Expressions; - //using Community.OData.Linq.Properties; using Microsoft.Extensions.DependencyInjection; using Microsoft.OData; @@ -27,10 +20,6 @@ using System.Diagnostics.Contracts; using System.Linq; - /// - /// Entry class - /// Hier zo veel mogelijk originele referenties gebruiken - /// public static class ODataLinqExtensions { /// diff --git a/src/Microsoft.AspNetCore.OData/Linq/ODataQuery.cs b/src/Microsoft.AspNetCore.OData/Linq/ODataQuery.cs index 46d393004..85de9106e 100644 --- a/src/Microsoft.AspNetCore.OData/Linq/ODataQuery.cs +++ b/src/Microsoft.AspNetCore.OData/Linq/ODataQuery.cs @@ -8,10 +8,6 @@ using System.Linq; using System.Linq.Expressions; - /// - /// Ook gebruikt - /// - /// public class ODataQuery : IQueryable { internal ODataQuery(IQueryable inner, IServiceProvider serviceProvider) diff --git a/src/Microsoft.AspNetCore.OData/Linq/ODataSettings.cs b/src/Microsoft.AspNetCore.OData/Linq/ODataSettings.cs index fa082d4ca..6dc5127f5 100644 --- a/src/Microsoft.AspNetCore.OData/Linq/ODataSettings.cs +++ b/src/Microsoft.AspNetCore.OData/Linq/ODataSettings.cs @@ -3,13 +3,9 @@ using Microsoft.AspNetCore.OData.Query; using Microsoft.AspNetCore.OData.Query.Validator; using Microsoft.OData.ModelBuilder.Config; - //using Community.OData.Linq.OData.Query; using Microsoft.OData.UriParser; using System; - /// - /// Ook gebruikt - /// public class ODataSettings { private static readonly object SyncObj = new object(); diff --git a/src/Microsoft.AspNetCore.OData/Linq/SelectExpandHelper.cs b/src/Microsoft.AspNetCore.OData/Linq/SelectExpandHelper.cs index b0f8544d6..0e21ab1c2 100644 --- a/src/Microsoft.AspNetCore.OData/Linq/SelectExpandHelper.cs +++ b/src/Microsoft.AspNetCore.OData/Linq/SelectExpandHelper.cs @@ -2,9 +2,6 @@ { using Microsoft.AspNetCore.OData.Edm; using Microsoft.AspNetCore.OData.Query; - //using Community.OData.Linq.OData.Formatter; - //using Community.OData.Linq.OData.Query; - using Microsoft.Extensions.DependencyInjection; using Microsoft.OData.Edm; using Microsoft.OData.UriParser; From b3a95804eae9138f95319a8dd32b04f4b07fa63e Mon Sep 17 00:00:00 2001 From: Arnaud Boussaer Date: Fri, 7 Feb 2025 15:07:35 +0100 Subject: [PATCH 06/53] update xml --- .../Microsoft.AspNetCore.OData.xml | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/src/Microsoft.AspNetCore.OData/Microsoft.AspNetCore.OData.xml b/src/Microsoft.AspNetCore.OData/Microsoft.AspNetCore.OData.xml index 2f3bf1d9b..43633c7f8 100644 --- a/src/Microsoft.AspNetCore.OData/Microsoft.AspNetCore.OData.xml +++ b/src/Microsoft.AspNetCore.OData/Microsoft.AspNetCore.OData.xml @@ -14988,12 +14988,6 @@ - - - Entry class - Hier zo veel mogelijk originele referenties gebruiken - - The simplified options. @@ -15152,17 +15146,6 @@ Argument Null Exception - - - Ook gebruikt - - - - - - Ook gebruikt - - Sets the action which will be used to initialize every instance of . From 39548597abd74c4f801f331dbf6e8ac9be2d37df Mon Sep 17 00:00:00 2001 From: Arnaud Boussaer Date: Fri, 7 Feb 2025 15:56:41 +0100 Subject: [PATCH 07/53] Move added code to separate project as friendly assembly --- AspNetCoreOData.sln | 16 +- OData2Linq/AssemblyInfo.cs | 17 ++ OData2Linq/FriendAssemblies.snk | Bin 0 -> 596 bytes OData2Linq/OData2Linq.csproj | 16 ++ .../ODataLinqExtensions.cs | 11 ++ .../Linq => OData2Linq}/ODataQuery.cs | 4 + .../Linq => OData2Linq}/ODataQueryOrdered.cs | 0 .../Linq => OData2Linq}/ODataSettings.cs | 4 + .../Linq => OData2Linq}/SelectExpandHelper.cs | 3 + .../Linq => OData2Linq}/TopSkipHelper.cs | 0 .../Microsoft.AspNetCore.OData.xml | 172 ------------------ .../Properties/AssemblyInfo.cs | 2 + .../DateTimeFilterTests.cs | 0 .../Linq => OData2Linq.Tests}/ExpandTests.cs | 0 .../FilterDataContractTests.cs | 0 .../FilterFunctionsTests.cs | 0 .../FilterNavigationCollectionTests.cs | 0 .../FilterNavigationLinkTests.cs | 0 .../Linq => OData2Linq.Tests}/FilterTests.cs | 0 .../Linq => OData2Linq.Tests}/HashTests.cs | 0 .../Issues/Issue29.cs | 0 .../Issues/Issue31.cs | 0 .../Issues/Issue32.cs | 0 .../Issues/Issue33.cs | 0 .../Linq => OData2Linq.Tests}/JsonTests.cs | 0 test/OData2Linq.Tests/OData2Linq.Tests.csproj | 21 +++ .../Linq => OData2Linq.Tests}/ODataTests.cs | 0 .../OpenTypesTests.cs | 0 .../Linq => OData2Linq.Tests}/OrderByTests.cs | 0 .../SampleData/ClassWithCollection.cs | 0 .../SampleData/ClassWithDeepNavigation.cs | 0 .../SampleData/ClassWithLink.cs | 0 .../SampleData/OpenType.cs | 0 .../SampleData/SampleWithCustomKey.cs | 0 .../SampleData/SampleWithoutKey.cs | 0 .../SampleData/SimpleClass.cs | 0 .../SampleData/SimpleClassDataContract.cs | 0 .../SampleData/TestEnum.cs | 0 .../SampleData/TestItem.cs | 0 .../Linq => OData2Linq.Tests}/SelectTests.cs | 0 .../Linq => OData2Linq.Tests}/TopSkipTests.cs | 0 41 files changed, 93 insertions(+), 173 deletions(-) create mode 100644 OData2Linq/AssemblyInfo.cs create mode 100644 OData2Linq/FriendAssemblies.snk create mode 100644 OData2Linq/OData2Linq.csproj rename {src/Microsoft.AspNetCore.OData/Linq => OData2Linq}/ODataLinqExtensions.cs (98%) rename {src/Microsoft.AspNetCore.OData/Linq => OData2Linq}/ODataQuery.cs (92%) rename {src/Microsoft.AspNetCore.OData/Linq => OData2Linq}/ODataQueryOrdered.cs (100%) rename {src/Microsoft.AspNetCore.OData/Linq => OData2Linq}/ODataSettings.cs (96%) rename {src/Microsoft.AspNetCore.OData/Linq => OData2Linq}/SelectExpandHelper.cs (98%) rename {src/Microsoft.AspNetCore.OData/Linq => OData2Linq}/TopSkipHelper.cs (100%) rename test/{Microsoft.AspNetCore.OData.Tests/Linq => OData2Linq.Tests}/DateTimeFilterTests.cs (100%) rename test/{Microsoft.AspNetCore.OData.Tests/Linq => OData2Linq.Tests}/ExpandTests.cs (100%) rename test/{Microsoft.AspNetCore.OData.Tests/Linq => OData2Linq.Tests}/FilterDataContractTests.cs (100%) rename test/{Microsoft.AspNetCore.OData.Tests/Linq => OData2Linq.Tests}/FilterFunctionsTests.cs (100%) rename test/{Microsoft.AspNetCore.OData.Tests/Linq => OData2Linq.Tests}/FilterNavigationCollectionTests.cs (100%) rename test/{Microsoft.AspNetCore.OData.Tests/Linq => OData2Linq.Tests}/FilterNavigationLinkTests.cs (100%) rename test/{Microsoft.AspNetCore.OData.Tests/Linq => OData2Linq.Tests}/FilterTests.cs (100%) rename test/{Microsoft.AspNetCore.OData.Tests/Linq => OData2Linq.Tests}/HashTests.cs (100%) rename test/{Microsoft.AspNetCore.OData.Tests/Linq => OData2Linq.Tests}/Issues/Issue29.cs (100%) rename test/{Microsoft.AspNetCore.OData.Tests/Linq => OData2Linq.Tests}/Issues/Issue31.cs (100%) rename test/{Microsoft.AspNetCore.OData.Tests/Linq => OData2Linq.Tests}/Issues/Issue32.cs (100%) rename test/{Microsoft.AspNetCore.OData.Tests/Linq => OData2Linq.Tests}/Issues/Issue33.cs (100%) rename test/{Microsoft.AspNetCore.OData.Tests/Linq => OData2Linq.Tests}/JsonTests.cs (100%) create mode 100644 test/OData2Linq.Tests/OData2Linq.Tests.csproj rename test/{Microsoft.AspNetCore.OData.Tests/Linq => OData2Linq.Tests}/ODataTests.cs (100%) rename test/{Microsoft.AspNetCore.OData.Tests/Linq => OData2Linq.Tests}/OpenTypesTests.cs (100%) rename test/{Microsoft.AspNetCore.OData.Tests/Linq => OData2Linq.Tests}/OrderByTests.cs (100%) rename test/{Microsoft.AspNetCore.OData.Tests/Linq => OData2Linq.Tests}/SampleData/ClassWithCollection.cs (100%) rename test/{Microsoft.AspNetCore.OData.Tests/Linq => OData2Linq.Tests}/SampleData/ClassWithDeepNavigation.cs (100%) rename test/{Microsoft.AspNetCore.OData.Tests/Linq => OData2Linq.Tests}/SampleData/ClassWithLink.cs (100%) rename test/{Microsoft.AspNetCore.OData.Tests/Linq => OData2Linq.Tests}/SampleData/OpenType.cs (100%) rename test/{Microsoft.AspNetCore.OData.Tests/Linq => OData2Linq.Tests}/SampleData/SampleWithCustomKey.cs (100%) rename test/{Microsoft.AspNetCore.OData.Tests/Linq => OData2Linq.Tests}/SampleData/SampleWithoutKey.cs (100%) rename test/{Microsoft.AspNetCore.OData.Tests/Linq => OData2Linq.Tests}/SampleData/SimpleClass.cs (100%) rename test/{Microsoft.AspNetCore.OData.Tests/Linq => OData2Linq.Tests}/SampleData/SimpleClassDataContract.cs (100%) rename test/{Microsoft.AspNetCore.OData.Tests/Linq => OData2Linq.Tests}/SampleData/TestEnum.cs (100%) rename test/{Microsoft.AspNetCore.OData.Tests/Linq => OData2Linq.Tests}/SampleData/TestItem.cs (100%) rename test/{Microsoft.AspNetCore.OData.Tests/Linq => OData2Linq.Tests}/SelectTests.cs (100%) rename test/{Microsoft.AspNetCore.OData.Tests/Linq => OData2Linq.Tests}/TopSkipTests.cs (100%) diff --git a/AspNetCoreOData.sln b/AspNetCoreOData.sln index a2771489b..69cff4667 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.3.32901.215 +VisualStudioVersion = 17.12.35707.178 d17.12 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{2F0E102B-EB33-4025-BE56-7B8F9D2C4B8A}" EndProject @@ -27,6 +27,10 @@ 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}") = "OData2Linq", "OData2Linq\OData2Linq.csproj", "{F339CB11-F64A-4497-B978-839D97FCE3F5}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OData2Linq.Tests", "test\OData2Linq.Tests\OData2Linq.Tests.csproj", "{E7F48129-7544-4D85-ABE1-4CB037E1C780}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -69,6 +73,14 @@ 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 + {F339CB11-F64A-4497-B978-839D97FCE3F5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F339CB11-F64A-4497-B978-839D97FCE3F5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F339CB11-F64A-4497-B978-839D97FCE3F5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F339CB11-F64A-4497-B978-839D97FCE3F5}.Release|Any CPU.Build.0 = Release|Any CPU + {E7F48129-7544-4D85-ABE1-4CB037E1C780}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E7F48129-7544-4D85-ABE1-4CB037E1C780}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E7F48129-7544-4D85-ABE1-4CB037E1C780}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E7F48129-7544-4D85-ABE1-4CB037E1C780}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -83,6 +95,8 @@ 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} + {F339CB11-F64A-4497-B978-839D97FCE3F5} = {2F0E102B-EB33-4025-BE56-7B8F9D2C4B8A} + {E7F48129-7544-4D85-ABE1-4CB037E1C780} = {F994C269-55BA-44F0-9DA7-6D5A3CFA79EB} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {540C9752-AAC0-49EA-BA60-78490C90FF86} diff --git a/OData2Linq/AssemblyInfo.cs b/OData2Linq/AssemblyInfo.cs new file mode 100644 index 000000000..6fa618d25 --- /dev/null +++ b/OData2Linq/AssemblyInfo.cs @@ -0,0 +1,17 @@ +using System.Runtime.InteropServices; + +// In SDK-style projects such as this one, several assembly attributes that were historically +// defined in this file are now automatically added during build and populated with +// values defined in project properties. For details of which attributes are included +// and how to customise this process see: https://aka.ms/assembly-info-properties + + +// Setting ComVisible to false makes the types in this assembly not visible to COM +// components. If you need to access a type in this assembly from COM, set the ComVisible +// attribute to true on that type. + +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM. + +[assembly: Guid("90adb1c3-e4aa-4250-b22b-1158576ae4d0")] diff --git a/OData2Linq/FriendAssemblies.snk b/OData2Linq/FriendAssemblies.snk new file mode 100644 index 0000000000000000000000000000000000000000..57a497086ba482582a43fb3eaf356e38d83b85ba GIT binary patch literal 596 zcmV-a0;~N80ssI2Bme+XQ$aES1ONa50097CsOuId2q-tf*5S04COihAm$8G*1 zqC|T)?Q=Q%kZlOqN<_VnS1|C8lKM2V{TrF| zv6P*QeGjpi<}s!)M!#HUwSp)~KJVhA?~b}0djAcBP=VC}teNaBz8-{VG3hLeW@8Dv z)m1-?8yZe6elby0#Lp`AJUtJpcz~sBPPs$$@maP3N34Z0k32xUeBby8Jjs i5crbZ(8M_TD?*ej^3Jm_iW9H)A+QAk{_&-}CGA4O=q9@W literal 0 HcmV?d00001 diff --git a/OData2Linq/OData2Linq.csproj b/OData2Linq/OData2Linq.csproj new file mode 100644 index 000000000..091b3716a --- /dev/null +++ b/OData2Linq/OData2Linq.csproj @@ -0,0 +1,16 @@ + + + + net6.0 + enable + enable + True + FriendAssemblies.snk + True + + + + + + + diff --git a/src/Microsoft.AspNetCore.OData/Linq/ODataLinqExtensions.cs b/OData2Linq/ODataLinqExtensions.cs similarity index 98% rename from src/Microsoft.AspNetCore.OData/Linq/ODataLinqExtensions.cs rename to OData2Linq/ODataLinqExtensions.cs index feee15d82..d1b7730f2 100644 --- a/src/Microsoft.AspNetCore.OData/Linq/ODataLinqExtensions.cs +++ b/OData2Linq/ODataLinqExtensions.cs @@ -6,6 +6,13 @@ using Microsoft.AspNetCore.OData.Query.Expressions; using Microsoft.AspNetCore.OData.Query.Validator; using Microsoft.AspNetCore.OData.Query.Wrapper; + //using Community.OData.Linq.Builder; + //using Community.OData.Linq.Builder.Validators; + //using Community.OData.Linq.Common; + //using Community.OData.Linq.OData; + //using Community.OData.Linq.OData.Query; + //using Community.OData.Linq.OData.Query.Expressions; + //using Community.OData.Linq.Properties; using Microsoft.Extensions.DependencyInjection; using Microsoft.OData; @@ -20,6 +27,10 @@ using System.Diagnostics.Contracts; using System.Linq; + /// + /// Entry class + /// Hier zo veel mogelijk originele referenties gebruiken + /// public static class ODataLinqExtensions { /// diff --git a/src/Microsoft.AspNetCore.OData/Linq/ODataQuery.cs b/OData2Linq/ODataQuery.cs similarity index 92% rename from src/Microsoft.AspNetCore.OData/Linq/ODataQuery.cs rename to OData2Linq/ODataQuery.cs index 85de9106e..46d393004 100644 --- a/src/Microsoft.AspNetCore.OData/Linq/ODataQuery.cs +++ b/OData2Linq/ODataQuery.cs @@ -8,6 +8,10 @@ using System.Linq; using System.Linq.Expressions; + /// + /// Ook gebruikt + /// + /// public class ODataQuery : IQueryable { internal ODataQuery(IQueryable inner, IServiceProvider serviceProvider) diff --git a/src/Microsoft.AspNetCore.OData/Linq/ODataQueryOrdered.cs b/OData2Linq/ODataQueryOrdered.cs similarity index 100% rename from src/Microsoft.AspNetCore.OData/Linq/ODataQueryOrdered.cs rename to OData2Linq/ODataQueryOrdered.cs diff --git a/src/Microsoft.AspNetCore.OData/Linq/ODataSettings.cs b/OData2Linq/ODataSettings.cs similarity index 96% rename from src/Microsoft.AspNetCore.OData/Linq/ODataSettings.cs rename to OData2Linq/ODataSettings.cs index 6dc5127f5..fa082d4ca 100644 --- a/src/Microsoft.AspNetCore.OData/Linq/ODataSettings.cs +++ b/OData2Linq/ODataSettings.cs @@ -3,9 +3,13 @@ using Microsoft.AspNetCore.OData.Query; using Microsoft.AspNetCore.OData.Query.Validator; using Microsoft.OData.ModelBuilder.Config; + //using Community.OData.Linq.OData.Query; using Microsoft.OData.UriParser; using System; + /// + /// Ook gebruikt + /// public class ODataSettings { private static readonly object SyncObj = new object(); diff --git a/src/Microsoft.AspNetCore.OData/Linq/SelectExpandHelper.cs b/OData2Linq/SelectExpandHelper.cs similarity index 98% rename from src/Microsoft.AspNetCore.OData/Linq/SelectExpandHelper.cs rename to OData2Linq/SelectExpandHelper.cs index 0e21ab1c2..b0f8544d6 100644 --- a/src/Microsoft.AspNetCore.OData/Linq/SelectExpandHelper.cs +++ b/OData2Linq/SelectExpandHelper.cs @@ -2,6 +2,9 @@ { using Microsoft.AspNetCore.OData.Edm; using Microsoft.AspNetCore.OData.Query; + //using Community.OData.Linq.OData.Formatter; + //using Community.OData.Linq.OData.Query; + using Microsoft.Extensions.DependencyInjection; using Microsoft.OData.Edm; using Microsoft.OData.UriParser; diff --git a/src/Microsoft.AspNetCore.OData/Linq/TopSkipHelper.cs b/OData2Linq/TopSkipHelper.cs similarity index 100% rename from src/Microsoft.AspNetCore.OData/Linq/TopSkipHelper.cs rename to OData2Linq/TopSkipHelper.cs diff --git a/src/Microsoft.AspNetCore.OData/Microsoft.AspNetCore.OData.xml b/src/Microsoft.AspNetCore.OData/Microsoft.AspNetCore.OData.xml index 43633c7f8..81922ce60 100644 --- a/src/Microsoft.AspNetCore.OData/Microsoft.AspNetCore.OData.xml +++ b/src/Microsoft.AspNetCore.OData/Microsoft.AspNetCore.OData.xml @@ -14988,177 +14988,5 @@ - - - The simplified options. - - - - - Enable applying OData specific functions to query - - - The query. - - - The configuration action. - - - The edm model. - - - The query type param - - - The OData aware query. - - - - - Apply select and expand query options. - - - The OData aware query. - - - The $select parameter text. - - - The $expand parameter text. - - - The entity set name. - - - The query type param - - - The selection result in specific format. - - - - - Apply select and expand query options to query. Warning! not all query providers support it. - - - The OData aware query. - - - The $select parameter text. - - - The $expand parameter text. - - - The entity set name. - - - The query type param - - - The selection result in specific format. - - - - - Apply $top and $skip parameters to query. - - The type param - The OData aware query. - $top parameter value - $skip parameter value - The entity set name. - The query with applied $top and $skip parameters. - - - - Apply OData query options except $select and $expand parameters. - - The type param. - The OData aware query. - The query options. - The entity set name. - The query with applied OData parameters. - - - - Apply OData query options and execute query. - - The type param. - The OData aware query. - The query options. - The entity set name. - The enumeration of query results . - - - - Apply OData query options. Warning! not all providers support it. - - The type param. - The OData aware query. - The query options. - The entity set name. - The query with special type of results . - - - - Apply $filter parameter to query. - - - The OData aware query. - - - The $filter parameter text. - - - The entity set name. - - - The query type param - - - The query with applied filter parameter. - - - Argument Null Exception - - - - - Apply $orderby parameter to query. - - - The OData aware query. - - - The $orderby parameter text. - - - The entity set name. - - - The query type param - - - The query with applied order by parameter. - - - Argument Null Exception - - - - - Sets the action which will be used to initialize every instance of . - - The action which will be used to initialize every instance of . - initializer - SetInitializer - - - - Added code - - - diff --git a/src/Microsoft.AspNetCore.OData/Properties/AssemblyInfo.cs b/src/Microsoft.AspNetCore.OData/Properties/AssemblyInfo.cs index 114c3c75b..550e8a1f3 100644 --- a/src/Microsoft.AspNetCore.OData/Properties/AssemblyInfo.cs +++ b/src/Microsoft.AspNetCore.OData/Properties/AssemblyInfo.cs @@ -24,3 +24,5 @@ [assembly: InternalsVisibleTo("Microsoft.AspNetCore.OData.E2E.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] [assembly: InternalsVisibleTo("Microsoft.AspNetCore.OData.NewtonsoftJson.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] + +[assembly: InternalsVisibleTo(assemblyName: "OData2Linq, PublicKey=00240000048000009400000006020000002400005253413100040000010001009d992fba5d4cbb3d67a46a65d01094fac14c4d8269c76dfe22a2447b37ed7339fb906d08d84a44bd8fe496bdab1a4d49c0a5df61600349d7a4708da1df0e4e22e726d830957a8ab2aba161633728e45561b078ee89c5c5afef23379499bee69c0b70e8039d09f11d0817e65cfd3848f2a6a2db0e75e767f596ff28d25c8acadb")] \ No newline at end of file diff --git a/test/Microsoft.AspNetCore.OData.Tests/Linq/DateTimeFilterTests.cs b/test/OData2Linq.Tests/DateTimeFilterTests.cs similarity index 100% rename from test/Microsoft.AspNetCore.OData.Tests/Linq/DateTimeFilterTests.cs rename to test/OData2Linq.Tests/DateTimeFilterTests.cs diff --git a/test/Microsoft.AspNetCore.OData.Tests/Linq/ExpandTests.cs b/test/OData2Linq.Tests/ExpandTests.cs similarity index 100% rename from test/Microsoft.AspNetCore.OData.Tests/Linq/ExpandTests.cs rename to test/OData2Linq.Tests/ExpandTests.cs diff --git a/test/Microsoft.AspNetCore.OData.Tests/Linq/FilterDataContractTests.cs b/test/OData2Linq.Tests/FilterDataContractTests.cs similarity index 100% rename from test/Microsoft.AspNetCore.OData.Tests/Linq/FilterDataContractTests.cs rename to test/OData2Linq.Tests/FilterDataContractTests.cs diff --git a/test/Microsoft.AspNetCore.OData.Tests/Linq/FilterFunctionsTests.cs b/test/OData2Linq.Tests/FilterFunctionsTests.cs similarity index 100% rename from test/Microsoft.AspNetCore.OData.Tests/Linq/FilterFunctionsTests.cs rename to test/OData2Linq.Tests/FilterFunctionsTests.cs diff --git a/test/Microsoft.AspNetCore.OData.Tests/Linq/FilterNavigationCollectionTests.cs b/test/OData2Linq.Tests/FilterNavigationCollectionTests.cs similarity index 100% rename from test/Microsoft.AspNetCore.OData.Tests/Linq/FilterNavigationCollectionTests.cs rename to test/OData2Linq.Tests/FilterNavigationCollectionTests.cs diff --git a/test/Microsoft.AspNetCore.OData.Tests/Linq/FilterNavigationLinkTests.cs b/test/OData2Linq.Tests/FilterNavigationLinkTests.cs similarity index 100% rename from test/Microsoft.AspNetCore.OData.Tests/Linq/FilterNavigationLinkTests.cs rename to test/OData2Linq.Tests/FilterNavigationLinkTests.cs diff --git a/test/Microsoft.AspNetCore.OData.Tests/Linq/FilterTests.cs b/test/OData2Linq.Tests/FilterTests.cs similarity index 100% rename from test/Microsoft.AspNetCore.OData.Tests/Linq/FilterTests.cs rename to test/OData2Linq.Tests/FilterTests.cs diff --git a/test/Microsoft.AspNetCore.OData.Tests/Linq/HashTests.cs b/test/OData2Linq.Tests/HashTests.cs similarity index 100% rename from test/Microsoft.AspNetCore.OData.Tests/Linq/HashTests.cs rename to test/OData2Linq.Tests/HashTests.cs diff --git a/test/Microsoft.AspNetCore.OData.Tests/Linq/Issues/Issue29.cs b/test/OData2Linq.Tests/Issues/Issue29.cs similarity index 100% rename from test/Microsoft.AspNetCore.OData.Tests/Linq/Issues/Issue29.cs rename to test/OData2Linq.Tests/Issues/Issue29.cs diff --git a/test/Microsoft.AspNetCore.OData.Tests/Linq/Issues/Issue31.cs b/test/OData2Linq.Tests/Issues/Issue31.cs similarity index 100% rename from test/Microsoft.AspNetCore.OData.Tests/Linq/Issues/Issue31.cs rename to test/OData2Linq.Tests/Issues/Issue31.cs diff --git a/test/Microsoft.AspNetCore.OData.Tests/Linq/Issues/Issue32.cs b/test/OData2Linq.Tests/Issues/Issue32.cs similarity index 100% rename from test/Microsoft.AspNetCore.OData.Tests/Linq/Issues/Issue32.cs rename to test/OData2Linq.Tests/Issues/Issue32.cs diff --git a/test/Microsoft.AspNetCore.OData.Tests/Linq/Issues/Issue33.cs b/test/OData2Linq.Tests/Issues/Issue33.cs similarity index 100% rename from test/Microsoft.AspNetCore.OData.Tests/Linq/Issues/Issue33.cs rename to test/OData2Linq.Tests/Issues/Issue33.cs diff --git a/test/Microsoft.AspNetCore.OData.Tests/Linq/JsonTests.cs b/test/OData2Linq.Tests/JsonTests.cs similarity index 100% rename from test/Microsoft.AspNetCore.OData.Tests/Linq/JsonTests.cs rename to test/OData2Linq.Tests/JsonTests.cs diff --git a/test/OData2Linq.Tests/OData2Linq.Tests.csproj b/test/OData2Linq.Tests/OData2Linq.Tests.csproj new file mode 100644 index 000000000..20769bbac --- /dev/null +++ b/test/OData2Linq.Tests/OData2Linq.Tests.csproj @@ -0,0 +1,21 @@ + + + + net6.0 + latest + enable + enable + + + + + + + + + + + + + + diff --git a/test/Microsoft.AspNetCore.OData.Tests/Linq/ODataTests.cs b/test/OData2Linq.Tests/ODataTests.cs similarity index 100% rename from test/Microsoft.AspNetCore.OData.Tests/Linq/ODataTests.cs rename to test/OData2Linq.Tests/ODataTests.cs diff --git a/test/Microsoft.AspNetCore.OData.Tests/Linq/OpenTypesTests.cs b/test/OData2Linq.Tests/OpenTypesTests.cs similarity index 100% rename from test/Microsoft.AspNetCore.OData.Tests/Linq/OpenTypesTests.cs rename to test/OData2Linq.Tests/OpenTypesTests.cs diff --git a/test/Microsoft.AspNetCore.OData.Tests/Linq/OrderByTests.cs b/test/OData2Linq.Tests/OrderByTests.cs similarity index 100% rename from test/Microsoft.AspNetCore.OData.Tests/Linq/OrderByTests.cs rename to test/OData2Linq.Tests/OrderByTests.cs diff --git a/test/Microsoft.AspNetCore.OData.Tests/Linq/SampleData/ClassWithCollection.cs b/test/OData2Linq.Tests/SampleData/ClassWithCollection.cs similarity index 100% rename from test/Microsoft.AspNetCore.OData.Tests/Linq/SampleData/ClassWithCollection.cs rename to test/OData2Linq.Tests/SampleData/ClassWithCollection.cs diff --git a/test/Microsoft.AspNetCore.OData.Tests/Linq/SampleData/ClassWithDeepNavigation.cs b/test/OData2Linq.Tests/SampleData/ClassWithDeepNavigation.cs similarity index 100% rename from test/Microsoft.AspNetCore.OData.Tests/Linq/SampleData/ClassWithDeepNavigation.cs rename to test/OData2Linq.Tests/SampleData/ClassWithDeepNavigation.cs diff --git a/test/Microsoft.AspNetCore.OData.Tests/Linq/SampleData/ClassWithLink.cs b/test/OData2Linq.Tests/SampleData/ClassWithLink.cs similarity index 100% rename from test/Microsoft.AspNetCore.OData.Tests/Linq/SampleData/ClassWithLink.cs rename to test/OData2Linq.Tests/SampleData/ClassWithLink.cs diff --git a/test/Microsoft.AspNetCore.OData.Tests/Linq/SampleData/OpenType.cs b/test/OData2Linq.Tests/SampleData/OpenType.cs similarity index 100% rename from test/Microsoft.AspNetCore.OData.Tests/Linq/SampleData/OpenType.cs rename to test/OData2Linq.Tests/SampleData/OpenType.cs diff --git a/test/Microsoft.AspNetCore.OData.Tests/Linq/SampleData/SampleWithCustomKey.cs b/test/OData2Linq.Tests/SampleData/SampleWithCustomKey.cs similarity index 100% rename from test/Microsoft.AspNetCore.OData.Tests/Linq/SampleData/SampleWithCustomKey.cs rename to test/OData2Linq.Tests/SampleData/SampleWithCustomKey.cs diff --git a/test/Microsoft.AspNetCore.OData.Tests/Linq/SampleData/SampleWithoutKey.cs b/test/OData2Linq.Tests/SampleData/SampleWithoutKey.cs similarity index 100% rename from test/Microsoft.AspNetCore.OData.Tests/Linq/SampleData/SampleWithoutKey.cs rename to test/OData2Linq.Tests/SampleData/SampleWithoutKey.cs diff --git a/test/Microsoft.AspNetCore.OData.Tests/Linq/SampleData/SimpleClass.cs b/test/OData2Linq.Tests/SampleData/SimpleClass.cs similarity index 100% rename from test/Microsoft.AspNetCore.OData.Tests/Linq/SampleData/SimpleClass.cs rename to test/OData2Linq.Tests/SampleData/SimpleClass.cs diff --git a/test/Microsoft.AspNetCore.OData.Tests/Linq/SampleData/SimpleClassDataContract.cs b/test/OData2Linq.Tests/SampleData/SimpleClassDataContract.cs similarity index 100% rename from test/Microsoft.AspNetCore.OData.Tests/Linq/SampleData/SimpleClassDataContract.cs rename to test/OData2Linq.Tests/SampleData/SimpleClassDataContract.cs diff --git a/test/Microsoft.AspNetCore.OData.Tests/Linq/SampleData/TestEnum.cs b/test/OData2Linq.Tests/SampleData/TestEnum.cs similarity index 100% rename from test/Microsoft.AspNetCore.OData.Tests/Linq/SampleData/TestEnum.cs rename to test/OData2Linq.Tests/SampleData/TestEnum.cs diff --git a/test/Microsoft.AspNetCore.OData.Tests/Linq/SampleData/TestItem.cs b/test/OData2Linq.Tests/SampleData/TestItem.cs similarity index 100% rename from test/Microsoft.AspNetCore.OData.Tests/Linq/SampleData/TestItem.cs rename to test/OData2Linq.Tests/SampleData/TestItem.cs diff --git a/test/Microsoft.AspNetCore.OData.Tests/Linq/SelectTests.cs b/test/OData2Linq.Tests/SelectTests.cs similarity index 100% rename from test/Microsoft.AspNetCore.OData.Tests/Linq/SelectTests.cs rename to test/OData2Linq.Tests/SelectTests.cs diff --git a/test/Microsoft.AspNetCore.OData.Tests/Linq/TopSkipTests.cs b/test/OData2Linq.Tests/TopSkipTests.cs similarity index 100% rename from test/Microsoft.AspNetCore.OData.Tests/Linq/TopSkipTests.cs rename to test/OData2Linq.Tests/TopSkipTests.cs From d7846329b4b7c03e29c16b507dd111912f3b538c Mon Sep 17 00:00:00 2001 From: Arnaud Boussaer Date: Fri, 7 Feb 2025 16:00:48 +0100 Subject: [PATCH 08/53] Specify for nuget that the OData lib is included as asset instead of referenced through nuget, add other references to nuget --- OData2Linq/OData2Linq.csproj | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/OData2Linq/OData2Linq.csproj b/OData2Linq/OData2Linq.csproj index 091b3716a..b3287833e 100644 --- a/OData2Linq/OData2Linq.csproj +++ b/OData2Linq/OData2Linq.csproj @@ -7,10 +7,21 @@ True FriendAssemblies.snk True + 0.0.3 - + + + + + All + + + + + + From e35dd4e7b0a346f11421bf9415e8d89084f37e4c Mon Sep 17 00:00:00 2001 From: Arnaud Boussaer Date: Fri, 7 Feb 2025 16:15:16 +0100 Subject: [PATCH 09/53] add extra reference to odata project in tests project --- OData2Linq/AssemblyInfo.cs | 17 ----------------- OData2Linq/OData2Linq.csproj | 4 ++-- OData2Linq/Program.cs | 11 +++++++++++ test/OData2Linq.Tests/OData2Linq.Tests.csproj | 1 + 4 files changed, 14 insertions(+), 19 deletions(-) delete mode 100644 OData2Linq/AssemblyInfo.cs create mode 100644 OData2Linq/Program.cs diff --git a/OData2Linq/AssemblyInfo.cs b/OData2Linq/AssemblyInfo.cs deleted file mode 100644 index 6fa618d25..000000000 --- a/OData2Linq/AssemblyInfo.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System.Runtime.InteropServices; - -// In SDK-style projects such as this one, several assembly attributes that were historically -// defined in this file are now automatically added during build and populated with -// values defined in project properties. For details of which attributes are included -// and how to customise this process see: https://aka.ms/assembly-info-properties - - -// Setting ComVisible to false makes the types in this assembly not visible to COM -// components. If you need to access a type in this assembly from COM, set the ComVisible -// attribute to true on that type. - -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM. - -[assembly: Guid("90adb1c3-e4aa-4250-b22b-1158576ae4d0")] diff --git a/OData2Linq/OData2Linq.csproj b/OData2Linq/OData2Linq.csproj index b3287833e..955c9881a 100644 --- a/OData2Linq/OData2Linq.csproj +++ b/OData2Linq/OData2Linq.csproj @@ -1,4 +1,4 @@ - + net6.0 @@ -7,7 +7,7 @@ True FriendAssemblies.snk True - 0.0.3 + 0.0.4 diff --git a/OData2Linq/Program.cs b/OData2Linq/Program.cs new file mode 100644 index 000000000..29f2e3006 --- /dev/null +++ b/OData2Linq/Program.cs @@ -0,0 +1,11 @@ +namespace OData2Linq +{ + public class Program + { + static void Main() + { + //Class1 inst = new Class1(); + //inst.Test(); + } + } +} diff --git a/test/OData2Linq.Tests/OData2Linq.Tests.csproj b/test/OData2Linq.Tests/OData2Linq.Tests.csproj index 20769bbac..12dff2f9e 100644 --- a/test/OData2Linq.Tests/OData2Linq.Tests.csproj +++ b/test/OData2Linq.Tests/OData2Linq.Tests.csproj @@ -16,6 +16,7 @@ + From 87c6f70764857825a583530f2c183d51b0e88477 Mon Sep 17 00:00:00 2001 From: Arnaud Boussaer Date: Fri, 7 Feb 2025 16:44:09 +0100 Subject: [PATCH 10/53] Add nuget information, add readme --- OData2Linq/OData2Linq.csproj | 12 ++++ OData2Linq/README.md | 104 +++++++++++++++++++++++++++++++++++ 2 files changed, 116 insertions(+) create mode 100644 OData2Linq/README.md diff --git a/OData2Linq/OData2Linq.csproj b/OData2Linq/OData2Linq.csproj index 955c9881a..915768eea 100644 --- a/OData2Linq/OData2Linq.csproj +++ b/OData2Linq/OData2Linq.csproj @@ -8,6 +8,11 @@ FriendAssemblies.snk True 0.0.4 + MIT + README.md + https://github.com/ArnaudB88/OData2Linq + Apply an OData filter text query to an IQueryable expression + odata;filter;linq @@ -24,4 +29,11 @@ + + + + True + \ + + diff --git a/OData2Linq/README.md b/OData2Linq/README.md new file mode 100644 index 000000000..7a427e6dc --- /dev/null +++ b/OData2Linq/README.md @@ -0,0 +1,104 @@ +# OData2Linq +Use OData filter text query in linq expresson for any . Support web and desktop applications. + +# Sample +Please check samples below to get started: + +### .NET Fiddle +https://dotnetfiddle.net/7Ndwot + +## Console app +``` +using System; +using System.Linq; + +using Community.OData.Linq; + +public class Entity +{ + public int Id { get; set; } + + public string Name { get; set; } +} + +public static class GetStartedDemo +{ + public static void Demo() + { + Entity[] items = + { + new Entity { Id = 1, Name = "n1" }, + new Entity { Id = 2, Name = "n2" }, + new Entity { Id = 3, Name = "n3" } + }; + IQueryable query = items.AsQueryable(); + + var result = query.OData().Filter("Id eq 1 or Name eq 'n3'").OrderBy("Name desc").TopSkip("10", "0").ToArray(); + + // Id: 3 Name: n3 + // Id: 1 Name: n1 + foreach (Entity entity in result) + { + Console.WriteLine("Id: {0} Name: {1}", entity.Id, entity.Name); + } + } +} +``` + +## Support ToArrayAsync(), ToListAsync(), and all other provider specific methods. +Use `.ToOriginalQuery()` after finishing working with OData to be able to support provider specific methods of original query. + +### Entity Framework async data fetch. +``` +Student[] array = await dbContext.Students.OData() + .Filter("LastName eq 'Alexander' or FirstMidName eq 'Laura'") + .OrderBy("EnrollmentDate desc") + .TopSkip("1","1") + .ToOriginalQuery() // required to be able to use .ToArrayAsync() next. + .ToArrayAsync(); + +ISelectExpandWrapper[] select2 = await dbContext.Students.OData() + .Filter("LastName eq 'Alexander' or FirstMidName eq 'Laura'") + .OrderBy("EnrollmentDate desc") + .SelectExpandAsQueryable("LastName", "Enrollments($select=CourseId)") //.SelectExpandAsQueryable() use .ToOriginalQuery() implicitly, so not need to call it. + .ToArrayAsync() +``` +### CosmosDb SQL API async data fetch. +``` +var item = await Container.GetItemLinqQueryable().OData() + .Filter($"Id eq '{id1}'") + .TopSkip("1") + .ToOriginalQuery() // required to be able to use .ToFeedIterator() next. + .ToFeedIterator() + .ReadNextAsync() +``` +# Advanced code samples at wiki +https://github.com/IharYakimush/comminity-data-odata-linq/wiki + +# Supported OData parameters +| Params | In Memory Collections | Entity Framework | CosmosDB SQL API | +| ------------- |:---------------------:|:----------------:| :---------------:| +| $filter |+ | + | + | +| $orderby |+ | + | + | +| $select |+ | + | - | +| $expand |+ | + | - | +| $top |+ | + | + | +| $skip |+ | + | + | + +# Nuget +- https://www.nuget.org/packages/Community.OData.Linq +- https://www.nuget.org/packages/Community.OData.Linq.Json +- https://www.nuget.org/packages/Community.OData.Linq.AspNetCore + +# Contribution +Please feel free to create issues and pool requests to develop branch + +# Build Status +[![Build status](https://ci.appveyor.com/api/projects/status/yrmp3074ryce61gb/branch/develop?svg=true)](https://ci.appveyor.com/project/IharYakimush/comminity-data-odata-linq/branch/develop) + +# References +This project is based on the following project: +https://github.com/IharYakimush/comminity-data-odata-linq + +The repository is a fork from: +https://github.com/OData/AspNetCoreOData From 9d18ed4ab421f909b293f496046ab916c62e60e2 Mon Sep 17 00:00:00 2001 From: Arnaud Boussaer Date: Fri, 7 Feb 2025 16:54:37 +0100 Subject: [PATCH 11/53] Change namespace --- OData2Linq/ODataLinqExtensions.cs | 16 +--------------- OData2Linq/ODataQuery.cs | 6 +----- OData2Linq/ODataQueryOrdered.cs | 6 +++--- OData2Linq/ODataSettings.cs | 6 +----- OData2Linq/Program.cs | 3 +-- OData2Linq/SelectExpandHelper.cs | 8 +------- OData2Linq/TopSkipHelper.cs | 2 +- test/OData2Linq.Tests/DateTimeFilterTests.cs | 7 ++----- test/OData2Linq.Tests/ExpandTests.cs | 4 ++-- test/OData2Linq.Tests/FilterDataContractTests.cs | 10 ++++------ test/OData2Linq.Tests/FilterFunctionsTests.cs | 4 ++-- .../FilterNavigationCollectionTests.cs | 4 ++-- .../FilterNavigationLinkTests.cs | 4 ++-- test/OData2Linq.Tests/FilterTests.cs | 4 ++-- test/OData2Linq.Tests/HashTests.cs | 2 +- test/OData2Linq.Tests/Issues/Issue29.cs | 2 +- test/OData2Linq.Tests/Issues/Issue31.cs | 4 ++-- test/OData2Linq.Tests/Issues/Issue32.cs | 2 +- test/OData2Linq.Tests/Issues/Issue33.cs | 2 +- test/OData2Linq.Tests/JsonTests.cs | 2 +- test/OData2Linq.Tests/ODataTests.cs | 4 ++-- test/OData2Linq.Tests/OpenTypesTests.cs | 4 ++-- test/OData2Linq.Tests/OrderByTests.cs | 4 ++-- .../SampleData/ClassWithCollection.cs | 2 +- .../SampleData/ClassWithDeepNavigation.cs | 2 +- .../OData2Linq.Tests/SampleData/ClassWithLink.cs | 2 +- test/OData2Linq.Tests/SampleData/OpenType.cs | 2 +- .../SampleData/SampleWithCustomKey.cs | 2 +- .../SampleData/SampleWithoutKey.cs | 2 +- test/OData2Linq.Tests/SampleData/SimpleClass.cs | 2 +- .../SampleData/SimpleClassDataContract.cs | 2 +- test/OData2Linq.Tests/SampleData/TestEnum.cs | 2 +- test/OData2Linq.Tests/SampleData/TestItem.cs | 2 +- test/OData2Linq.Tests/SelectTests.cs | 4 ++-- test/OData2Linq.Tests/TopSkipTests.cs | 4 ++-- 35 files changed, 52 insertions(+), 86 deletions(-) diff --git a/OData2Linq/ODataLinqExtensions.cs b/OData2Linq/ODataLinqExtensions.cs index d1b7730f2..556440928 100644 --- a/OData2Linq/ODataLinqExtensions.cs +++ b/OData2Linq/ODataLinqExtensions.cs @@ -1,19 +1,9 @@ -namespace Community.OData.Linq +namespace OData2Linq { - using Community.OData.Linq.OData; - using Community.OData.Linq.OData.Query; using Microsoft.AspNetCore.OData.Query; using Microsoft.AspNetCore.OData.Query.Expressions; using Microsoft.AspNetCore.OData.Query.Validator; using Microsoft.AspNetCore.OData.Query.Wrapper; - //using Community.OData.Linq.Builder; - //using Community.OData.Linq.Builder.Validators; - //using Community.OData.Linq.Common; - //using Community.OData.Linq.OData; - //using Community.OData.Linq.OData.Query; - //using Community.OData.Linq.OData.Query.Expressions; - //using Community.OData.Linq.Properties; - using Microsoft.Extensions.DependencyInjection; using Microsoft.OData; using Microsoft.OData.Edm; @@ -27,10 +17,6 @@ using System.Diagnostics.Contracts; using System.Linq; - /// - /// Entry class - /// Hier zo veel mogelijk originele referenties gebruiken - /// public static class ODataLinqExtensions { /// diff --git a/OData2Linq/ODataQuery.cs b/OData2Linq/ODataQuery.cs index 46d393004..32ff71ece 100644 --- a/OData2Linq/ODataQuery.cs +++ b/OData2Linq/ODataQuery.cs @@ -1,4 +1,4 @@ -namespace Community.OData.Linq +namespace OData2Linq { using Microsoft.Extensions.DependencyInjection; using Microsoft.OData.Edm; @@ -8,10 +8,6 @@ using System.Linq; using System.Linq.Expressions; - /// - /// Ook gebruikt - /// - /// public class ODataQuery : IQueryable { internal ODataQuery(IQueryable inner, IServiceProvider serviceProvider) diff --git a/OData2Linq/ODataQueryOrdered.cs b/OData2Linq/ODataQueryOrdered.cs index d0f8e88dc..c2bc8f389 100644 --- a/OData2Linq/ODataQueryOrdered.cs +++ b/OData2Linq/ODataQueryOrdered.cs @@ -1,12 +1,12 @@ -namespace Community.OData.Linq +namespace OData2Linq { using System; using System.Linq; public class ODataQueryOrdered : ODataQuery, IOrderedQueryable { - internal ODataQueryOrdered(IOrderedQueryable inner, IServiceProvider serviceProvider):base(inner,serviceProvider) + internal ODataQueryOrdered(IOrderedQueryable inner, IServiceProvider serviceProvider) : base(inner, serviceProvider) { - } + } } } diff --git a/OData2Linq/ODataSettings.cs b/OData2Linq/ODataSettings.cs index fa082d4ca..c87c780eb 100644 --- a/OData2Linq/ODataSettings.cs +++ b/OData2Linq/ODataSettings.cs @@ -1,15 +1,11 @@ -namespace Community.OData.Linq +namespace OData2Linq { using Microsoft.AspNetCore.OData.Query; using Microsoft.AspNetCore.OData.Query.Validator; using Microsoft.OData.ModelBuilder.Config; - //using Community.OData.Linq.OData.Query; using Microsoft.OData.UriParser; using System; - /// - /// Ook gebruikt - /// public class ODataSettings { private static readonly object SyncObj = new object(); diff --git a/OData2Linq/Program.cs b/OData2Linq/Program.cs index 29f2e3006..b94888a5c 100644 --- a/OData2Linq/Program.cs +++ b/OData2Linq/Program.cs @@ -1,11 +1,10 @@ namespace OData2Linq { + //Used for assembly friend public class Program { static void Main() { - //Class1 inst = new Class1(); - //inst.Test(); } } } diff --git a/OData2Linq/SelectExpandHelper.cs b/OData2Linq/SelectExpandHelper.cs index b0f8544d6..0aa75e182 100644 --- a/OData2Linq/SelectExpandHelper.cs +++ b/OData2Linq/SelectExpandHelper.cs @@ -1,9 +1,7 @@ -namespace Community.OData.Linq.OData +namespace OData2Linq { using Microsoft.AspNetCore.OData.Edm; using Microsoft.AspNetCore.OData.Query; - //using Community.OData.Linq.OData.Formatter; - //using Community.OData.Linq.OData.Query; using Microsoft.Extensions.DependencyInjection; using Microsoft.OData.Edm; @@ -13,10 +11,6 @@ using System.Globalization; using System.Linq; - /// - /// Added code - /// - /// public class SelectExpandHelper { private readonly ODataQuery query; diff --git a/OData2Linq/TopSkipHelper.cs b/OData2Linq/TopSkipHelper.cs index fa48d6af5..182c1824c 100644 --- a/OData2Linq/TopSkipHelper.cs +++ b/OData2Linq/TopSkipHelper.cs @@ -1,4 +1,4 @@ -namespace Community.OData.Linq.OData.Query +namespace OData2Linq { using Microsoft.AspNetCore.OData; using Microsoft.AspNetCore.OData.Query; diff --git a/test/OData2Linq.Tests/DateTimeFilterTests.cs b/test/OData2Linq.Tests/DateTimeFilterTests.cs index 29a67b2b5..df66dc18a 100644 --- a/test/OData2Linq.Tests/DateTimeFilterTests.cs +++ b/test/OData2Linq.Tests/DateTimeFilterTests.cs @@ -1,12 +1,9 @@ -using Community.OData.Linq.xTests.SampleData; - -using System; -using System.Linq; +using OData2Linq.SampleData; using Xunit; using Xunit.Abstractions; -namespace Community.OData.Linq.xTests +namespace OData2Linq { public class DateTimeFilterTests { diff --git a/test/OData2Linq.Tests/ExpandTests.cs b/test/OData2Linq.Tests/ExpandTests.cs index c32795fde..a85ebca1e 100644 --- a/test/OData2Linq.Tests/ExpandTests.cs +++ b/test/OData2Linq.Tests/ExpandTests.cs @@ -1,8 +1,8 @@ -namespace Community.OData.Linq.xTests +namespace OData2Linq { - using Community.OData.Linq.xTests.SampleData; using Microsoft.AspNetCore.OData.Query.Wrapper; using Microsoft.OData; + using OData2Linq.SampleData; using System.Collections.Generic; using System.Linq; using Xunit; diff --git a/test/OData2Linq.Tests/FilterDataContractTests.cs b/test/OData2Linq.Tests/FilterDataContractTests.cs index 7c407fa80..f33ce6f4f 100644 --- a/test/OData2Linq.Tests/FilterDataContractTests.cs +++ b/test/OData2Linq.Tests/FilterDataContractTests.cs @@ -1,12 +1,10 @@ - +using Microsoft.OData; -using Microsoft.OData; - -namespace Community.OData.Linq.xTests +namespace OData2Linq { - using Community.OData.Linq.xTests.SampleData; - using Xunit; + using OData2Linq.SampleData; using System.Linq; + using Xunit; public class FilterDataContractTests { diff --git a/test/OData2Linq.Tests/FilterFunctionsTests.cs b/test/OData2Linq.Tests/FilterFunctionsTests.cs index 9d743a406..d43e7f08c 100644 --- a/test/OData2Linq.Tests/FilterFunctionsTests.cs +++ b/test/OData2Linq.Tests/FilterFunctionsTests.cs @@ -1,8 +1,8 @@  -namespace Community.OData.Linq.xTests +namespace OData2Linq { - using Community.OData.Linq.xTests.SampleData; + using OData2Linq.SampleData; using Xunit; using System.Linq; diff --git a/test/OData2Linq.Tests/FilterNavigationCollectionTests.cs b/test/OData2Linq.Tests/FilterNavigationCollectionTests.cs index 9e8f92d28..1dc84b7e3 100644 --- a/test/OData2Linq.Tests/FilterNavigationCollectionTests.cs +++ b/test/OData2Linq.Tests/FilterNavigationCollectionTests.cs @@ -1,9 +1,9 @@ -namespace Community.OData.Linq.xTests +namespace OData2Linq { using System.Collections; using System.Linq; - using Community.OData.Linq.xTests.SampleData; + using OData2Linq.SampleData; using Xunit; diff --git a/test/OData2Linq.Tests/FilterNavigationLinkTests.cs b/test/OData2Linq.Tests/FilterNavigationLinkTests.cs index 553d8adfb..630415fd0 100644 --- a/test/OData2Linq.Tests/FilterNavigationLinkTests.cs +++ b/test/OData2Linq.Tests/FilterNavigationLinkTests.cs @@ -1,9 +1,9 @@ -namespace Community.OData.Linq.xTests +namespace OData2Linq { using System.Collections; using System.Linq; - using Community.OData.Linq.xTests.SampleData; + using OData2Linq.SampleData; using Microsoft.OData; diff --git a/test/OData2Linq.Tests/FilterTests.cs b/test/OData2Linq.Tests/FilterTests.cs index e39ff8bef..ee2935887 100644 --- a/test/OData2Linq.Tests/FilterTests.cs +++ b/test/OData2Linq.Tests/FilterTests.cs @@ -1,6 +1,6 @@ -namespace Community.OData.Linq.xTests +namespace OData2Linq { - using Community.OData.Linq.xTests.SampleData; + using OData2Linq.SampleData; using Microsoft.OData; using System.Linq; using Xunit; diff --git a/test/OData2Linq.Tests/HashTests.cs b/test/OData2Linq.Tests/HashTests.cs index b9ea33b97..c83e345f1 100644 --- a/test/OData2Linq.Tests/HashTests.cs +++ b/test/OData2Linq.Tests/HashTests.cs @@ -2,7 +2,7 @@ using Xunit; -namespace Community.OData.Linq.xTests +namespace OData2Linq { public class HashTests { diff --git a/test/OData2Linq.Tests/Issues/Issue29.cs b/test/OData2Linq.Tests/Issues/Issue29.cs index f993b60b6..8675760a3 100644 --- a/test/OData2Linq.Tests/Issues/Issue29.cs +++ b/test/OData2Linq.Tests/Issues/Issue29.cs @@ -7,7 +7,7 @@ //using Xunit; //using Community.OData.Linq.Json; -//namespace Community.OData.Linq.xTests.Issues29 +//namespace OData2Linq.Issues29 //{ // using Microsoft.AspNetCore.OData.Query.Wrapper; // using System.Diagnostics; diff --git a/test/OData2Linq.Tests/Issues/Issue31.cs b/test/OData2Linq.Tests/Issues/Issue31.cs index bd2817467..8b5f650ba 100644 --- a/test/OData2Linq.Tests/Issues/Issue31.cs +++ b/test/OData2Linq.Tests/Issues/Issue31.cs @@ -1,7 +1,7 @@  -namespace Community.OData.Linq.xTests.Issues +namespace OData2Linq.Issues { - using Community.OData.Linq.xTests.SampleData; + using OData2Linq.SampleData; using System.Linq; using Xunit; diff --git a/test/OData2Linq.Tests/Issues/Issue32.cs b/test/OData2Linq.Tests/Issues/Issue32.cs index 14da4ce78..62c79bc7f 100644 --- a/test/OData2Linq.Tests/Issues/Issue32.cs +++ b/test/OData2Linq.Tests/Issues/Issue32.cs @@ -1,4 +1,4 @@ -//namespace Community.OData.Linq.xTests.Issues29 +//namespace OData2Linq.Issues29 //{ // using System; // using System.Linq; diff --git a/test/OData2Linq.Tests/Issues/Issue33.cs b/test/OData2Linq.Tests/Issues/Issue33.cs index bfc00a5ee..ee89a2974 100644 --- a/test/OData2Linq.Tests/Issues/Issue33.cs +++ b/test/OData2Linq.Tests/Issues/Issue33.cs @@ -1,6 +1,6 @@ // Issue 33 : Recursive loop of complex types -namespace Community.OData.Linq.xTests.Issues33 +namespace OData2Linq.Issues33 { using System; using System.Linq; diff --git a/test/OData2Linq.Tests/JsonTests.cs b/test/OData2Linq.Tests/JsonTests.cs index 8a62a33d0..bacd29824 100644 --- a/test/OData2Linq.Tests/JsonTests.cs +++ b/test/OData2Linq.Tests/JsonTests.cs @@ -1,4 +1,4 @@ -namespace Community.OData.Linq.xTests +namespace OData2Linq { using Newtonsoft.Json; using Newtonsoft.Json.Linq; diff --git a/test/OData2Linq.Tests/ODataTests.cs b/test/OData2Linq.Tests/ODataTests.cs index b0ff38463..591a72053 100644 --- a/test/OData2Linq.Tests/ODataTests.cs +++ b/test/OData2Linq.Tests/ODataTests.cs @@ -1,10 +1,10 @@ -namespace Community.OData.Linq.xTests +namespace OData2Linq { using System; using System.Collections.Generic; using System.Linq; - using Community.OData.Linq.xTests.SampleData; + using OData2Linq.SampleData; using Microsoft.OData; diff --git a/test/OData2Linq.Tests/OpenTypesTests.cs b/test/OData2Linq.Tests/OpenTypesTests.cs index 4c077a743..e6b8d1976 100644 --- a/test/OData2Linq.Tests/OpenTypesTests.cs +++ b/test/OData2Linq.Tests/OpenTypesTests.cs @@ -1,7 +1,7 @@  -namespace Community.OData.Linq.xTests +namespace OData2Linq { - using Community.OData.Linq.xTests.SampleData; + using OData2Linq.SampleData; using Microsoft.AspNetCore.OData.Query; using System; using System.Linq; diff --git a/test/OData2Linq.Tests/OrderByTests.cs b/test/OData2Linq.Tests/OrderByTests.cs index 346e9a7fa..1df661e2f 100644 --- a/test/OData2Linq.Tests/OrderByTests.cs +++ b/test/OData2Linq.Tests/OrderByTests.cs @@ -1,9 +1,9 @@ using System.Linq; -using Community.OData.Linq.xTests.SampleData; +using OData2Linq.SampleData; using Microsoft.OData; using Xunit; -namespace Community.OData.Linq.xTests +namespace OData2Linq { using System; diff --git a/test/OData2Linq.Tests/SampleData/ClassWithCollection.cs b/test/OData2Linq.Tests/SampleData/ClassWithCollection.cs index af15c2abe..0eefdf90b 100644 --- a/test/OData2Linq.Tests/SampleData/ClassWithCollection.cs +++ b/test/OData2Linq.Tests/SampleData/ClassWithCollection.cs @@ -1,4 +1,4 @@ -namespace Community.OData.Linq.xTests.SampleData +namespace OData2Linq.SampleData { using System.Collections.Generic; using System.Linq; diff --git a/test/OData2Linq.Tests/SampleData/ClassWithDeepNavigation.cs b/test/OData2Linq.Tests/SampleData/ClassWithDeepNavigation.cs index 629553a2d..22707c65d 100644 --- a/test/OData2Linq.Tests/SampleData/ClassWithDeepNavigation.cs +++ b/test/OData2Linq.Tests/SampleData/ClassWithDeepNavigation.cs @@ -1,7 +1,7 @@ using Microsoft.OData.ModelBuilder; using System.Linq; -namespace Community.OData.Linq.xTests.SampleData +namespace OData2Linq.SampleData { public class ClassWithDeepNavigation { diff --git a/test/OData2Linq.Tests/SampleData/ClassWithLink.cs b/test/OData2Linq.Tests/SampleData/ClassWithLink.cs index 6ebedbdf2..74cef3056 100644 --- a/test/OData2Linq.Tests/SampleData/ClassWithLink.cs +++ b/test/OData2Linq.Tests/SampleData/ClassWithLink.cs @@ -1,4 +1,4 @@ -namespace Community.OData.Linq.xTests.SampleData +namespace OData2Linq.SampleData { using Microsoft.OData.ModelBuilder; using System.Linq; diff --git a/test/OData2Linq.Tests/SampleData/OpenType.cs b/test/OData2Linq.Tests/SampleData/OpenType.cs index 394ae5f11..4f4775e71 100644 --- a/test/OData2Linq.Tests/SampleData/OpenType.cs +++ b/test/OData2Linq.Tests/SampleData/OpenType.cs @@ -3,7 +3,7 @@ using System.Linq; using System.Text; -namespace Community.OData.Linq.xTests.SampleData +namespace OData2Linq.SampleData { public class OpenType { diff --git a/test/OData2Linq.Tests/SampleData/SampleWithCustomKey.cs b/test/OData2Linq.Tests/SampleData/SampleWithCustomKey.cs index cc08ddc85..a34638f85 100644 --- a/test/OData2Linq.Tests/SampleData/SampleWithCustomKey.cs +++ b/test/OData2Linq.Tests/SampleData/SampleWithCustomKey.cs @@ -1,4 +1,4 @@ -namespace Community.OData.Linq.xTests.SampleData +namespace OData2Linq.SampleData { using Microsoft.OData.ModelBuilder; using System; diff --git a/test/OData2Linq.Tests/SampleData/SampleWithoutKey.cs b/test/OData2Linq.Tests/SampleData/SampleWithoutKey.cs index 4890344a4..8315f4b2e 100644 --- a/test/OData2Linq.Tests/SampleData/SampleWithoutKey.cs +++ b/test/OData2Linq.Tests/SampleData/SampleWithoutKey.cs @@ -1,4 +1,4 @@ -namespace Community.OData.Linq.xTests.SampleData +namespace OData2Linq.SampleData { using System; using System.Linq; diff --git a/test/OData2Linq.Tests/SampleData/SimpleClass.cs b/test/OData2Linq.Tests/SampleData/SimpleClass.cs index 8f5e9b944..d5c769c5b 100644 --- a/test/OData2Linq.Tests/SampleData/SimpleClass.cs +++ b/test/OData2Linq.Tests/SampleData/SimpleClass.cs @@ -1,6 +1,6 @@ using System.Runtime.Serialization; -namespace Community.OData.Linq.xTests.SampleData +namespace OData2Linq.SampleData { using Microsoft.OData.ModelBuilder; using System; diff --git a/test/OData2Linq.Tests/SampleData/SimpleClassDataContract.cs b/test/OData2Linq.Tests/SampleData/SimpleClassDataContract.cs index 59f9da344..11233b571 100644 --- a/test/OData2Linq.Tests/SampleData/SimpleClassDataContract.cs +++ b/test/OData2Linq.Tests/SampleData/SimpleClassDataContract.cs @@ -1,4 +1,4 @@ -namespace Community.OData.Linq.xTests.SampleData +namespace OData2Linq.SampleData { using System.Linq; using System.Runtime.Serialization; diff --git a/test/OData2Linq.Tests/SampleData/TestEnum.cs b/test/OData2Linq.Tests/SampleData/TestEnum.cs index 08d52d452..cfaf641d5 100644 --- a/test/OData2Linq.Tests/SampleData/TestEnum.cs +++ b/test/OData2Linq.Tests/SampleData/TestEnum.cs @@ -1,4 +1,4 @@ -namespace Community.OData.Linq.xTests.SampleData +namespace OData2Linq.SampleData { public enum TestEnum { diff --git a/test/OData2Linq.Tests/SampleData/TestItem.cs b/test/OData2Linq.Tests/SampleData/TestItem.cs index a709dc1d1..853fdc25a 100644 --- a/test/OData2Linq.Tests/SampleData/TestItem.cs +++ b/test/OData2Linq.Tests/SampleData/TestItem.cs @@ -1,6 +1,6 @@ using System; -namespace Community.OData.Linq.xTests.SampleData +namespace OData2Linq.SampleData { /// /// A utility class for use in ODataTests diff --git a/test/OData2Linq.Tests/SelectTests.cs b/test/OData2Linq.Tests/SelectTests.cs index b73636de7..c8db7a13c 100644 --- a/test/OData2Linq.Tests/SelectTests.cs +++ b/test/OData2Linq.Tests/SelectTests.cs @@ -1,6 +1,6 @@ -namespace Community.OData.Linq.xTests +namespace OData2Linq { - using Community.OData.Linq.xTests.SampleData; + using OData2Linq.SampleData; using Microsoft.AspNetCore.OData.Query.Wrapper; using Microsoft.OData; using System; diff --git a/test/OData2Linq.Tests/TopSkipTests.cs b/test/OData2Linq.Tests/TopSkipTests.cs index 355cb35eb..324e14718 100644 --- a/test/OData2Linq.Tests/TopSkipTests.cs +++ b/test/OData2Linq.Tests/TopSkipTests.cs @@ -1,9 +1,9 @@ -namespace Community.OData.Linq.xTests +namespace OData2Linq { using System.Collections; using System.Linq; - using Community.OData.Linq.xTests.SampleData; + using OData2Linq.SampleData; using Microsoft.OData; From 1548dd641574c420334764b45f39914fad3c3702 Mon Sep 17 00:00:00 2001 From: Arnaud Boussaer Date: Fri, 7 Feb 2025 17:11:20 +0100 Subject: [PATCH 12/53] update version --- OData2Linq/OData2Linq.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OData2Linq/OData2Linq.csproj b/OData2Linq/OData2Linq.csproj index 915768eea..310aaa47e 100644 --- a/OData2Linq/OData2Linq.csproj +++ b/OData2Linq/OData2Linq.csproj @@ -7,7 +7,7 @@ True FriendAssemblies.snk True - 0.0.4 + 0.0.5 MIT README.md https://github.com/ArnaudB88/OData2Linq From 86e9a87a4fb87829e6220e9033156dbd87f02058 Mon Sep 17 00:00:00 2001 From: Arnaud Boussaer Date: Tue, 11 Feb 2025 14:45:44 +0100 Subject: [PATCH 13/53] Update readme --- OData2Linq/README.md | 69 +++++++++++++++++++++++--------------------- 1 file changed, 36 insertions(+), 33 deletions(-) diff --git a/OData2Linq/README.md b/OData2Linq/README.md index 7a427e6dc..f41ada1ed 100644 --- a/OData2Linq/README.md +++ b/OData2Linq/README.md @@ -1,23 +1,36 @@ +![NuGet Version](https://img.shields.io/nuget/v/OData2Linq) + # OData2Linq -Use OData filter text query in linq expresson for any . Support web and desktop applications. +Apply an OData text query (filter, order by, select, ..) to an IQueryable expression. -# Sample -Please check samples below to get started: +# Features +- Manipulate an IQueryable expression with OData text query -### .NET Fiddle -https://dotnetfiddle.net/7Ndwot +## Supported OData parameters +| Params | In Memory Collections | Entity Framework | CosmosDB SQL API | +| ------------- |:---------------------:|:----------------:| :---------------:| +| $filter |+ | + | + | +| $orderby |+ | + | + | +| $select |+ | + | - | +| $expand |+ | + | - | +| $top |+ | + | + | +| $skip |+ | + | + | + +# Samples +Please check samples below to get started + +## .NET Fiddle +https://dotnetfiddle.net/6dLB2g ## Console app -``` +```csharp using System; using System.Linq; - -using Community.OData.Linq; +using OData2Linq; public class Entity { public int Id { get; set; } - public string Name { get; set; } } @@ -26,11 +39,11 @@ public static class GetStartedDemo public static void Demo() { Entity[] items = - { - new Entity { Id = 1, Name = "n1" }, - new Entity { Id = 2, Name = "n2" }, - new Entity { Id = 3, Name = "n3" } - }; + { + new Entity { Id = 1, Name = "n1" }, + new Entity { Id = 2, Name = "n2" }, + new Entity { Id = 3, Name = "n3" } + }; IQueryable query = items.AsQueryable(); var result = query.OData().Filter("Id eq 1 or Name eq 'n3'").OrderBy("Name desc").TopSkip("10", "0").ToArray(); @@ -72,29 +85,19 @@ var item = await Container.GetItemLinqQueryable().OData() .ToFeedIterator() .ReadNextAsync() ``` -# Advanced code samples at wiki -https://github.com/IharYakimush/comminity-data-odata-linq/wiki -# Supported OData parameters -| Params | In Memory Collections | Entity Framework | CosmosDB SQL API | -| ------------- |:---------------------:|:----------------:| :---------------:| -| $filter |+ | + | + | -| $orderby |+ | + | + | -| $select |+ | + | - | -| $expand |+ | + | - | -| $top |+ | + | + | -| $skip |+ | + | + | - -# Nuget -- https://www.nuget.org/packages/Community.OData.Linq -- https://www.nuget.org/packages/Community.OData.Linq.Json -- https://www.nuget.org/packages/Community.OData.Linq.AspNetCore +## Advanced code samples at wiki +See the [Wiki pages](https://github.com/ArnaudB88/OData2Linq/wiki) # Contribution -Please feel free to create issues and pool requests to develop branch +Please feel free to create issues and pull requests to the main branch. -# Build Status -[![Build status](https://ci.appveyor.com/api/projects/status/yrmp3074ryce61gb/branch/develop?svg=true)](https://ci.appveyor.com/project/IharYakimush/comminity-data-odata-linq/branch/develop) +# Nuget +| Package | NuGet | Info | +|---------------------------------|----------------------------------------------------------------------------------|------------------| +| OData2Linq | ![NuGet Version](https://img.shields.io/nuget/v/OData2Linq) | OData v8 support | +| Community.OData.Linq.Json | ![NuGet Version](https://img.shields.io/nuget/v/Community.OData.Linq.Json) | Still works on odata v7 libraries. Open an issue to create a new package for odata v8. | +| Community.OData.Linq.AspNetCore | ![NuGet Version](https://img.shields.io/nuget/v/Community.OData.Linq.AspNetCore) | Still works on odata v7 libraries. Open an issue to create a new package for odata v8. | # References This project is based on the following project: From 7324c0985ff344c6c4fc9da058ec76c2e3bf0590 Mon Sep 17 00:00:00 2001 From: Arnaud Boussaer Date: Tue, 11 Feb 2025 14:58:12 +0100 Subject: [PATCH 14/53] move readme file, move OData2Linq files --- AspNetCoreOData.sln | 16 ++++++------- OData2Linq/README.md => README-odata2linq.md | 0 .../OData2Linq}/FriendAssemblies.snk | Bin .../OData2Linq}/OData2Linq.csproj | 21 ++++++++---------- .../OData2Linq}/ODataLinqExtensions.cs | 0 {OData2Linq => src/OData2Linq}/ODataQuery.cs | 0 .../OData2Linq}/ODataQueryOrdered.cs | 0 .../OData2Linq}/ODataSettings.cs | 0 {OData2Linq => src/OData2Linq}/Program.cs | 0 .../OData2Linq}/SelectExpandHelper.cs | 0 .../OData2Linq}/TopSkipHelper.cs | 0 test/OData2Linq.Tests/OData2Linq.Tests.csproj | 2 +- 12 files changed, 18 insertions(+), 21 deletions(-) rename OData2Linq/README.md => README-odata2linq.md (100%) rename {OData2Linq => src/OData2Linq}/FriendAssemblies.snk (100%) rename {OData2Linq => src/OData2Linq}/OData2Linq.csproj (73%) rename {OData2Linq => src/OData2Linq}/ODataLinqExtensions.cs (100%) rename {OData2Linq => src/OData2Linq}/ODataQuery.cs (100%) rename {OData2Linq => src/OData2Linq}/ODataQueryOrdered.cs (100%) rename {OData2Linq => src/OData2Linq}/ODataSettings.cs (100%) rename {OData2Linq => src/OData2Linq}/Program.cs (100%) rename {OData2Linq => src/OData2Linq}/SelectExpandHelper.cs (100%) rename {OData2Linq => src/OData2Linq}/TopSkipHelper.cs (100%) diff --git a/AspNetCoreOData.sln b/AspNetCoreOData.sln index 69cff4667..2b16a34bd 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.12.35707.178 d17.12 +VisualStudioVersion = 17.12.35707.178 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{2F0E102B-EB33-4025-BE56-7B8F9D2C4B8A}" EndProject @@ -27,10 +27,10 @@ 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}") = "OData2Linq", "OData2Linq\OData2Linq.csproj", "{F339CB11-F64A-4497-B978-839D97FCE3F5}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OData2Linq.Tests", "test\OData2Linq.Tests\OData2Linq.Tests.csproj", "{E7F48129-7544-4D85-ABE1-4CB037E1C780}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OData2Linq", "src\OData2Linq\OData2Linq.csproj", "{568A35C2-8677-41C6-9098-3AEF23286A9E}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -73,14 +73,14 @@ 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 - {F339CB11-F64A-4497-B978-839D97FCE3F5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F339CB11-F64A-4497-B978-839D97FCE3F5}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F339CB11-F64A-4497-B978-839D97FCE3F5}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F339CB11-F64A-4497-B978-839D97FCE3F5}.Release|Any CPU.Build.0 = Release|Any CPU {E7F48129-7544-4D85-ABE1-4CB037E1C780}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {E7F48129-7544-4D85-ABE1-4CB037E1C780}.Debug|Any CPU.Build.0 = Debug|Any CPU {E7F48129-7544-4D85-ABE1-4CB037E1C780}.Release|Any CPU.ActiveCfg = Release|Any CPU {E7F48129-7544-4D85-ABE1-4CB037E1C780}.Release|Any CPU.Build.0 = Release|Any CPU + {568A35C2-8677-41C6-9098-3AEF23286A9E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {568A35C2-8677-41C6-9098-3AEF23286A9E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {568A35C2-8677-41C6-9098-3AEF23286A9E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {568A35C2-8677-41C6-9098-3AEF23286A9E}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -95,8 +95,8 @@ 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} - {F339CB11-F64A-4497-B978-839D97FCE3F5} = {2F0E102B-EB33-4025-BE56-7B8F9D2C4B8A} {E7F48129-7544-4D85-ABE1-4CB037E1C780} = {F994C269-55BA-44F0-9DA7-6D5A3CFA79EB} + {568A35C2-8677-41C6-9098-3AEF23286A9E} = {2F0E102B-EB33-4025-BE56-7B8F9D2C4B8A} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {540C9752-AAC0-49EA-BA60-78490C90FF86} diff --git a/OData2Linq/README.md b/README-odata2linq.md similarity index 100% rename from OData2Linq/README.md rename to README-odata2linq.md diff --git a/OData2Linq/FriendAssemblies.snk b/src/OData2Linq/FriendAssemblies.snk similarity index 100% rename from OData2Linq/FriendAssemblies.snk rename to src/OData2Linq/FriendAssemblies.snk diff --git a/OData2Linq/OData2Linq.csproj b/src/OData2Linq/OData2Linq.csproj similarity index 73% rename from OData2Linq/OData2Linq.csproj rename to src/OData2Linq/OData2Linq.csproj index 310aaa47e..f9ac7bbce 100644 --- a/OData2Linq/OData2Linq.csproj +++ b/src/OData2Linq/OData2Linq.csproj @@ -9,7 +9,7 @@ True 0.0.5 MIT - README.md + README-odata2linq.md https://github.com/ArnaudB88/OData2Linq Apply an OData filter text query to an IQueryable expression odata;filter;linq @@ -20,20 +20,17 @@ - - - All - - - - + + All + - - True - \ - + + + True + \ + diff --git a/OData2Linq/ODataLinqExtensions.cs b/src/OData2Linq/ODataLinqExtensions.cs similarity index 100% rename from OData2Linq/ODataLinqExtensions.cs rename to src/OData2Linq/ODataLinqExtensions.cs diff --git a/OData2Linq/ODataQuery.cs b/src/OData2Linq/ODataQuery.cs similarity index 100% rename from OData2Linq/ODataQuery.cs rename to src/OData2Linq/ODataQuery.cs diff --git a/OData2Linq/ODataQueryOrdered.cs b/src/OData2Linq/ODataQueryOrdered.cs similarity index 100% rename from OData2Linq/ODataQueryOrdered.cs rename to src/OData2Linq/ODataQueryOrdered.cs diff --git a/OData2Linq/ODataSettings.cs b/src/OData2Linq/ODataSettings.cs similarity index 100% rename from OData2Linq/ODataSettings.cs rename to src/OData2Linq/ODataSettings.cs diff --git a/OData2Linq/Program.cs b/src/OData2Linq/Program.cs similarity index 100% rename from OData2Linq/Program.cs rename to src/OData2Linq/Program.cs diff --git a/OData2Linq/SelectExpandHelper.cs b/src/OData2Linq/SelectExpandHelper.cs similarity index 100% rename from OData2Linq/SelectExpandHelper.cs rename to src/OData2Linq/SelectExpandHelper.cs diff --git a/OData2Linq/TopSkipHelper.cs b/src/OData2Linq/TopSkipHelper.cs similarity index 100% rename from OData2Linq/TopSkipHelper.cs rename to src/OData2Linq/TopSkipHelper.cs diff --git a/test/OData2Linq.Tests/OData2Linq.Tests.csproj b/test/OData2Linq.Tests/OData2Linq.Tests.csproj index 12dff2f9e..507002fdc 100644 --- a/test/OData2Linq.Tests/OData2Linq.Tests.csproj +++ b/test/OData2Linq.Tests/OData2Linq.Tests.csproj @@ -15,8 +15,8 @@ - + From 91e572e94bc20fb3b2e37de0e4b709cf4c6777b5 Mon Sep 17 00:00:00 2001 From: Arnaud Boussaer Date: Tue, 11 Feb 2025 15:06:36 +0100 Subject: [PATCH 15/53] add a symbolic link to the odata2linq readme --- .github/README.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 .github/README.md diff --git a/.github/README.md b/.github/README.md new file mode 100644 index 000000000..2ee6245c6 --- /dev/null +++ b/.github/README.md @@ -0,0 +1 @@ +../README-odata2linq.md \ No newline at end of file From 616a2e557072510a98a10452cec09d07acac37e0 Mon Sep 17 00:00:00 2001 From: Arnaud Boussaer Date: Tue, 11 Feb 2025 15:18:17 +0100 Subject: [PATCH 16/53] instead of symlink, move readme to github folder --- .github/README.md | 108 ++++++++++++++++++++++++++++++- README-odata2linq.md | 107 ------------------------------ src/OData2Linq/OData2Linq.csproj | 4 +- 3 files changed, 109 insertions(+), 110 deletions(-) delete mode 100644 README-odata2linq.md diff --git a/.github/README.md b/.github/README.md index 2ee6245c6..f41ada1ed 100644 --- a/.github/README.md +++ b/.github/README.md @@ -1 +1,107 @@ -../README-odata2linq.md \ No newline at end of file +![NuGet Version](https://img.shields.io/nuget/v/OData2Linq) + +# OData2Linq +Apply an OData text query (filter, order by, select, ..) to an IQueryable expression. + +# Features +- Manipulate an IQueryable expression with OData text query + +## Supported OData parameters +| Params | In Memory Collections | Entity Framework | CosmosDB SQL API | +| ------------- |:---------------------:|:----------------:| :---------------:| +| $filter |+ | + | + | +| $orderby |+ | + | + | +| $select |+ | + | - | +| $expand |+ | + | - | +| $top |+ | + | + | +| $skip |+ | + | + | + +# Samples +Please check samples below to get started + +## .NET Fiddle +https://dotnetfiddle.net/6dLB2g + +## Console app +```csharp +using System; +using System.Linq; +using OData2Linq; + +public class Entity +{ + public int Id { get; set; } + public string Name { get; set; } +} + +public static class GetStartedDemo +{ + public static void Demo() + { + Entity[] items = + { + new Entity { Id = 1, Name = "n1" }, + new Entity { Id = 2, Name = "n2" }, + new Entity { Id = 3, Name = "n3" } + }; + IQueryable query = items.AsQueryable(); + + var result = query.OData().Filter("Id eq 1 or Name eq 'n3'").OrderBy("Name desc").TopSkip("10", "0").ToArray(); + + // Id: 3 Name: n3 + // Id: 1 Name: n1 + foreach (Entity entity in result) + { + Console.WriteLine("Id: {0} Name: {1}", entity.Id, entity.Name); + } + } +} +``` + +## Support ToArrayAsync(), ToListAsync(), and all other provider specific methods. +Use `.ToOriginalQuery()` after finishing working with OData to be able to support provider specific methods of original query. + +### Entity Framework async data fetch. +``` +Student[] array = await dbContext.Students.OData() + .Filter("LastName eq 'Alexander' or FirstMidName eq 'Laura'") + .OrderBy("EnrollmentDate desc") + .TopSkip("1","1") + .ToOriginalQuery() // required to be able to use .ToArrayAsync() next. + .ToArrayAsync(); + +ISelectExpandWrapper[] select2 = await dbContext.Students.OData() + .Filter("LastName eq 'Alexander' or FirstMidName eq 'Laura'") + .OrderBy("EnrollmentDate desc") + .SelectExpandAsQueryable("LastName", "Enrollments($select=CourseId)") //.SelectExpandAsQueryable() use .ToOriginalQuery() implicitly, so not need to call it. + .ToArrayAsync() +``` +### CosmosDb SQL API async data fetch. +``` +var item = await Container.GetItemLinqQueryable().OData() + .Filter($"Id eq '{id1}'") + .TopSkip("1") + .ToOriginalQuery() // required to be able to use .ToFeedIterator() next. + .ToFeedIterator() + .ReadNextAsync() +``` + +## Advanced code samples at wiki +See the [Wiki pages](https://github.com/ArnaudB88/OData2Linq/wiki) + +# Contribution +Please feel free to create issues and pull requests to the main branch. + +# Nuget +| Package | NuGet | Info | +|---------------------------------|----------------------------------------------------------------------------------|------------------| +| OData2Linq | ![NuGet Version](https://img.shields.io/nuget/v/OData2Linq) | OData v8 support | +| Community.OData.Linq.Json | ![NuGet Version](https://img.shields.io/nuget/v/Community.OData.Linq.Json) | Still works on odata v7 libraries. Open an issue to create a new package for odata v8. | +| Community.OData.Linq.AspNetCore | ![NuGet Version](https://img.shields.io/nuget/v/Community.OData.Linq.AspNetCore) | Still works on odata v7 libraries. Open an issue to create a new package for odata v8. | + +# References +This project is based on the following project: +https://github.com/IharYakimush/comminity-data-odata-linq + +The repository is a fork from: +https://github.com/OData/AspNetCoreOData diff --git a/README-odata2linq.md b/README-odata2linq.md deleted file mode 100644 index f41ada1ed..000000000 --- a/README-odata2linq.md +++ /dev/null @@ -1,107 +0,0 @@ -![NuGet Version](https://img.shields.io/nuget/v/OData2Linq) - -# OData2Linq -Apply an OData text query (filter, order by, select, ..) to an IQueryable expression. - -# Features -- Manipulate an IQueryable expression with OData text query - -## Supported OData parameters -| Params | In Memory Collections | Entity Framework | CosmosDB SQL API | -| ------------- |:---------------------:|:----------------:| :---------------:| -| $filter |+ | + | + | -| $orderby |+ | + | + | -| $select |+ | + | - | -| $expand |+ | + | - | -| $top |+ | + | + | -| $skip |+ | + | + | - -# Samples -Please check samples below to get started - -## .NET Fiddle -https://dotnetfiddle.net/6dLB2g - -## Console app -```csharp -using System; -using System.Linq; -using OData2Linq; - -public class Entity -{ - public int Id { get; set; } - public string Name { get; set; } -} - -public static class GetStartedDemo -{ - public static void Demo() - { - Entity[] items = - { - new Entity { Id = 1, Name = "n1" }, - new Entity { Id = 2, Name = "n2" }, - new Entity { Id = 3, Name = "n3" } - }; - IQueryable query = items.AsQueryable(); - - var result = query.OData().Filter("Id eq 1 or Name eq 'n3'").OrderBy("Name desc").TopSkip("10", "0").ToArray(); - - // Id: 3 Name: n3 - // Id: 1 Name: n1 - foreach (Entity entity in result) - { - Console.WriteLine("Id: {0} Name: {1}", entity.Id, entity.Name); - } - } -} -``` - -## Support ToArrayAsync(), ToListAsync(), and all other provider specific methods. -Use `.ToOriginalQuery()` after finishing working with OData to be able to support provider specific methods of original query. - -### Entity Framework async data fetch. -``` -Student[] array = await dbContext.Students.OData() - .Filter("LastName eq 'Alexander' or FirstMidName eq 'Laura'") - .OrderBy("EnrollmentDate desc") - .TopSkip("1","1") - .ToOriginalQuery() // required to be able to use .ToArrayAsync() next. - .ToArrayAsync(); - -ISelectExpandWrapper[] select2 = await dbContext.Students.OData() - .Filter("LastName eq 'Alexander' or FirstMidName eq 'Laura'") - .OrderBy("EnrollmentDate desc") - .SelectExpandAsQueryable("LastName", "Enrollments($select=CourseId)") //.SelectExpandAsQueryable() use .ToOriginalQuery() implicitly, so not need to call it. - .ToArrayAsync() -``` -### CosmosDb SQL API async data fetch. -``` -var item = await Container.GetItemLinqQueryable().OData() - .Filter($"Id eq '{id1}'") - .TopSkip("1") - .ToOriginalQuery() // required to be able to use .ToFeedIterator() next. - .ToFeedIterator() - .ReadNextAsync() -``` - -## Advanced code samples at wiki -See the [Wiki pages](https://github.com/ArnaudB88/OData2Linq/wiki) - -# Contribution -Please feel free to create issues and pull requests to the main branch. - -# Nuget -| Package | NuGet | Info | -|---------------------------------|----------------------------------------------------------------------------------|------------------| -| OData2Linq | ![NuGet Version](https://img.shields.io/nuget/v/OData2Linq) | OData v8 support | -| Community.OData.Linq.Json | ![NuGet Version](https://img.shields.io/nuget/v/Community.OData.Linq.Json) | Still works on odata v7 libraries. Open an issue to create a new package for odata v8. | -| Community.OData.Linq.AspNetCore | ![NuGet Version](https://img.shields.io/nuget/v/Community.OData.Linq.AspNetCore) | Still works on odata v7 libraries. Open an issue to create a new package for odata v8. | - -# References -This project is based on the following project: -https://github.com/IharYakimush/comminity-data-odata-linq - -The repository is a fork from: -https://github.com/OData/AspNetCoreOData diff --git a/src/OData2Linq/OData2Linq.csproj b/src/OData2Linq/OData2Linq.csproj index f9ac7bbce..cc4ccf6b9 100644 --- a/src/OData2Linq/OData2Linq.csproj +++ b/src/OData2Linq/OData2Linq.csproj @@ -9,7 +9,7 @@ True 0.0.5 MIT - README-odata2linq.md + README.md https://github.com/ArnaudB88/OData2Linq Apply an OData filter text query to an IQueryable expression odata;filter;linq @@ -28,7 +28,7 @@ - + True \ From 4d0851f31ea90c0a95de9c1d12ce1d3d45ed3a6a Mon Sep 17 00:00:00 2001 From: Arnaud Boussaer Date: Tue, 11 Feb 2025 15:37:48 +0100 Subject: [PATCH 17/53] skip json tests, fix namespace --- .../{ => Helpers}/SelectExpandHelper.cs | 54 +++--- src/OData2Linq/{ => Helpers}/TopSkipHelper.cs | 4 +- src/OData2Linq/ODataLinqExtensions.cs | 164 +++++------------- test/OData2Linq.Tests/DateTimeFilterTests.cs | 5 +- test/OData2Linq.Tests/ExpandTests.cs | 4 +- .../FilterDataContractTests.cs | 4 +- test/OData2Linq.Tests/FilterFunctionsTests.cs | 8 +- .../FilterNavigationCollectionTests.cs | 6 +- .../FilterNavigationLinkTests.cs | 13 +- test/OData2Linq.Tests/FilterTests.cs | 4 +- test/OData2Linq.Tests/HashTests.cs | 6 +- test/OData2Linq.Tests/Issues/Issue29.cs | 2 +- test/OData2Linq.Tests/Issues/Issue31.cs | 4 +- test/OData2Linq.Tests/Issues/Issue32.cs | 2 +- test/OData2Linq.Tests/Issues/Issue33.cs | 2 +- test/OData2Linq.Tests/JsonTests.cs | 6 +- test/OData2Linq.Tests/ODataTests.cs | 8 +- test/OData2Linq.Tests/OpenTypesTests.cs | 5 +- test/OData2Linq.Tests/OrderByTests.cs | 9 +- .../SampleData/ClassWithCollection.cs | 2 +- .../SampleData/ClassWithDeepNavigation.cs | 3 +- .../SampleData/ClassWithLink.cs | 2 +- test/OData2Linq.Tests/SampleData/OpenType.cs | 7 +- .../SampleData/SampleWithCustomKey.cs | 2 +- .../SampleData/SampleWithoutKey.cs | 2 +- .../SampleData/SimpleClass.cs | 2 +- .../SampleData/SimpleClassDataContract.cs | 2 +- test/OData2Linq.Tests/SampleData/TestEnum.cs | 2 +- test/OData2Linq.Tests/SampleData/TestItem.cs | 2 +- test/OData2Linq.Tests/SelectTests.cs | 4 +- test/OData2Linq.Tests/TopSkipTests.cs | 10 +- 31 files changed, 118 insertions(+), 232 deletions(-) rename src/OData2Linq/{ => Helpers}/SelectExpandHelper.cs (81%) rename src/OData2Linq/{ => Helpers}/TopSkipHelper.cs (97%) diff --git a/src/OData2Linq/SelectExpandHelper.cs b/src/OData2Linq/Helpers/SelectExpandHelper.cs similarity index 81% rename from src/OData2Linq/SelectExpandHelper.cs rename to src/OData2Linq/Helpers/SelectExpandHelper.cs index 0aa75e182..8ae43fe78 100644 --- a/src/OData2Linq/SelectExpandHelper.cs +++ b/src/OData2Linq/Helpers/SelectExpandHelper.cs @@ -1,4 +1,4 @@ -namespace OData2Linq +namespace OData2Linq.Helpers { using Microsoft.AspNetCore.OData.Edm; using Microsoft.AspNetCore.OData.Query; @@ -11,7 +11,7 @@ using System.Globalization; using System.Linq; - public class SelectExpandHelper + internal class SelectExpandHelper { private readonly ODataQuery query; @@ -23,30 +23,30 @@ public class SelectExpandHelper public SelectExpandHelper(ODataRawQueryOptions rawQueryOptions, ODataQuery query, string entitySetName) { - this.Context = new ODataQueryContext(query.EdmModel, query.ElementType, null); - this.Context.RequestContainer = query.ServiceProvider; + Context = new ODataQueryContext(query.EdmModel, query.ElementType, null); + Context.RequestContainer = query.ServiceProvider; this.query = query; this.entitySetName = entitySetName; - this.RawValues = rawQueryOptions ?? throw new ArgumentNullException(nameof(rawQueryOptions)); - if (this.RawValues.Select != null || this.RawValues.Expand != null) + RawValues = rawQueryOptions ?? throw new ArgumentNullException(nameof(rawQueryOptions)); + if (RawValues.Select != null || RawValues.Expand != null) { Dictionary raws = new Dictionary(); - if (this.RawValues.Select != null) + if (RawValues.Select != null) { - raws["$select"] = this.RawValues.Select; + raws["$select"] = RawValues.Select; } - if (this.RawValues.Expand != null) + if (RawValues.Expand != null) { - raws["$expand"] = this.RawValues.Expand; + raws["$expand"] = RawValues.Expand; } ODataQueryOptionParser parser = ODataLinqExtensions.GetParser(this.query, this.entitySetName, raws); - this.SelectExpand = new SelectExpandQueryOption( - this.RawValues.Select, - this.RawValues.Expand, - this.Context, + SelectExpand = new SelectExpandQueryOption( + RawValues.Select, + RawValues.Expand, + Context, parser); } } @@ -86,18 +86,18 @@ public void AddAutoSelectExpandProperties() if (containsAutoSelectExpandProperties) { ODataQueryOptionParser parser = ODataLinqExtensions.GetParser( - this.query, - this.entitySetName, + query, + entitySetName, queryParameters); - var originalSelectExpand = this.SelectExpand; - this.SelectExpand = new SelectExpandQueryOption( + var originalSelectExpand = SelectExpand; + SelectExpand = new SelectExpandQueryOption( autoSelectRawValue, autoExpandRawValue, - this.Context, + Context, parser); if (originalSelectExpand != null && originalSelectExpand.LevelsMaxLiteralExpansionDepth > 0) { - this.SelectExpand.LevelsMaxLiteralExpansionDepth = + SelectExpand.LevelsMaxLiteralExpansionDepth = originalSelectExpand.LevelsMaxLiteralExpansionDepth; } } @@ -106,9 +106,9 @@ public void AddAutoSelectExpandProperties() public IQueryable Apply(ODataQuery query) { IQueryable result = query; - if (this.SelectExpand != null) + if (SelectExpand != null) { - var tempResult = this.ApplySelectExpand( + var tempResult = ApplySelectExpand( result, (ODataQuerySettings)query.ServiceProvider.GetService(typeof(ODataQuerySettings))); if (tempResult != default(IQueryable)) @@ -124,14 +124,14 @@ private TSelect ApplySelectExpand(TSelect entity, ODataQuerySettings se { var result = default(TSelect); - SelectExpandClause processedClause = this.SelectExpand.ProcessLevels(); + SelectExpandClause processedClause = SelectExpand.ProcessLevels(); SelectExpandQueryOption newSelectExpand = new SelectExpandQueryOption( - this.SelectExpand.RawSelect, - this.SelectExpand.RawExpand, - this.SelectExpand.Context, + SelectExpand.RawSelect, + SelectExpand.RawExpand, + SelectExpand.Context, processedClause); - ODataSettings qsettings = this.Context.RequestContainer.GetRequiredService(); + ODataSettings qsettings = Context.RequestContainer.GetRequiredService(); newSelectExpand.Validate(qsettings.ValidationSettings); diff --git a/src/OData2Linq/TopSkipHelper.cs b/src/OData2Linq/Helpers/TopSkipHelper.cs similarity index 97% rename from src/OData2Linq/TopSkipHelper.cs rename to src/OData2Linq/Helpers/TopSkipHelper.cs index 182c1824c..8544e147c 100644 --- a/src/OData2Linq/TopSkipHelper.cs +++ b/src/OData2Linq/Helpers/TopSkipHelper.cs @@ -1,11 +1,11 @@ -namespace OData2Linq +namespace OData2Linq.Helpers { using Microsoft.AspNetCore.OData; using Microsoft.AspNetCore.OData.Query; using Microsoft.OData; using System.Linq; - public class TopSkipHelper + internal class TopSkipHelper { public static IQueryable ApplyTopWithValidation(IQueryable query, long? top, ODataSettings settings) { diff --git a/src/OData2Linq/ODataLinqExtensions.cs b/src/OData2Linq/ODataLinqExtensions.cs index 556440928..7e2b4df90 100644 --- a/src/OData2Linq/ODataLinqExtensions.cs +++ b/src/OData2Linq/ODataLinqExtensions.cs @@ -10,6 +10,7 @@ using Microsoft.OData.ModelBuilder; using Microsoft.OData.ModelBuilder.Config; using Microsoft.OData.UriParser; + using OData2Linq.Helpers; using System; using System.Collections.Concurrent; using System.Collections.Generic; @@ -31,21 +32,11 @@ public static class ODataLinqExtensions /// /// Enable applying OData specific functions to query /// - /// - /// The query. - /// - /// - /// The configuration action. - /// - /// - /// The edm model. - /// - /// - /// The query type param - /// - /// - /// The OData aware query. - /// + /// The query. + /// The configuration action. + /// The edm model. + /// The query type param + /// The OData aware query. public static ODataQuery OData(this IQueryable query, Action configuration = null, IEdmModel edmModel = null) { if (query == null) throw new ArgumentNullException(nameof(query)); @@ -105,32 +96,15 @@ public static ODataQuery OData(this IQueryable query, Action - /// Apply select and expand query options. - /// - /// - /// The OData aware query. - /// - /// - /// The $select parameter text. - /// - /// - /// The $expand parameter text. - /// - /// - /// The entity set name. - /// - /// - /// The query type param - /// - /// - /// The selection result in specific format. - /// - public static IEnumerable SelectExpand( - this ODataQuery query, - string selectText = null, - string expandText = null, - string entitySetName = null) + /// Apply select and expand query options. + /// The OData aware query. + /// The $select parameter text. + /// The $expand parameter text. + /// The entity set name. + /// The query type param + /// The selection result in specific format. + public static IEnumerable SelectExpand(this ODataQuery query, + string selectText = null, string expandText = null, string entitySetName = null) { var result = SelectExpandInternal(query, selectText, expandText, entitySetName); @@ -140,36 +114,20 @@ public static IEnumerable SelectExpand( /// /// Apply select and expand query options to query. Warning! not all query providers support it. /// - /// - /// The OData aware query. - /// - /// - /// The $select parameter text. - /// - /// - /// The $expand parameter text. - /// - /// - /// The entity set name. - /// - /// - /// The query type param - /// - /// - /// The selection result in specific format. - /// - public static IQueryable SelectExpandAsQueryable( - this ODataQuery query, - string selectText = null, - string expandText = null, - string entitySetName = null) + /// The OData aware query. + /// The $select parameter text. + /// The $expand parameter text. + /// The entity set name. + /// The query type param + /// The selection result in specific format. + public static IQueryable SelectExpandAsQueryable(this ODataQuery query, + string selectText = null, string expandText = null, string entitySetName = null) { IQueryable result = SelectExpandInternal(query, selectText, expandText, entitySetName); return query.Provider.CreateQuery(result.Expression); } - private static IQueryable SelectExpandInternal(ODataQuery query, string selectText, string expandText, - string entitySetName) + private static IQueryable SelectExpandInternal(ODataQuery query, string selectText, string expandText, string entitySetName) { SelectExpandHelper helper = new SelectExpandHelper( new ODataRawQueryOptions { Select = selectText, Expand = expandText }, @@ -303,10 +261,7 @@ private static ODataQuery ApplyQueryOptionsInternal(ODataQuery query, O if (rawQueryOptions.Filter != null) { - //foreach (string filter in rawQueryOptions.Filters) - //{ query = query.Filter(rawQueryOptions.Filter, entitySetName); - //} } if (rawQueryOptions.OrderBy != null) @@ -322,24 +277,12 @@ private static ODataQuery ApplyQueryOptionsInternal(ODataQuery query, O /// /// Apply $filter parameter to query. /// - /// - /// The OData aware query. - /// - /// - /// The $filter parameter text. - /// - /// - /// The entity set name. - /// - /// - /// The query type param - /// - /// - /// The query with applied filter parameter. - /// - /// - /// Argument Null Exception - /// + /// The OData aware query. + /// The $filter parameter text. + /// The entity set name. + /// The query type param + /// The query with applied filter parameter. + /// Argument Null Exception public static ODataQuery Filter(this ODataQuery query, string filterText, string entitySetName = null) { if (query == null) throw new ArgumentNullException(nameof(query)); @@ -361,15 +304,6 @@ public static ODataQuery Filter(this ODataQuery query, string filterTex filterClause = new FilterClause(filterExpression, filterClause.RangeVariable); Contract.Assert(filterClause != null); - //OLD - //var validator = new FilterQueryValidator(settings.DefaultQuerySettings); - //validator.Validate(filterClause, settings.ValidationSettings, edmModel); - - //Expression filter = FilterBinder.Bind(query, filterClause, typeof(T), query.ServiceProvider); - //var result = ExpressionHelpers.Where(query, filter, typeof(T)); - // - - //NEW var queryContext = new ODataQueryContext(edmModel, query.ElementType) { RequestContainer = query.ServiceProvider @@ -378,6 +312,7 @@ public static ODataQuery Filter(this ODataQuery query, string filterTex var option = new FilterQueryOption(queryContext, filterClause); validator2.Validate(option, settings.ValidationSettings); + //TODO: check alternative //var binder = query.ServiceProvider.GetRequiredService(); //QueryBinderContext context = new QueryBinderContext(edmModel, query.ServiceProvider.GetRequiredService(), query.ElementType) //{ @@ -392,27 +327,13 @@ public static ODataQuery Filter(this ODataQuery query, string filterTex return new ODataQuery(result3, query.ServiceProvider); } - /// - /// Apply $orderby parameter to query. - /// - /// - /// The OData aware query. - /// - /// - /// The $orderby parameter text. - /// - /// - /// The entity set name. - /// - /// - /// The query type param - /// - /// - /// The query with applied order by parameter. - /// - /// - /// Argument Null Exception - /// + /// Apply $orderby parameter to query. + /// The OData aware query. + /// The $orderby parameter text. + /// The entity set name. + /// The query type param + /// The query with applied order by parameter. + /// Argument Null Exception public static ODataQueryOrdered OrderBy(this ODataQuery query, string orderbyText, string entitySetName = null) { if (query == null) throw new ArgumentNullException(nameof(query)); @@ -433,12 +354,6 @@ public static ODataQueryOrdered OrderBy(this ODataQuery query, string o ICollection nodes = OrderByNode.CreateCollection(orderByClause); - //OLD - //var validator = new OrderByQueryValidator(settings.DefaultQuerySettings); - //validator.Validate(nodes, settings.ValidationSettings, edmModel); - //IOrderedQueryable result = (IOrderedQueryable)OrderByBinder.OrderApplyToCore(query, settings.QuerySettings, nodes, edmModel); - - //NEW var queryContext = new ODataQueryContext(edmModel, query.ElementType) { RequestContainer = query.ServiceProvider @@ -447,6 +362,7 @@ public static ODataQueryOrdered OrderBy(this ODataQuery query, string o var option = new OrderByQueryOption(orderbyText, queryContext, queryOptionParser); validator2.Validate(option, settings.ValidationSettings); + //TODO: check alternative //var binder = new OrderByBinder(); //QueryBinderContext context = new QueryBinderContext(edmModel, query.ServiceProvider.GetRequiredService(), typeof(T)) //{ @@ -456,8 +372,6 @@ public static ODataQueryOrdered OrderBy(this ODataQuery query, string o //var orderbyResult = binder.BindOrderBy(option.OrderByClause, context); var result2 = option.ApplyTo(query, query.ServiceProvider.GetRequiredService()); - //return result2; - return new ODataQueryOrdered(result2, query.ServiceProvider); } diff --git a/test/OData2Linq.Tests/DateTimeFilterTests.cs b/test/OData2Linq.Tests/DateTimeFilterTests.cs index df66dc18a..bbf587a27 100644 --- a/test/OData2Linq.Tests/DateTimeFilterTests.cs +++ b/test/OData2Linq.Tests/DateTimeFilterTests.cs @@ -1,9 +1,8 @@ -using OData2Linq.SampleData; - +using OData2Linq.Tests.SampleData; using Xunit; using Xunit.Abstractions; -namespace OData2Linq +namespace OData2Linq.Tests { public class DateTimeFilterTests { diff --git a/test/OData2Linq.Tests/ExpandTests.cs b/test/OData2Linq.Tests/ExpandTests.cs index a85ebca1e..73d21c174 100644 --- a/test/OData2Linq.Tests/ExpandTests.cs +++ b/test/OData2Linq.Tests/ExpandTests.cs @@ -1,8 +1,8 @@ -namespace OData2Linq +namespace OData2Linq.Tests { using Microsoft.AspNetCore.OData.Query.Wrapper; using Microsoft.OData; - using OData2Linq.SampleData; + using OData2Linq.Tests.SampleData; using System.Collections.Generic; using System.Linq; using Xunit; diff --git a/test/OData2Linq.Tests/FilterDataContractTests.cs b/test/OData2Linq.Tests/FilterDataContractTests.cs index f33ce6f4f..6df744b60 100644 --- a/test/OData2Linq.Tests/FilterDataContractTests.cs +++ b/test/OData2Linq.Tests/FilterDataContractTests.cs @@ -1,8 +1,8 @@ using Microsoft.OData; -namespace OData2Linq +namespace OData2Linq.Tests { - using OData2Linq.SampleData; + using OData2Linq.Tests.SampleData; using System.Linq; using Xunit; diff --git a/test/OData2Linq.Tests/FilterFunctionsTests.cs b/test/OData2Linq.Tests/FilterFunctionsTests.cs index d43e7f08c..02449a9b8 100644 --- a/test/OData2Linq.Tests/FilterFunctionsTests.cs +++ b/test/OData2Linq.Tests/FilterFunctionsTests.cs @@ -1,10 +1,8 @@ - - -namespace OData2Linq +namespace OData2Linq.Tests { - using OData2Linq.SampleData; - using Xunit; + using OData2Linq.Tests.SampleData; using System.Linq; + using Xunit; public class FilterFunctionsTests { diff --git a/test/OData2Linq.Tests/FilterNavigationCollectionTests.cs b/test/OData2Linq.Tests/FilterNavigationCollectionTests.cs index 1dc84b7e3..b9eb945e5 100644 --- a/test/OData2Linq.Tests/FilterNavigationCollectionTests.cs +++ b/test/OData2Linq.Tests/FilterNavigationCollectionTests.cs @@ -1,10 +1,8 @@ -namespace OData2Linq +namespace OData2Linq.Tests { + using OData2Linq.Tests.SampleData; using System.Collections; using System.Linq; - - using OData2Linq.SampleData; - using Xunit; public class FilterNavigationCollectionTests diff --git a/test/OData2Linq.Tests/FilterNavigationLinkTests.cs b/test/OData2Linq.Tests/FilterNavigationLinkTests.cs index 630415fd0..1dccdfb0b 100644 --- a/test/OData2Linq.Tests/FilterNavigationLinkTests.cs +++ b/test/OData2Linq.Tests/FilterNavigationLinkTests.cs @@ -1,22 +1,19 @@ -namespace OData2Linq +namespace OData2Linq.Tests { + using Microsoft.OData; + using OData2Linq.Tests.SampleData; using System.Collections; using System.Linq; - - using OData2Linq.SampleData; - - using Microsoft.OData; - using Xunit; public class FilterNavigationLinkTests { [Fact] public void WhereNav1() - { + { var result = ClassWithLink.CreateQuery().OData().Filter("Link1/Id eq 211").ToArray(); - Assert.Single((IEnumerable) result); + Assert.Single((IEnumerable)result); Assert.Equal(21, result[0].Id); } diff --git a/test/OData2Linq.Tests/FilterTests.cs b/test/OData2Linq.Tests/FilterTests.cs index ee2935887..0b6803819 100644 --- a/test/OData2Linq.Tests/FilterTests.cs +++ b/test/OData2Linq.Tests/FilterTests.cs @@ -1,7 +1,7 @@ -namespace OData2Linq +namespace OData2Linq.Tests { - using OData2Linq.SampleData; using Microsoft.OData; + using OData2Linq.Tests.SampleData; using System.Linq; using Xunit; diff --git a/test/OData2Linq.Tests/HashTests.cs b/test/OData2Linq.Tests/HashTests.cs index c83e345f1..4e5a0dec8 100644 --- a/test/OData2Linq.Tests/HashTests.cs +++ b/test/OData2Linq.Tests/HashTests.cs @@ -1,8 +1,6 @@ -using System; +using Xunit; -using Xunit; - -namespace OData2Linq +namespace OData2Linq.Tests { public class HashTests { diff --git a/test/OData2Linq.Tests/Issues/Issue29.cs b/test/OData2Linq.Tests/Issues/Issue29.cs index 8675760a3..dd7b8d2d1 100644 --- a/test/OData2Linq.Tests/Issues/Issue29.cs +++ b/test/OData2Linq.Tests/Issues/Issue29.cs @@ -7,7 +7,7 @@ //using Xunit; //using Community.OData.Linq.Json; -//namespace OData2Linq.Issues29 +//namespace OData2Linq.Tests.Issues29 //{ // using Microsoft.AspNetCore.OData.Query.Wrapper; // using System.Diagnostics; diff --git a/test/OData2Linq.Tests/Issues/Issue31.cs b/test/OData2Linq.Tests/Issues/Issue31.cs index 8b5f650ba..ce9a9fc63 100644 --- a/test/OData2Linq.Tests/Issues/Issue31.cs +++ b/test/OData2Linq.Tests/Issues/Issue31.cs @@ -1,7 +1,7 @@  -namespace OData2Linq.Issues +namespace OData2Linq.Tests.Issues { - using OData2Linq.SampleData; + using OData2Linq.Tests.SampleData; using System.Linq; using Xunit; diff --git a/test/OData2Linq.Tests/Issues/Issue32.cs b/test/OData2Linq.Tests/Issues/Issue32.cs index 62c79bc7f..76f884d90 100644 --- a/test/OData2Linq.Tests/Issues/Issue32.cs +++ b/test/OData2Linq.Tests/Issues/Issue32.cs @@ -1,4 +1,4 @@ -//namespace OData2Linq.Issues29 +//namespace OData2Linq.Tests.Issues29 //{ // using System; // using System.Linq; diff --git a/test/OData2Linq.Tests/Issues/Issue33.cs b/test/OData2Linq.Tests/Issues/Issue33.cs index ee89a2974..71f38fe35 100644 --- a/test/OData2Linq.Tests/Issues/Issue33.cs +++ b/test/OData2Linq.Tests/Issues/Issue33.cs @@ -1,6 +1,6 @@ // Issue 33 : Recursive loop of complex types -namespace OData2Linq.Issues33 +namespace OData2Linq.Tests.Issues33 { using System; using System.Linq; diff --git a/test/OData2Linq.Tests/JsonTests.cs b/test/OData2Linq.Tests/JsonTests.cs index bacd29824..6ea9c2bbe 100644 --- a/test/OData2Linq.Tests/JsonTests.cs +++ b/test/OData2Linq.Tests/JsonTests.cs @@ -1,4 +1,4 @@ -namespace OData2Linq +namespace OData2Linq.Tests { using Newtonsoft.Json; using Newtonsoft.Json.Linq; @@ -7,7 +7,7 @@ public class JsonTests { - [Fact] + [Fact(Skip = "Currently not supported")] public void SerializeSelectExpand() { JToken token = null;// ClassWithCollection.CreateQuery().OData().SelectExpand("Name", "Link2($filter=Id eq 311;$select=Name)").ToJson(); @@ -16,7 +16,7 @@ public void SerializeSelectExpand() Assert.DoesNotContain("ModelID", token.ToString(Formatting.None)); } - [Fact] + [Fact(Skip = "Currently not supported")] public void SerializeSelectExpand2() { JToken token = null;// ClassWithCollection.CreateQuery().OData().SelectExpand("Name", "Link2($filter=Id eq 311;$select=Name)").ToJson(); diff --git a/test/OData2Linq.Tests/ODataTests.cs b/test/OData2Linq.Tests/ODataTests.cs index 591a72053..1bcedf679 100644 --- a/test/OData2Linq.Tests/ODataTests.cs +++ b/test/OData2Linq.Tests/ODataTests.cs @@ -1,13 +1,9 @@ -namespace OData2Linq +namespace OData2Linq.Tests { + using OData2Linq.Tests.SampleData; using System; using System.Collections.Generic; using System.Linq; - - using OData2Linq.SampleData; - - using Microsoft.OData; - using Xunit; public class ODataTests diff --git a/test/OData2Linq.Tests/OpenTypesTests.cs b/test/OData2Linq.Tests/OpenTypesTests.cs index e6b8d1976..ee6210e3c 100644 --- a/test/OData2Linq.Tests/OpenTypesTests.cs +++ b/test/OData2Linq.Tests/OpenTypesTests.cs @@ -1,8 +1,7 @@ - -namespace OData2Linq +namespace OData2Linq.Tests { - using OData2Linq.SampleData; using Microsoft.AspNetCore.OData.Query; + using OData2Linq.Tests.SampleData; using System; using System.Linq; using Xunit; diff --git a/test/OData2Linq.Tests/OrderByTests.cs b/test/OData2Linq.Tests/OrderByTests.cs index 1df661e2f..824805452 100644 --- a/test/OData2Linq.Tests/OrderByTests.cs +++ b/test/OData2Linq.Tests/OrderByTests.cs @@ -1,12 +1,9 @@ -using System.Linq; -using OData2Linq.SampleData; -using Microsoft.OData; +using Microsoft.OData; +using OData2Linq.Tests.SampleData; using Xunit; -namespace OData2Linq +namespace OData2Linq.Tests { - using System; - public class OrderByTests { [Fact] diff --git a/test/OData2Linq.Tests/SampleData/ClassWithCollection.cs b/test/OData2Linq.Tests/SampleData/ClassWithCollection.cs index 0eefdf90b..8e8c38ebe 100644 --- a/test/OData2Linq.Tests/SampleData/ClassWithCollection.cs +++ b/test/OData2Linq.Tests/SampleData/ClassWithCollection.cs @@ -1,4 +1,4 @@ -namespace OData2Linq.SampleData +namespace OData2Linq.Tests.SampleData { using System.Collections.Generic; using System.Linq; diff --git a/test/OData2Linq.Tests/SampleData/ClassWithDeepNavigation.cs b/test/OData2Linq.Tests/SampleData/ClassWithDeepNavigation.cs index 22707c65d..14f5c4d5c 100644 --- a/test/OData2Linq.Tests/SampleData/ClassWithDeepNavigation.cs +++ b/test/OData2Linq.Tests/SampleData/ClassWithDeepNavigation.cs @@ -1,7 +1,6 @@ using Microsoft.OData.ModelBuilder; -using System.Linq; -namespace OData2Linq.SampleData +namespace OData2Linq.Tests.SampleData { public class ClassWithDeepNavigation { diff --git a/test/OData2Linq.Tests/SampleData/ClassWithLink.cs b/test/OData2Linq.Tests/SampleData/ClassWithLink.cs index 74cef3056..a8b2d463d 100644 --- a/test/OData2Linq.Tests/SampleData/ClassWithLink.cs +++ b/test/OData2Linq.Tests/SampleData/ClassWithLink.cs @@ -1,4 +1,4 @@ -namespace OData2Linq.SampleData +namespace OData2Linq.Tests.SampleData { using Microsoft.OData.ModelBuilder; using System.Linq; diff --git a/test/OData2Linq.Tests/SampleData/OpenType.cs b/test/OData2Linq.Tests/SampleData/OpenType.cs index 4f4775e71..47bf94107 100644 --- a/test/OData2Linq.Tests/SampleData/OpenType.cs +++ b/test/OData2Linq.Tests/SampleData/OpenType.cs @@ -1,9 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace OData2Linq.SampleData +namespace OData2Linq.Tests.SampleData { public class OpenType { diff --git a/test/OData2Linq.Tests/SampleData/SampleWithCustomKey.cs b/test/OData2Linq.Tests/SampleData/SampleWithCustomKey.cs index a34638f85..2a5249814 100644 --- a/test/OData2Linq.Tests/SampleData/SampleWithCustomKey.cs +++ b/test/OData2Linq.Tests/SampleData/SampleWithCustomKey.cs @@ -1,4 +1,4 @@ -namespace OData2Linq.SampleData +namespace OData2Linq.Tests.SampleData { using Microsoft.OData.ModelBuilder; using System; diff --git a/test/OData2Linq.Tests/SampleData/SampleWithoutKey.cs b/test/OData2Linq.Tests/SampleData/SampleWithoutKey.cs index 8315f4b2e..bd807b01d 100644 --- a/test/OData2Linq.Tests/SampleData/SampleWithoutKey.cs +++ b/test/OData2Linq.Tests/SampleData/SampleWithoutKey.cs @@ -1,4 +1,4 @@ -namespace OData2Linq.SampleData +namespace OData2Linq.Tests.SampleData { using System; using System.Linq; diff --git a/test/OData2Linq.Tests/SampleData/SimpleClass.cs b/test/OData2Linq.Tests/SampleData/SimpleClass.cs index d5c769c5b..a773dca7d 100644 --- a/test/OData2Linq.Tests/SampleData/SimpleClass.cs +++ b/test/OData2Linq.Tests/SampleData/SimpleClass.cs @@ -1,6 +1,6 @@ using System.Runtime.Serialization; -namespace OData2Linq.SampleData +namespace OData2Linq.Tests.SampleData { using Microsoft.OData.ModelBuilder; using System; diff --git a/test/OData2Linq.Tests/SampleData/SimpleClassDataContract.cs b/test/OData2Linq.Tests/SampleData/SimpleClassDataContract.cs index 11233b571..16653cc35 100644 --- a/test/OData2Linq.Tests/SampleData/SimpleClassDataContract.cs +++ b/test/OData2Linq.Tests/SampleData/SimpleClassDataContract.cs @@ -1,4 +1,4 @@ -namespace OData2Linq.SampleData +namespace OData2Linq.Tests.SampleData { using System.Linq; using System.Runtime.Serialization; diff --git a/test/OData2Linq.Tests/SampleData/TestEnum.cs b/test/OData2Linq.Tests/SampleData/TestEnum.cs index cfaf641d5..634bd894e 100644 --- a/test/OData2Linq.Tests/SampleData/TestEnum.cs +++ b/test/OData2Linq.Tests/SampleData/TestEnum.cs @@ -1,4 +1,4 @@ -namespace OData2Linq.SampleData +namespace OData2Linq.Tests.SampleData { public enum TestEnum { diff --git a/test/OData2Linq.Tests/SampleData/TestItem.cs b/test/OData2Linq.Tests/SampleData/TestItem.cs index 853fdc25a..50250ec2b 100644 --- a/test/OData2Linq.Tests/SampleData/TestItem.cs +++ b/test/OData2Linq.Tests/SampleData/TestItem.cs @@ -1,6 +1,6 @@ using System; -namespace OData2Linq.SampleData +namespace OData2Linq.Tests.SampleData { /// /// A utility class for use in ODataTests diff --git a/test/OData2Linq.Tests/SelectTests.cs b/test/OData2Linq.Tests/SelectTests.cs index c8db7a13c..3572963ec 100644 --- a/test/OData2Linq.Tests/SelectTests.cs +++ b/test/OData2Linq.Tests/SelectTests.cs @@ -1,8 +1,8 @@ -namespace OData2Linq +namespace OData2Linq.Tests { - using OData2Linq.SampleData; using Microsoft.AspNetCore.OData.Query.Wrapper; using Microsoft.OData; + using OData2Linq.Tests.SampleData; using System; using System.Collections.Generic; using System.Linq; diff --git a/test/OData2Linq.Tests/TopSkipTests.cs b/test/OData2Linq.Tests/TopSkipTests.cs index 324e14718..5cb13d645 100644 --- a/test/OData2Linq.Tests/TopSkipTests.cs +++ b/test/OData2Linq.Tests/TopSkipTests.cs @@ -1,12 +1,8 @@ -namespace OData2Linq +namespace OData2Linq.Tests { - using System.Collections; - using System.Linq; - - using OData2Linq.SampleData; - using Microsoft.OData; - + using OData2Linq.Tests.SampleData; + using System.Linq; using Xunit; public class TopSkipTests From 0a05a5e7b95ff15f21da3253782039d6b87ec6a6 Mon Sep 17 00:00:00 2001 From: Arnaud Boussaer Date: Tue, 11 Feb 2025 15:57:04 +0100 Subject: [PATCH 18/53] add CI pipeline --- .github/workflows/odata2linq-ci.yml | 31 +++++++++++++++++++++++++++++ src/OData2Linq/OData2Linq.csproj | 1 + 2 files changed, 32 insertions(+) create mode 100644 .github/workflows/odata2linq-ci.yml diff --git a/.github/workflows/odata2linq-ci.yml b/.github/workflows/odata2linq-ci.yml new file mode 100644 index 000000000..a315798bf --- /dev/null +++ b/.github/workflows/odata2linq-ci.yml @@ -0,0 +1,31 @@ +# This workflow will build a .NET project +# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-net + +name: .NET + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + +jobs: + build: + + runs-on: ubuntu-latest + strategy: + matrix: + dotnet-version: [ '3.1.x', '6.0.x', 8.0.x ] + + steps: + - uses: actions/checkout@v4 + - name: Setup dotnet ${{ matrix.dotnet-version }} + uses: actions/setup-dotnet@v4 + with: + dotnet-version: ${{ matrix.dotnet-version }} + - name: Restore dependencies + run: dotnet restore + - name: Build + run: dotnet build --no-restore + - name: Test + run: dotnet test --no-build --verbosity normal diff --git a/src/OData2Linq/OData2Linq.csproj b/src/OData2Linq/OData2Linq.csproj index cc4ccf6b9..4e662ec06 100644 --- a/src/OData2Linq/OData2Linq.csproj +++ b/src/OData2Linq/OData2Linq.csproj @@ -32,5 +32,6 @@ True \ + From 1f83cc8eab8f810907916556a0a66346b362e0ac Mon Sep 17 00:00:00 2001 From: ArnaudB88 Date: Tue, 11 Feb 2025 16:02:45 +0100 Subject: [PATCH 19/53] Create odata2linq-ci2.yml --- .github/workflows/odata2linq-ci2.yml | 31 ++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 .github/workflows/odata2linq-ci2.yml diff --git a/.github/workflows/odata2linq-ci2.yml b/.github/workflows/odata2linq-ci2.yml new file mode 100644 index 000000000..a315798bf --- /dev/null +++ b/.github/workflows/odata2linq-ci2.yml @@ -0,0 +1,31 @@ +# This workflow will build a .NET project +# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-net + +name: .NET + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + +jobs: + build: + + runs-on: ubuntu-latest + strategy: + matrix: + dotnet-version: [ '3.1.x', '6.0.x', 8.0.x ] + + steps: + - uses: actions/checkout@v4 + - name: Setup dotnet ${{ matrix.dotnet-version }} + uses: actions/setup-dotnet@v4 + with: + dotnet-version: ${{ matrix.dotnet-version }} + - name: Restore dependencies + run: dotnet restore + - name: Build + run: dotnet build --no-restore + - name: Test + run: dotnet test --no-build --verbosity normal From bfd7dcee30951f10ba5c35dbcc1f2706e46aeccc Mon Sep 17 00:00:00 2001 From: Arnaud Boussaer Date: Tue, 11 Feb 2025 16:09:53 +0100 Subject: [PATCH 20/53] remove duplicate flow --- .github/workflows/odata2linq-ci2.yml | 31 ---------------------------- 1 file changed, 31 deletions(-) delete mode 100644 .github/workflows/odata2linq-ci2.yml diff --git a/.github/workflows/odata2linq-ci2.yml b/.github/workflows/odata2linq-ci2.yml deleted file mode 100644 index a315798bf..000000000 --- a/.github/workflows/odata2linq-ci2.yml +++ /dev/null @@ -1,31 +0,0 @@ -# This workflow will build a .NET project -# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-net - -name: .NET - -on: - push: - branches: [ "main" ] - pull_request: - branches: [ "main" ] - -jobs: - build: - - runs-on: ubuntu-latest - strategy: - matrix: - dotnet-version: [ '3.1.x', '6.0.x', 8.0.x ] - - steps: - - uses: actions/checkout@v4 - - name: Setup dotnet ${{ matrix.dotnet-version }} - uses: actions/setup-dotnet@v4 - with: - dotnet-version: ${{ matrix.dotnet-version }} - - name: Restore dependencies - run: dotnet restore - - name: Build - run: dotnet build --no-restore - - name: Test - run: dotnet test --no-build --verbosity normal From 8512499d64b3e0eefafb7930c89bde972721aa4e Mon Sep 17 00:00:00 2001 From: Arnaud Boussaer Date: Tue, 11 Feb 2025 16:34:18 +0100 Subject: [PATCH 21/53] rollback conflicting changes --- AspNetCoreOData.sln | 28 +++++++++---------- .../ODataSampleCommon.csproj | 2 +- .../Microsoft.AspNetCore.OData.csproj | 3 +- src/OData2Linq/OData2Linq.csproj | 2 +- ...icrosoft.AspNetCore.OData.E2E.Tests.csproj | 2 +- ...crosoft.AspNetCore.OData.TestCommon.csproj | 2 +- .../Microsoft.AspNetCore.OData.Tests.csproj | 2 +- test/OData2Linq.Tests/OData2Linq.Tests.csproj | 2 +- 8 files changed, 22 insertions(+), 21 deletions(-) diff --git a/AspNetCoreOData.sln b/AspNetCoreOData.sln index 2b16a34bd..026b36655 100644 --- a/AspNetCoreOData.sln +++ b/AspNetCoreOData.sln @@ -19,6 +19,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.OData. EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.OData.TestCommon", "test\Microsoft.AspNetCore.OData.TestCommon\Microsoft.AspNetCore.OData.TestCommon.csproj", "{9DB7ACCB-79CC-495C-A145-BC50A7733A21}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OData2Linq.Tests", "test\OData2Linq.Tests\OData2Linq.Tests.csproj", "{E7F48129-7544-4D85-ABE1-4CB037E1C780}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OData2Linq", "src\OData2Linq\OData2Linq.csproj", "{568A35C2-8677-41C6-9098-3AEF23286A9E}" +EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ODataCustomizedSample", "sample\ODataCustomizedSample\ODataCustomizedSample.csproj", "{BDC5474B-9511-4CDF-83FE-376C7130F7F0}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ODataDynamicModel", "sample\ODataDynamicModel\ODataDynamicModel.csproj", "{CE04E38B-547F-46C0-ABE4-F981E3A1874F}" @@ -27,10 +31,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}") = "OData2Linq.Tests", "test\OData2Linq.Tests\OData2Linq.Tests.csproj", "{E7F48129-7544-4D85-ABE1-4CB037E1C780}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OData2Linq", "src\OData2Linq\OData2Linq.csproj", "{568A35C2-8677-41C6-9098-3AEF23286A9E}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -57,6 +57,14 @@ Global {9DB7ACCB-79CC-495C-A145-BC50A7733A21}.Debug|Any CPU.Build.0 = Debug|Any CPU {9DB7ACCB-79CC-495C-A145-BC50A7733A21}.Release|Any CPU.ActiveCfg = Release|Any CPU {9DB7ACCB-79CC-495C-A145-BC50A7733A21}.Release|Any CPU.Build.0 = Release|Any CPU + {E7F48129-7544-4D85-ABE1-4CB037E1C780}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E7F48129-7544-4D85-ABE1-4CB037E1C780}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E7F48129-7544-4D85-ABE1-4CB037E1C780}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E7F48129-7544-4D85-ABE1-4CB037E1C780}.Release|Any CPU.Build.0 = Release|Any CPU + {568A35C2-8677-41C6-9098-3AEF23286A9E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {568A35C2-8677-41C6-9098-3AEF23286A9E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {568A35C2-8677-41C6-9098-3AEF23286A9E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {568A35C2-8677-41C6-9098-3AEF23286A9E}.Release|Any CPU.Build.0 = Release|Any CPU {BDC5474B-9511-4CDF-83FE-376C7130F7F0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {BDC5474B-9511-4CDF-83FE-376C7130F7F0}.Debug|Any CPU.Build.0 = Debug|Any CPU {BDC5474B-9511-4CDF-83FE-376C7130F7F0}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -73,14 +81,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 - {E7F48129-7544-4D85-ABE1-4CB037E1C780}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E7F48129-7544-4D85-ABE1-4CB037E1C780}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E7F48129-7544-4D85-ABE1-4CB037E1C780}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E7F48129-7544-4D85-ABE1-4CB037E1C780}.Release|Any CPU.Build.0 = Release|Any CPU - {568A35C2-8677-41C6-9098-3AEF23286A9E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {568A35C2-8677-41C6-9098-3AEF23286A9E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {568A35C2-8677-41C6-9098-3AEF23286A9E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {568A35C2-8677-41C6-9098-3AEF23286A9E}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -91,12 +91,12 @@ Global {FF17DFD5-7ED0-433F-8DD9-4576E0CFFFBA} = {F994C269-55BA-44F0-9DA7-6D5A3CFA79EB} {E428817E-A93C-4C1F-9D20-F5CA8C3627C1} = {F994C269-55BA-44F0-9DA7-6D5A3CFA79EB} {9DB7ACCB-79CC-495C-A145-BC50A7733A21} = {F994C269-55BA-44F0-9DA7-6D5A3CFA79EB} + {E7F48129-7544-4D85-ABE1-4CB037E1C780} = {F994C269-55BA-44F0-9DA7-6D5A3CFA79EB} + {568A35C2-8677-41C6-9098-3AEF23286A9E} = {2F0E102B-EB33-4025-BE56-7B8F9D2C4B8A} {BDC5474B-9511-4CDF-83FE-376C7130F7F0} = {B1F86961-6958-4617-ACA4-C231F95AE099} {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} - {E7F48129-7544-4D85-ABE1-4CB037E1C780} = {F994C269-55BA-44F0-9DA7-6D5A3CFA79EB} - {568A35C2-8677-41C6-9098-3AEF23286A9E} = {2F0E102B-EB33-4025-BE56-7B8F9D2C4B8A} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {540C9752-AAC0-49EA-BA60-78490C90FF86} diff --git a/sample/ODataSampleCommon/ODataSampleCommon.csproj b/sample/ODataSampleCommon/ODataSampleCommon.csproj index 3e0a50a8d..3d4bc25fd 100644 --- a/sample/ODataSampleCommon/ODataSampleCommon.csproj +++ b/sample/ODataSampleCommon/ODataSampleCommon.csproj @@ -1,7 +1,7 @@  - net6.0 + net5.0 Library true diff --git a/src/Microsoft.AspNetCore.OData/Microsoft.AspNetCore.OData.csproj b/src/Microsoft.AspNetCore.OData/Microsoft.AspNetCore.OData.csproj index 5e7dbe8fa..b526c3f2a 100644 --- a/src/Microsoft.AspNetCore.OData/Microsoft.AspNetCore.OData.csproj +++ b/src/Microsoft.AspNetCore.OData/Microsoft.AspNetCore.OData.csproj @@ -1,7 +1,8 @@  - net6.0 + netcoreapp3.1;net5.0 + $(TargetFrameworks);net6.0 Microsoft.AspNetCore.OData $(OutputPath)$(AssemblyName).xml true diff --git a/src/OData2Linq/OData2Linq.csproj b/src/OData2Linq/OData2Linq.csproj index 4e662ec06..144a810fb 100644 --- a/src/OData2Linq/OData2Linq.csproj +++ b/src/OData2Linq/OData2Linq.csproj @@ -1,7 +1,7 @@  - net6.0 + netcoreapp3.1;net6.0 enable enable True diff --git a/test/Microsoft.AspNetCore.OData.E2E.Tests/Microsoft.AspNetCore.OData.E2E.Tests.csproj b/test/Microsoft.AspNetCore.OData.E2E.Tests/Microsoft.AspNetCore.OData.E2E.Tests.csproj index ea54445ad..b4073ca90 100644 --- a/test/Microsoft.AspNetCore.OData.E2E.Tests/Microsoft.AspNetCore.OData.E2E.Tests.csproj +++ b/test/Microsoft.AspNetCore.OData.E2E.Tests/Microsoft.AspNetCore.OData.E2E.Tests.csproj @@ -1,7 +1,7 @@  - net6.0 + netcoreapp3.1;net6.0 Microsoft.AspNetCore.OData.E2E.Tests Microsoft.AspNetCore.OData.E2E.Tests diff --git a/test/Microsoft.AspNetCore.OData.TestCommon/Microsoft.AspNetCore.OData.TestCommon.csproj b/test/Microsoft.AspNetCore.OData.TestCommon/Microsoft.AspNetCore.OData.TestCommon.csproj index 54252380b..a6922e9bd 100644 --- a/test/Microsoft.AspNetCore.OData.TestCommon/Microsoft.AspNetCore.OData.TestCommon.csproj +++ b/test/Microsoft.AspNetCore.OData.TestCommon/Microsoft.AspNetCore.OData.TestCommon.csproj @@ -1,7 +1,7 @@  - net6.0 + netcoreapp3.1;net6.0 Microsoft.AspNetCore.OData.TestCommon Microsoft.AspNetCore.OData.TestCommon diff --git a/test/Microsoft.AspNetCore.OData.Tests/Microsoft.AspNetCore.OData.Tests.csproj b/test/Microsoft.AspNetCore.OData.Tests/Microsoft.AspNetCore.OData.Tests.csproj index 88ad04d2d..d105f21ba 100644 --- a/test/Microsoft.AspNetCore.OData.Tests/Microsoft.AspNetCore.OData.Tests.csproj +++ b/test/Microsoft.AspNetCore.OData.Tests/Microsoft.AspNetCore.OData.Tests.csproj @@ -1,7 +1,7 @@  - net6.0 + netcoreapp3.1;net6.0 Microsoft.AspNetCore.OData.Tests Microsoft.AspNetCore.OData.Tests diff --git a/test/OData2Linq.Tests/OData2Linq.Tests.csproj b/test/OData2Linq.Tests/OData2Linq.Tests.csproj index 507002fdc..ae31519d0 100644 --- a/test/OData2Linq.Tests/OData2Linq.Tests.csproj +++ b/test/OData2Linq.Tests/OData2Linq.Tests.csproj @@ -1,7 +1,7 @@  - net6.0 + netcoreapp3.1;net6.0 latest enable enable From 4801ac9b70d4c7fea6962cc09738fe39633d52ff Mon Sep 17 00:00:00 2001 From: Arnaud Boussaer Date: Tue, 11 Feb 2025 16:44:40 +0100 Subject: [PATCH 22/53] fix pipeline --- .github/workflows/odata2linq-ci.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/odata2linq-ci.yml b/.github/workflows/odata2linq-ci.yml index a315798bf..03fe38643 100644 --- a/.github/workflows/odata2linq-ci.yml +++ b/.github/workflows/odata2linq-ci.yml @@ -24,8 +24,8 @@ jobs: with: dotnet-version: ${{ matrix.dotnet-version }} - name: Restore dependencies - run: dotnet restore + run: dotnet restore AspNetCoreOData.sln - name: Build - run: dotnet build --no-restore + run: dotnet build AspNetCoreOData.sln --no-restore - name: Test - run: dotnet test --no-build --verbosity normal + run: dotnet test AspNetCoreOData.sln --no-build --verbosity normal From 9adec7a829a3259ab444e84ab6bf2c75b9d06481 Mon Sep 17 00:00:00 2001 From: Arnaud Boussaer Date: Tue, 11 Feb 2025 16:46:53 +0100 Subject: [PATCH 23/53] fix --- .github/workflows/odata2linq-ci.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/odata2linq-ci.yml b/.github/workflows/odata2linq-ci.yml index 03fe38643..13ba90ecb 100644 --- a/.github/workflows/odata2linq-ci.yml +++ b/.github/workflows/odata2linq-ci.yml @@ -24,8 +24,8 @@ jobs: with: dotnet-version: ${{ matrix.dotnet-version }} - name: Restore dependencies - run: dotnet restore AspNetCoreOData.sln + run: dotnet restore ./AspNetCoreOData.sln - name: Build - run: dotnet build AspNetCoreOData.sln --no-restore + run: dotnet build ./AspNetCoreOData.sln --no-restore - name: Test - run: dotnet test AspNetCoreOData.sln --no-build --verbosity normal + run: dotnet test ./AspNetCoreOData.sln --no-build --verbosity normal From 8a5aece86c574897ebad75348b2418ddd529d77b Mon Sep 17 00:00:00 2001 From: Arnaud Boussaer Date: Tue, 11 Feb 2025 16:53:54 +0100 Subject: [PATCH 24/53] fix TMFs --- .github/workflows/odata2linq-ci.yml | 2 +- src/OData2Linq/OData2Linq.csproj | 2 +- test/OData2Linq.Tests/OData2Linq.Tests.csproj | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/odata2linq-ci.yml b/.github/workflows/odata2linq-ci.yml index 13ba90ecb..e9b64a288 100644 --- a/.github/workflows/odata2linq-ci.yml +++ b/.github/workflows/odata2linq-ci.yml @@ -15,7 +15,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - dotnet-version: [ '3.1.x', '6.0.x', 8.0.x ] + dotnet-version: ['6.0.x', 8.0.x ] steps: - uses: actions/checkout@v4 diff --git a/src/OData2Linq/OData2Linq.csproj b/src/OData2Linq/OData2Linq.csproj index 144a810fb..d002cdfee 100644 --- a/src/OData2Linq/OData2Linq.csproj +++ b/src/OData2Linq/OData2Linq.csproj @@ -1,7 +1,7 @@  - netcoreapp3.1;net6.0 + netcoreapp3.1;net6.0 enable enable True diff --git a/test/OData2Linq.Tests/OData2Linq.Tests.csproj b/test/OData2Linq.Tests/OData2Linq.Tests.csproj index ae31519d0..55bdfdf2b 100644 --- a/test/OData2Linq.Tests/OData2Linq.Tests.csproj +++ b/test/OData2Linq.Tests/OData2Linq.Tests.csproj @@ -1,7 +1,7 @@  - netcoreapp3.1;net6.0 + netcoreapp3.1;net6.0 latest enable enable From 1dbd36e6e366e8c0911346687807cc499ea56354 Mon Sep 17 00:00:00 2001 From: Arnaud Boussaer Date: Tue, 11 Feb 2025 16:55:50 +0100 Subject: [PATCH 25/53] fix net8 --- src/OData2Linq/OData2Linq.csproj | 2 +- test/OData2Linq.Tests/OData2Linq.Tests.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/OData2Linq/OData2Linq.csproj b/src/OData2Linq/OData2Linq.csproj index d002cdfee..09b5e878f 100644 --- a/src/OData2Linq/OData2Linq.csproj +++ b/src/OData2Linq/OData2Linq.csproj @@ -1,7 +1,7 @@  - netcoreapp3.1;net6.0 + netcoreapp3.1;net6.0;net8.0 enable enable True diff --git a/test/OData2Linq.Tests/OData2Linq.Tests.csproj b/test/OData2Linq.Tests/OData2Linq.Tests.csproj index 55bdfdf2b..72cb7d58f 100644 --- a/test/OData2Linq.Tests/OData2Linq.Tests.csproj +++ b/test/OData2Linq.Tests/OData2Linq.Tests.csproj @@ -1,7 +1,7 @@  - netcoreapp3.1;net6.0 + netcoreapp3.1;net6.0;net8.0 latest enable enable From 6403933cd4a80980455aa84fffc5274d81a5afbb Mon Sep 17 00:00:00 2001 From: Arnaud Boussaer Date: Tue, 11 Feb 2025 17:04:19 +0100 Subject: [PATCH 26/53] fix tmf --- .../Microsoft.AspNetCore.OData.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Microsoft.AspNetCore.OData/Microsoft.AspNetCore.OData.csproj b/src/Microsoft.AspNetCore.OData/Microsoft.AspNetCore.OData.csproj index b526c3f2a..6b5875602 100644 --- a/src/Microsoft.AspNetCore.OData/Microsoft.AspNetCore.OData.csproj +++ b/src/Microsoft.AspNetCore.OData/Microsoft.AspNetCore.OData.csproj @@ -1,7 +1,7 @@  - netcoreapp3.1;net5.0 + netcoreapp3.1;net5.0;net6.0;net8.0 $(TargetFrameworks);net6.0 Microsoft.AspNetCore.OData $(OutputPath)$(AssemblyName).xml From cae726cfe623b0dde90fd1a4fc3d97da29ddce69 Mon Sep 17 00:00:00 2001 From: Arnaud Boussaer Date: Tue, 11 Feb 2025 17:06:59 +0100 Subject: [PATCH 27/53] fix conflicts --- .../Microsoft.AspNetCore.OData.csproj | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Microsoft.AspNetCore.OData/Microsoft.AspNetCore.OData.csproj b/src/Microsoft.AspNetCore.OData/Microsoft.AspNetCore.OData.csproj index 6b5875602..cd1768ff9 100644 --- a/src/Microsoft.AspNetCore.OData/Microsoft.AspNetCore.OData.csproj +++ b/src/Microsoft.AspNetCore.OData/Microsoft.AspNetCore.OData.csproj @@ -1,8 +1,9 @@  - netcoreapp3.1;net5.0;net6.0;net8.0 + netcoreapp3.1;net5.0 $(TargetFrameworks);net6.0 + net6.0;net8.0 Microsoft.AspNetCore.OData $(OutputPath)$(AssemblyName).xml true From 5a4aa09f4f738da74f91d79c37b79bf1d099193e Mon Sep 17 00:00:00 2001 From: Arnaud Boussaer Date: Tue, 11 Feb 2025 17:07:34 +0100 Subject: [PATCH 28/53] fix --- .../Microsoft.AspNetCore.OData.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Microsoft.AspNetCore.OData/Microsoft.AspNetCore.OData.csproj b/src/Microsoft.AspNetCore.OData/Microsoft.AspNetCore.OData.csproj index cd1768ff9..9d0e66f15 100644 --- a/src/Microsoft.AspNetCore.OData/Microsoft.AspNetCore.OData.csproj +++ b/src/Microsoft.AspNetCore.OData/Microsoft.AspNetCore.OData.csproj @@ -3,8 +3,8 @@ netcoreapp3.1;net5.0 $(TargetFrameworks);net6.0 - net6.0;net8.0 Microsoft.AspNetCore.OData + net6.0;net8.0 $(OutputPath)$(AssemblyName).xml true From bf02db41e982b60c24b14b60f2c42dee64957656 Mon Sep 17 00:00:00 2001 From: Arnaud Boussaer Date: Tue, 11 Feb 2025 17:28:13 +0100 Subject: [PATCH 29/53] Revert "remove duplicate flow" This reverts commit bfd7dcee30951f10ba5c35dbcc1f2706e46aeccc. --- .github/workflows/odata2linq-ci2.yml | 31 ++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 .github/workflows/odata2linq-ci2.yml diff --git a/.github/workflows/odata2linq-ci2.yml b/.github/workflows/odata2linq-ci2.yml new file mode 100644 index 000000000..a315798bf --- /dev/null +++ b/.github/workflows/odata2linq-ci2.yml @@ -0,0 +1,31 @@ +# This workflow will build a .NET project +# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-net + +name: .NET + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + +jobs: + build: + + runs-on: ubuntu-latest + strategy: + matrix: + dotnet-version: [ '3.1.x', '6.0.x', 8.0.x ] + + steps: + - uses: actions/checkout@v4 + - name: Setup dotnet ${{ matrix.dotnet-version }} + uses: actions/setup-dotnet@v4 + with: + dotnet-version: ${{ matrix.dotnet-version }} + - name: Restore dependencies + run: dotnet restore + - name: Build + run: dotnet build --no-restore + - name: Test + run: dotnet test --no-build --verbosity normal From 03710ce97bf96362a0870b7207c7ddb68edde507 Mon Sep 17 00:00:00 2001 From: Arnaud Boussaer Date: Tue, 11 Feb 2025 17:43:44 +0100 Subject: [PATCH 30/53] rollback all TMF changes --- .github/workflows/odata2linq-ci.yml | 2 +- sample/ODataSampleCommon/ODataSampleCommon.csproj | 2 +- .../Microsoft.AspNetCore.OData.csproj | 4 +--- src/OData2Linq/OData2Linq.csproj | 4 ++-- .../Microsoft.AspNetCore.OData.E2E.Tests.csproj | 2 +- .../Microsoft.AspNetCore.OData.TestCommon.csproj | 2 +- .../Microsoft.AspNetCore.OData.Tests.csproj | 2 +- test/OData2Linq.Tests/OData2Linq.Tests.csproj | 2 +- 8 files changed, 9 insertions(+), 11 deletions(-) diff --git a/.github/workflows/odata2linq-ci.yml b/.github/workflows/odata2linq-ci.yml index e9b64a288..d96c06816 100644 --- a/.github/workflows/odata2linq-ci.yml +++ b/.github/workflows/odata2linq-ci.yml @@ -15,7 +15,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - dotnet-version: ['6.0.x', 8.0.x ] + dotnet-version: ['6.0.x'] steps: - uses: actions/checkout@v4 diff --git a/sample/ODataSampleCommon/ODataSampleCommon.csproj b/sample/ODataSampleCommon/ODataSampleCommon.csproj index 3d4bc25fd..3e0a50a8d 100644 --- a/sample/ODataSampleCommon/ODataSampleCommon.csproj +++ b/sample/ODataSampleCommon/ODataSampleCommon.csproj @@ -1,7 +1,7 @@  - net5.0 + net6.0 Library true diff --git a/src/Microsoft.AspNetCore.OData/Microsoft.AspNetCore.OData.csproj b/src/Microsoft.AspNetCore.OData/Microsoft.AspNetCore.OData.csproj index 9d0e66f15..5e7dbe8fa 100644 --- a/src/Microsoft.AspNetCore.OData/Microsoft.AspNetCore.OData.csproj +++ b/src/Microsoft.AspNetCore.OData/Microsoft.AspNetCore.OData.csproj @@ -1,10 +1,8 @@  - netcoreapp3.1;net5.0 - $(TargetFrameworks);net6.0 + net6.0 Microsoft.AspNetCore.OData - net6.0;net8.0 $(OutputPath)$(AssemblyName).xml true diff --git a/src/OData2Linq/OData2Linq.csproj b/src/OData2Linq/OData2Linq.csproj index 09b5e878f..622633cc7 100644 --- a/src/OData2Linq/OData2Linq.csproj +++ b/src/OData2Linq/OData2Linq.csproj @@ -1,13 +1,13 @@  - netcoreapp3.1;net6.0;net8.0 + net6.0 enable enable True FriendAssemblies.snk True - 0.0.5 + 0.0.6 MIT README.md https://github.com/ArnaudB88/OData2Linq diff --git a/test/Microsoft.AspNetCore.OData.E2E.Tests/Microsoft.AspNetCore.OData.E2E.Tests.csproj b/test/Microsoft.AspNetCore.OData.E2E.Tests/Microsoft.AspNetCore.OData.E2E.Tests.csproj index b4073ca90..ea54445ad 100644 --- a/test/Microsoft.AspNetCore.OData.E2E.Tests/Microsoft.AspNetCore.OData.E2E.Tests.csproj +++ b/test/Microsoft.AspNetCore.OData.E2E.Tests/Microsoft.AspNetCore.OData.E2E.Tests.csproj @@ -1,7 +1,7 @@  - netcoreapp3.1;net6.0 + net6.0 Microsoft.AspNetCore.OData.E2E.Tests Microsoft.AspNetCore.OData.E2E.Tests diff --git a/test/Microsoft.AspNetCore.OData.TestCommon/Microsoft.AspNetCore.OData.TestCommon.csproj b/test/Microsoft.AspNetCore.OData.TestCommon/Microsoft.AspNetCore.OData.TestCommon.csproj index a6922e9bd..54252380b 100644 --- a/test/Microsoft.AspNetCore.OData.TestCommon/Microsoft.AspNetCore.OData.TestCommon.csproj +++ b/test/Microsoft.AspNetCore.OData.TestCommon/Microsoft.AspNetCore.OData.TestCommon.csproj @@ -1,7 +1,7 @@  - netcoreapp3.1;net6.0 + net6.0 Microsoft.AspNetCore.OData.TestCommon Microsoft.AspNetCore.OData.TestCommon diff --git a/test/Microsoft.AspNetCore.OData.Tests/Microsoft.AspNetCore.OData.Tests.csproj b/test/Microsoft.AspNetCore.OData.Tests/Microsoft.AspNetCore.OData.Tests.csproj index d105f21ba..88ad04d2d 100644 --- a/test/Microsoft.AspNetCore.OData.Tests/Microsoft.AspNetCore.OData.Tests.csproj +++ b/test/Microsoft.AspNetCore.OData.Tests/Microsoft.AspNetCore.OData.Tests.csproj @@ -1,7 +1,7 @@  - netcoreapp3.1;net6.0 + net6.0 Microsoft.AspNetCore.OData.Tests Microsoft.AspNetCore.OData.Tests diff --git a/test/OData2Linq.Tests/OData2Linq.Tests.csproj b/test/OData2Linq.Tests/OData2Linq.Tests.csproj index 72cb7d58f..184b2569b 100644 --- a/test/OData2Linq.Tests/OData2Linq.Tests.csproj +++ b/test/OData2Linq.Tests/OData2Linq.Tests.csproj @@ -1,7 +1,7 @@  - netcoreapp3.1;net6.0;net8.0 + net6.0 latest enable enable From eafef1998cefd036aadcf70c639afee51bda36c0 Mon Sep 17 00:00:00 2001 From: Arnaud Boussaer Date: Wed, 12 Feb 2025 12:34:23 +0100 Subject: [PATCH 31/53] Add Benchmark project --- AspNetCoreOData.sln | 7 ++ test/OData2Linq.Benchmark/InitQuery.cs | 90 +++++++++++++++++++ .../OData2Linq.Benchmark.csproj | 19 ++++ test/OData2Linq.Benchmark/Program.cs | 9 ++ 4 files changed, 125 insertions(+) create mode 100644 test/OData2Linq.Benchmark/InitQuery.cs create mode 100644 test/OData2Linq.Benchmark/OData2Linq.Benchmark.csproj create mode 100644 test/OData2Linq.Benchmark/Program.cs diff --git a/AspNetCoreOData.sln b/AspNetCoreOData.sln index 026b36655..7b2b729a4 100644 --- a/AspNetCoreOData.sln +++ b/AspNetCoreOData.sln @@ -31,6 +31,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}") = "OData2Linq.Benchmark", "test\OData2Linq.Benchmark\OData2Linq.Benchmark.csproj", "{11B8ED5C-E54C-46A4-A5A7-E880E5EC634E}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -81,6 +83,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 + {11B8ED5C-E54C-46A4-A5A7-E880E5EC634E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {11B8ED5C-E54C-46A4-A5A7-E880E5EC634E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {11B8ED5C-E54C-46A4-A5A7-E880E5EC634E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {11B8ED5C-E54C-46A4-A5A7-E880E5EC634E}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -97,6 +103,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} + {11B8ED5C-E54C-46A4-A5A7-E880E5EC634E} = {F994C269-55BA-44F0-9DA7-6D5A3CFA79EB} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {540C9752-AAC0-49EA-BA60-78490C90FF86} diff --git a/test/OData2Linq.Benchmark/InitQuery.cs b/test/OData2Linq.Benchmark/InitQuery.cs new file mode 100644 index 000000000..1f62e64af --- /dev/null +++ b/test/OData2Linq.Benchmark/InitQuery.cs @@ -0,0 +1,90 @@ +using BenchmarkDotNet.Attributes; +using Microsoft.AspNetCore.OData.Query; +using Microsoft.AspNetCore.OData.Query.Expressions; +using Microsoft.AspNetCore.OData.Query.Validator; +using Microsoft.OData; +using Microsoft.OData.Edm; +using Microsoft.OData.ModelBuilder; +using Microsoft.OData.UriParser; +using OData2Linq.Tests.SampleData; +using System.ComponentModel.Design; + +namespace OData2Linq.Benchmark +{ + public class InitQuery + { + private static readonly ODataSimplifiedOptions SimplifiedOptions = new ODataSimplifiedOptions(); + + private static ODataUriResolver DefaultResolver = new StringAsEnumResolver { EnableCaseInsensitive = true }; + + private static readonly IEdmModel defaultEdmModel; + + private static readonly IQueryable query; + + static InitQuery() + { + query = ClassWithDeepNavigation.CreateQuery(); + + ODataConventionModelBuilder builder = new ODataConventionModelBuilder(); + builder.AddEntityType(typeof(ClassWithDeepNavigation)); + builder.AddEntitySet(typeof(ClassWithDeepNavigation).Name, new EntityTypeConfiguration(new ODataModelBuilder(), typeof(ClassWithDeepNavigation))); + defaultEdmModel = builder.GetEdmModel(); + } + + [Benchmark] + public Tuple LegacyEdmAndContainer() + { + ODataConventionModelBuilder builder = new ODataConventionModelBuilder(); + builder.AddEntityType(typeof(ClassWithDeepNavigation)); + builder.AddEntitySet(typeof(ClassWithDeepNavigation).Name, new EntityTypeConfiguration(new ODataModelBuilder(), typeof(ClassWithDeepNavigation))); + var edmModel = builder.GetEdmModel(); + + ODataSettings settings = new ODataSettings(); + + ServiceContainer container = new ServiceContainer(); + container.AddService(typeof(IEdmModel), edmModel); + container.AddService(typeof(ODataQuerySettings), settings.QuerySettings); + container.AddService(typeof(ODataUriParserSettings), settings.ParserSettings); + container.AddService(typeof(IFilterBinder), new FilterBinder()); + container.AddService(typeof(ODataUriResolver), settings.Resolver ?? DefaultResolver); + container.AddService(typeof(ODataSimplifiedOptions), SimplifiedOptions); + container.AddService(typeof(ODataSettings), settings); + container.AddService(typeof(DefaultQueryConfigurations), settings.DefaultQueryConfigurations); + container.AddService(typeof(ISelectExpandQueryValidator), new SelectExpandQueryValidator()); + + return new Tuple(query, container); + } + + [Benchmark] + public Tuple LegacyContainer() + { + var edmModel = defaultEdmModel; + + if (edmModel.SchemaElements.Count(e => e.SchemaElementKind == EdmSchemaElementKind.EntityContainer) == 0) + { + throw new ArgumentException("Provided Entity Model have no IEdmEntityContainer", nameof(edmModel)); + } + + ODataSettings settings = new ODataSettings(); + + ServiceContainer container = new ServiceContainer(); + container.AddService(typeof(IEdmModel), edmModel); + container.AddService(typeof(ODataQuerySettings), settings.QuerySettings); + container.AddService(typeof(ODataUriParserSettings), settings.ParserSettings); + container.AddService(typeof(IFilterBinder), new FilterBinder()); + container.AddService(typeof(ODataUriResolver), settings.Resolver ?? DefaultResolver); + container.AddService(typeof(ODataSimplifiedOptions), SimplifiedOptions); + container.AddService(typeof(ODataSettings), settings); + container.AddService(typeof(DefaultQueryConfigurations), settings.DefaultQueryConfigurations); + container.AddService(typeof(ISelectExpandQueryValidator), new SelectExpandQueryValidator()); + + return new Tuple(query, container); + } + + [Benchmark] + public Tuple ODataExtension() + { + return new Tuple(query.OData(), new ServiceContainer()); + } + } +} diff --git a/test/OData2Linq.Benchmark/OData2Linq.Benchmark.csproj b/test/OData2Linq.Benchmark/OData2Linq.Benchmark.csproj new file mode 100644 index 000000000..073538114 --- /dev/null +++ b/test/OData2Linq.Benchmark/OData2Linq.Benchmark.csproj @@ -0,0 +1,19 @@ + + + + Exe + net6.0 + enable + enable + + + + + + + + + + + + diff --git a/test/OData2Linq.Benchmark/Program.cs b/test/OData2Linq.Benchmark/Program.cs new file mode 100644 index 000000000..0296092a9 --- /dev/null +++ b/test/OData2Linq.Benchmark/Program.cs @@ -0,0 +1,9 @@ +// See https://aka.ms/new-console-template for more information +using BenchmarkDotNet.Running; +using OData2Linq.Benchmark; + +Console.WriteLine("Start benchmarking"); + +BenchmarkRunner.Run(); + +Console.WriteLine("End benchmarking"); \ No newline at end of file From ea3c0a0c5cfbaf388f9705a102c6ef93d52d6e2b Mon Sep 17 00:00:00 2001 From: Arnaud Boussaer Date: Wed, 12 Feb 2025 13:18:45 +0100 Subject: [PATCH 32/53] Fix hashing which only constructs one container per setting (for performance) --- src/OData2Linq/Helpers/SelectExpandHelper.cs | 1 + src/OData2Linq/Helpers/TopSkipHelper.cs | 1 + src/OData2Linq/ODataLinqExtensions.cs | 10 +++---- .../DefaultQueryConfigurationsHashable.cs | 12 ++++++++ .../Settings/ODataQuerySettingsHashable.cs | 14 +++++++++ .../{ => Settings}/ODataSettings.cs | 21 ++++---------- .../OData2Linq.Benchmark.csproj | 3 +- test/OData2Linq.Tests/HashTests.cs | 29 ++++++++++--------- test/OData2Linq.Tests/Issues/Issue33.cs | 1 + 9 files changed, 54 insertions(+), 38 deletions(-) create mode 100644 src/OData2Linq/Settings/DefaultQueryConfigurationsHashable.cs create mode 100644 src/OData2Linq/Settings/ODataQuerySettingsHashable.cs rename src/OData2Linq/{ => Settings}/ODataSettings.cs (72%) diff --git a/src/OData2Linq/Helpers/SelectExpandHelper.cs b/src/OData2Linq/Helpers/SelectExpandHelper.cs index 8ae43fe78..2be2e0913 100644 --- a/src/OData2Linq/Helpers/SelectExpandHelper.cs +++ b/src/OData2Linq/Helpers/SelectExpandHelper.cs @@ -6,6 +6,7 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.OData.Edm; using Microsoft.OData.UriParser; + using OData2Linq.Settings; using System; using System.Collections.Generic; using System.Globalization; diff --git a/src/OData2Linq/Helpers/TopSkipHelper.cs b/src/OData2Linq/Helpers/TopSkipHelper.cs index 8544e147c..f75ba5926 100644 --- a/src/OData2Linq/Helpers/TopSkipHelper.cs +++ b/src/OData2Linq/Helpers/TopSkipHelper.cs @@ -3,6 +3,7 @@ using Microsoft.AspNetCore.OData; using Microsoft.AspNetCore.OData.Query; using Microsoft.OData; + using OData2Linq.Settings; using System.Linq; internal class TopSkipHelper diff --git a/src/OData2Linq/ODataLinqExtensions.cs b/src/OData2Linq/ODataLinqExtensions.cs index 7e2b4df90..9ced84125 100644 --- a/src/OData2Linq/ODataLinqExtensions.cs +++ b/src/OData2Linq/ODataLinqExtensions.cs @@ -11,6 +11,7 @@ using Microsoft.OData.ModelBuilder.Config; using Microsoft.OData.UriParser; using OData2Linq.Helpers; + using OData2Linq.Settings; using System; using System.Collections.Concurrent; using System.Collections.Generic; @@ -65,7 +66,7 @@ public static ODataQuery OData(this IQueryable query, Action OData(this IQueryable query, Action OData(this IQueryable query, Action dataQuery = new ODataQuery(query, container); + var dataQuery = new ODataQuery(query, container); return dataQuery; } diff --git a/src/OData2Linq/Settings/DefaultQueryConfigurationsHashable.cs b/src/OData2Linq/Settings/DefaultQueryConfigurationsHashable.cs new file mode 100644 index 000000000..f7df8d921 --- /dev/null +++ b/src/OData2Linq/Settings/DefaultQueryConfigurationsHashable.cs @@ -0,0 +1,12 @@ +using Microsoft.AspNetCore.OData.Query; + +namespace OData2Linq.Settings +{ + public class DefaultQueryConfigurationsHashable : DefaultQueryConfigurations + { + public override int GetHashCode() + { + return HashCode.Combine(EnableExpand, EnableSelect, EnableCount, EnableOrderBy, EnableFilter, MaxTop, EnableSkipToken); + } + } +} diff --git a/src/OData2Linq/Settings/ODataQuerySettingsHashable.cs b/src/OData2Linq/Settings/ODataQuerySettingsHashable.cs new file mode 100644 index 000000000..63d6b649c --- /dev/null +++ b/src/OData2Linq/Settings/ODataQuerySettingsHashable.cs @@ -0,0 +1,14 @@ +using Microsoft.AspNetCore.OData.Query; + +namespace OData2Linq.Settings +{ + public class ODataQuerySettingsHashable : ODataQuerySettings + { + public override int GetHashCode() + { + var x = base.GetHashCode(); + return HashCode.Combine(HandleNullPropagation, PageSize, ModelBoundPageSize, EnsureStableOrdering, EnableConstantParameterization, TimeZone, + HashCode.Combine(EnableCorrelatedSubqueryBuffering, IgnoredQueryOptions, IgnoredNestedQueryOptions, HandleReferenceNavigationPropertyExpandFilter)); + } + } +} diff --git a/src/OData2Linq/ODataSettings.cs b/src/OData2Linq/Settings/ODataSettings.cs similarity index 72% rename from src/OData2Linq/ODataSettings.cs rename to src/OData2Linq/Settings/ODataSettings.cs index c87c780eb..3543a9ffb 100644 --- a/src/OData2Linq/ODataSettings.cs +++ b/src/OData2Linq/Settings/ODataSettings.cs @@ -1,8 +1,6 @@ -namespace OData2Linq +namespace OData2Linq.Settings { - using Microsoft.AspNetCore.OData.Query; using Microsoft.AspNetCore.OData.Query.Validator; - using Microsoft.OData.ModelBuilder.Config; using Microsoft.OData.UriParser; using System; @@ -16,8 +14,8 @@ public class ODataSettings /// Sets the action which will be used to initialize every instance of . /// /// The action which will be used to initialize every instance of . - /// initializer - /// SetInitializer + /// initializer + /// SetInitializer public static void SetInitializer(Action initializer) { if (initializer == null) throw new ArgumentNullException(nameof(initializer)); @@ -39,7 +37,7 @@ public static void SetInitializer(Action initializer) internal static ODataUriResolver DefaultResolver = new StringAsEnumResolver { EnableCaseInsensitive = true }; - public ODataQuerySettings QuerySettings { get; } = new ODataQuerySettings { PageSize = 20 }; + public ODataQuerySettingsHashable QuerySettings { get; } = new ODataQuerySettingsHashable { PageSize = 20 }; public ODataValidationSettings ValidationSettings { get; } = new ODataValidationSettings(); @@ -52,16 +50,7 @@ public static void SetInitializer(Action initializer) [Obsolete("Not supported anymore")] public bool AllowRecursiveLoopOfComplexTypes { get; set; } = false; - public DefaultQuerySettings DefaultQuerySettings { get; } = - new DefaultQuerySettings - { - EnableFilter = true, - EnableOrderBy = true, - EnableExpand = true, - EnableSelect = true, - MaxTop = 100 - }; - public DefaultQueryConfigurations DefaultQueryConfigurations { get; } = new DefaultQueryConfigurations + public DefaultQueryConfigurationsHashable DefaultQueryConfigurations { get; } = new DefaultQueryConfigurationsHashable { EnableFilter = true, EnableOrderBy = true, diff --git a/test/OData2Linq.Benchmark/OData2Linq.Benchmark.csproj b/test/OData2Linq.Benchmark/OData2Linq.Benchmark.csproj index 073538114..7ed4b0fa8 100644 --- a/test/OData2Linq.Benchmark/OData2Linq.Benchmark.csproj +++ b/test/OData2Linq.Benchmark/OData2Linq.Benchmark.csproj @@ -7,9 +7,8 @@ enable - - + diff --git a/test/OData2Linq.Tests/HashTests.cs b/test/OData2Linq.Tests/HashTests.cs index 4e5a0dec8..246479da8 100644 --- a/test/OData2Linq.Tests/HashTests.cs +++ b/test/OData2Linq.Tests/HashTests.cs @@ -1,38 +1,39 @@ -using Xunit; +using OData2Linq.Settings; +using Xunit; namespace OData2Linq.Tests { public class HashTests { - [Fact(Skip = "Fails because the custom hash methods are not present in the original OData classes")] + [Fact] public void Hashes() { ODataSettings s1 = new ODataSettings(); ODataSettings s2 = new ODataSettings(); Assert.Equal(s1.QuerySettings.GetHashCode(), s2.QuerySettings.GetHashCode()); - Assert.Equal(s1.DefaultQuerySettings.GetHashCode(), s2.DefaultQuerySettings.GetHashCode()); - Assert.Equal(HashCode.Combine(s1.QuerySettings, s1.DefaultQuerySettings), HashCode.Combine(s2.QuerySettings, s2.DefaultQuerySettings)); + Assert.Equal(s1.DefaultQueryConfigurations.GetHashCode(), s2.DefaultQueryConfigurations.GetHashCode()); + Assert.Equal(HashCode.Combine(s1.QuerySettings, s1.DefaultQueryConfigurations), HashCode.Combine(s2.QuerySettings, s2.DefaultQueryConfigurations)); s1.QuerySettings.EnsureStableOrdering = false; Assert.NotEqual(s1.QuerySettings.GetHashCode(), s2.QuerySettings.GetHashCode()); - Assert.Equal(s1.DefaultQuerySettings.GetHashCode(), s2.DefaultQuerySettings.GetHashCode()); - Assert.NotEqual(HashCode.Combine(s1.QuerySettings, s1.DefaultQuerySettings), HashCode.Combine(s2.QuerySettings, s2.DefaultQuerySettings)); + Assert.Equal(s1.DefaultQueryConfigurations.GetHashCode(), s2.DefaultQueryConfigurations.GetHashCode()); + Assert.NotEqual(HashCode.Combine(s1.QuerySettings, s1.DefaultQueryConfigurations), HashCode.Combine(s2.QuerySettings, s2.DefaultQueryConfigurations)); s1.QuerySettings.EnsureStableOrdering = true; Assert.Equal(s1.QuerySettings.GetHashCode(), s2.QuerySettings.GetHashCode()); - Assert.Equal(s1.DefaultQuerySettings.GetHashCode(), s2.DefaultQuerySettings.GetHashCode()); - Assert.Equal(HashCode.Combine(s1.QuerySettings, s1.DefaultQuerySettings), HashCode.Combine(s2.QuerySettings, s2.DefaultQuerySettings)); + Assert.Equal(s1.DefaultQueryConfigurations.GetHashCode(), s2.DefaultQueryConfigurations.GetHashCode()); + Assert.Equal(HashCode.Combine(s1.QuerySettings, s1.DefaultQueryConfigurations), HashCode.Combine(s2.QuerySettings, s2.DefaultQueryConfigurations)); - s1.DefaultQuerySettings.EnableExpand = false; + s1.DefaultQueryConfigurations.EnableExpand = false; Assert.Equal(s1.QuerySettings.GetHashCode(), s2.QuerySettings.GetHashCode()); - Assert.NotEqual(s1.DefaultQuerySettings.GetHashCode(), s2.DefaultQuerySettings.GetHashCode()); - Assert.NotEqual(HashCode.Combine(s1.QuerySettings, s1.DefaultQuerySettings), HashCode.Combine(s2.QuerySettings, s2.DefaultQuerySettings)); + Assert.NotEqual(s1.DefaultQueryConfigurations.GetHashCode(), s2.DefaultQueryConfigurations.GetHashCode()); + Assert.NotEqual(HashCode.Combine(s1.QuerySettings, s1.DefaultQueryConfigurations), HashCode.Combine(s2.QuerySettings, s2.DefaultQueryConfigurations)); - s1.DefaultQuerySettings.EnableExpand = true; + s1.DefaultQueryConfigurations.EnableExpand = true; Assert.Equal(s1.QuerySettings.GetHashCode(), s2.QuerySettings.GetHashCode()); - Assert.Equal(s1.DefaultQuerySettings.GetHashCode(), s2.DefaultQuerySettings.GetHashCode()); - Assert.Equal(HashCode.Combine(s1.QuerySettings, s1.DefaultQuerySettings), HashCode.Combine(s2.QuerySettings, s2.DefaultQuerySettings)); + Assert.Equal(s1.DefaultQueryConfigurations.GetHashCode(), s2.DefaultQueryConfigurations.GetHashCode()); + Assert.Equal(HashCode.Combine(s1.QuerySettings, s1.DefaultQueryConfigurations), HashCode.Combine(s2.QuerySettings, s2.DefaultQueryConfigurations)); // Microsoft classes public properties Assert.Equal(HashCode.Combine(s1.ParserSettings.MaximumExpansionCount, s1.ParserSettings.MaximumExpansionDepth), HashCode.Combine(s2.ParserSettings.MaximumExpansionCount, s2.ParserSettings.MaximumExpansionDepth)); diff --git a/test/OData2Linq.Tests/Issues/Issue33.cs b/test/OData2Linq.Tests/Issues/Issue33.cs index 71f38fe35..c002be59b 100644 --- a/test/OData2Linq.Tests/Issues/Issue33.cs +++ b/test/OData2Linq.Tests/Issues/Issue33.cs @@ -2,6 +2,7 @@ namespace OData2Linq.Tests.Issues33 { + using OData2Linq.Settings; using System; using System.Linq; using Xunit; From ac848cd9029ae2bac4fb6d1c3fae32b6973f0d9b Mon Sep 17 00:00:00 2001 From: Arnaud Boussaer Date: Wed, 12 Feb 2025 14:45:12 +0100 Subject: [PATCH 33/53] remove unneeded registrations and old code --- src/OData2Linq/ODataLinqExtensions.cs | 92 ++++---------------- test/OData2Linq.Benchmark/InitQuery.cs | 10 +-- test/OData2Linq.Benchmark/Program.cs | 1 + test/OData2Linq.Benchmark/QueryOperations.cs | 27 ++++++ 4 files changed, 50 insertions(+), 80 deletions(-) create mode 100644 test/OData2Linq.Benchmark/QueryOperations.cs diff --git a/src/OData2Linq/ODataLinqExtensions.cs b/src/OData2Linq/ODataLinqExtensions.cs index 9ced84125..3dee134ac 100644 --- a/src/OData2Linq/ODataLinqExtensions.cs +++ b/src/OData2Linq/ODataLinqExtensions.cs @@ -1,14 +1,12 @@ namespace OData2Linq { using Microsoft.AspNetCore.OData.Query; - using Microsoft.AspNetCore.OData.Query.Expressions; using Microsoft.AspNetCore.OData.Query.Validator; using Microsoft.AspNetCore.OData.Query.Wrapper; using Microsoft.Extensions.DependencyInjection; using Microsoft.OData; using Microsoft.OData.Edm; using Microsoft.OData.ModelBuilder; - using Microsoft.OData.ModelBuilder.Config; using Microsoft.OData.UriParser; using OData2Linq.Helpers; using OData2Linq.Settings; @@ -16,7 +14,6 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.ComponentModel.Design; - using System.Diagnostics.Contracts; using System.Linq; public static class ODataLinqExtensions @@ -42,15 +39,14 @@ public static ODataQuery OData(this IQueryable query, Action { - ODataConventionModelBuilder builder = new ODataConventionModelBuilder(); - //builder.ODataSettings = settings; + var builder = new ODataConventionModelBuilder(); builder.AddEntityType(t); builder.AddEntitySet(t.Name, new EntityTypeConfiguration(builder, t)); return builder.GetEdmModel(); @@ -71,9 +67,9 @@ public static ODataQuery OData(this IQueryable query, Action + var baseContainer = Containers.GetOrAdd(settingsHash, i => { - ServiceContainer c = new ServiceContainer(); + var c = new ServiceContainer(); c.AddService(typeof(ODataQuerySettings), settings.QuerySettings); c.AddService(typeof(DefaultQueryConfigurations), settings.DefaultQueryConfigurations); @@ -83,9 +79,8 @@ public static ODataQuery OData(this IQueryable query, Action Filter(this ODataQuery query, string filterTex ODataSettings settings = query.ServiceProvider.GetRequiredService(); - FilterClause filterClause = queryOptionParser.ParseFilter(); - SingleValueNode filterExpression = filterClause.Expression.Accept( - new ParameterAliasNodeTranslator(queryOptionParser.ParameterAliasNodes)) as SingleValueNode; - filterExpression = filterExpression ?? new ConstantNode(null); - filterClause = new FilterClause(filterExpression, filterClause.RangeVariable); - Contract.Assert(filterClause != null); - var queryContext = new ODataQueryContext(edmModel, query.ElementType) { RequestContainer = query.ServiceProvider }; - var validator2 = new FilterQueryValidator(); - var option = new FilterQueryOption(queryContext, filterClause); - validator2.Validate(option, settings.ValidationSettings); - - //TODO: check alternative - //var binder = query.ServiceProvider.GetRequiredService(); - //QueryBinderContext context = new QueryBinderContext(edmModel, query.ServiceProvider.GetRequiredService(), query.ElementType) - //{ - // AssembliesResolver = AssemblyResolverHelper.Default, - // //Source = query.Expression - //}; - //var filter2 = binder.BindFilter(filterClause, context); - //var result2 = ExpressionHelpers.Where(query, filter2, typeof(T)); - - var result3 = option.ApplyTo(query, query.ServiceProvider.GetRequiredService()); - - return new ODataQuery(result3, query.ServiceProvider); + var option = new FilterQueryOption(filterText, queryContext, queryOptionParser); + var validator = new FilterQueryValidator(); + validator.Validate(option, settings.ValidationSettings); + + var result = option.ApplyTo(query, query.ServiceProvider.GetRequiredService()); + + return new ODataQuery(result, query.ServiceProvider); } /// Apply $orderby parameter to query. @@ -330,7 +308,7 @@ public static ODataQuery Filter(this ODataQuery query, string filterTex /// The $orderby parameter text. /// The entity set name. /// The query type param - /// The query with applied order by parameter. + /// The query with applied order by parameter. /// Argument Null Exception public static ODataQueryOrdered OrderBy(this ODataQuery query, string orderbyText, string entitySetName = null) { @@ -346,31 +324,17 @@ public static ODataQueryOrdered OrderBy(this ODataQuery query, string o ODataSettings settings = query.ServiceProvider.GetRequiredService(); - var orderByClause = queryOptionParser.ParseOrderBy(); - - orderByClause = TranslateParameterAlias(orderByClause, queryOptionParser); - - ICollection nodes = OrderByNode.CreateCollection(orderByClause); - var queryContext = new ODataQueryContext(edmModel, query.ElementType) { RequestContainer = query.ServiceProvider }; - var validator2 = new OrderByQueryValidator(); var option = new OrderByQueryOption(orderbyText, queryContext, queryOptionParser); - validator2.Validate(option, settings.ValidationSettings); - - //TODO: check alternative - //var binder = new OrderByBinder(); - //QueryBinderContext context = new QueryBinderContext(edmModel, query.ServiceProvider.GetRequiredService(), typeof(T)) - //{ - // AssembliesResolver = AssemblyResolverHelper.Default, - // Source = query.Expression - //}; - //var orderbyResult = binder.BindOrderBy(option.OrderByClause, context); - var result2 = option.ApplyTo(query, query.ServiceProvider.GetRequiredService()); - - return new ODataQueryOrdered(result2, query.ServiceProvider); + var validator = new OrderByQueryValidator(); + validator.Validate(option, settings.ValidationSettings); + + var result = option.ApplyTo(query, query.ServiceProvider.GetRequiredService()); + + return new ODataQueryOrdered(result, query.ServiceProvider); } private static IEnumerable Enumerate(IQueryable queryable) where T : class @@ -383,24 +347,6 @@ private static IEnumerable Enumerate(IQueryable queryable) where T : class } } - private static OrderByClause TranslateParameterAlias(OrderByClause orderBy, ODataQueryOptionParser queryOptionParser) - { - if (orderBy == null) - { - return null; - } - - SingleValueNode orderByExpression = orderBy.Expression.Accept( - new ParameterAliasNodeTranslator(queryOptionParser.ParameterAliasNodes)) as SingleValueNode; - orderByExpression = orderByExpression ?? new ConstantNode(null, "null"); - - return new OrderByClause( - TranslateParameterAlias(orderBy.ThenBy, queryOptionParser), - orderByExpression, - orderBy.Direction, - orderBy.RangeVariable); - } - public static ODataQueryOptionParser GetParser(ODataQuery query, string entitySetName, IDictionary raws) { IEdmModel edmModel = query.EdmModel; diff --git a/test/OData2Linq.Benchmark/InitQuery.cs b/test/OData2Linq.Benchmark/InitQuery.cs index 1f62e64af..0c5bbddd2 100644 --- a/test/OData2Linq.Benchmark/InitQuery.cs +++ b/test/OData2Linq.Benchmark/InitQuery.cs @@ -1,11 +1,10 @@ using BenchmarkDotNet.Attributes; using Microsoft.AspNetCore.OData.Query; -using Microsoft.AspNetCore.OData.Query.Expressions; -using Microsoft.AspNetCore.OData.Query.Validator; using Microsoft.OData; using Microsoft.OData.Edm; using Microsoft.OData.ModelBuilder; using Microsoft.OData.UriParser; +using OData2Linq.Settings; using OData2Linq.Tests.SampleData; using System.ComponentModel.Design; @@ -45,12 +44,10 @@ public Tuple LegacyEdmAndContainer() container.AddService(typeof(IEdmModel), edmModel); container.AddService(typeof(ODataQuerySettings), settings.QuerySettings); container.AddService(typeof(ODataUriParserSettings), settings.ParserSettings); - container.AddService(typeof(IFilterBinder), new FilterBinder()); container.AddService(typeof(ODataUriResolver), settings.Resolver ?? DefaultResolver); container.AddService(typeof(ODataSimplifiedOptions), SimplifiedOptions); container.AddService(typeof(ODataSettings), settings); container.AddService(typeof(DefaultQueryConfigurations), settings.DefaultQueryConfigurations); - container.AddService(typeof(ISelectExpandQueryValidator), new SelectExpandQueryValidator()); return new Tuple(query, container); } @@ -71,12 +68,10 @@ public Tuple LegacyContainer() container.AddService(typeof(IEdmModel), edmModel); container.AddService(typeof(ODataQuerySettings), settings.QuerySettings); container.AddService(typeof(ODataUriParserSettings), settings.ParserSettings); - container.AddService(typeof(IFilterBinder), new FilterBinder()); container.AddService(typeof(ODataUriResolver), settings.Resolver ?? DefaultResolver); container.AddService(typeof(ODataSimplifiedOptions), SimplifiedOptions); container.AddService(typeof(ODataSettings), settings); container.AddService(typeof(DefaultQueryConfigurations), settings.DefaultQueryConfigurations); - container.AddService(typeof(ISelectExpandQueryValidator), new SelectExpandQueryValidator()); return new Tuple(query, container); } @@ -84,7 +79,8 @@ public Tuple LegacyContainer() [Benchmark] public Tuple ODataExtension() { - return new Tuple(query.OData(), new ServiceContainer()); + var odataQuery = query.OData(); + return new Tuple(odataQuery, odataQuery.ServiceProvider); } } } diff --git a/test/OData2Linq.Benchmark/Program.cs b/test/OData2Linq.Benchmark/Program.cs index 0296092a9..db05da820 100644 --- a/test/OData2Linq.Benchmark/Program.cs +++ b/test/OData2Linq.Benchmark/Program.cs @@ -5,5 +5,6 @@ Console.WriteLine("Start benchmarking"); BenchmarkRunner.Run(); +BenchmarkRunner.Run(); Console.WriteLine("End benchmarking"); \ No newline at end of file diff --git a/test/OData2Linq.Benchmark/QueryOperations.cs b/test/OData2Linq.Benchmark/QueryOperations.cs new file mode 100644 index 000000000..abf77c34c --- /dev/null +++ b/test/OData2Linq.Benchmark/QueryOperations.cs @@ -0,0 +1,27 @@ +using BenchmarkDotNet.Attributes; +using OData2Linq.Tests.SampleData; + +namespace OData2Linq.Benchmark +{ + public class QueryOperations + { + private static readonly IQueryable query; + + static QueryOperations() + { + query = SimpleClass.CreateQuery(); + } + + [Benchmark] + public SimpleClass[] ODataFilter() + { + return query.OData().Filter("Id eq 1").ToArray(); + } + + [Benchmark] + public SimpleClass ODataOrderByIdDefault() + { + return query.OData().OrderBy("Id,Name").First(); + } + } +} From 65c24e9ae691d3f3e540a677cf0974a8da653b6f Mon Sep 17 00:00:00 2001 From: Arnaud Boussaer Date: Wed, 12 Feb 2025 15:24:41 +0100 Subject: [PATCH 34/53] fix unit test, code cleanup --- src/OData2Linq/Helpers/SelectExpandHelper.cs | 1 - src/OData2Linq/Helpers/TopSkipHelper.cs | 40 +++----------------- src/OData2Linq/ODataQuery.cs | 18 ++++----- test/OData2Linq.Tests/ExpandTests.cs | 2 +- 4 files changed, 16 insertions(+), 45 deletions(-) diff --git a/src/OData2Linq/Helpers/SelectExpandHelper.cs b/src/OData2Linq/Helpers/SelectExpandHelper.cs index 2be2e0913..f2ae38856 100644 --- a/src/OData2Linq/Helpers/SelectExpandHelper.cs +++ b/src/OData2Linq/Helpers/SelectExpandHelper.cs @@ -2,7 +2,6 @@ { using Microsoft.AspNetCore.OData.Edm; using Microsoft.AspNetCore.OData.Query; - using Microsoft.Extensions.DependencyInjection; using Microsoft.OData.Edm; using Microsoft.OData.UriParser; diff --git a/src/OData2Linq/Helpers/TopSkipHelper.cs b/src/OData2Linq/Helpers/TopSkipHelper.cs index f75ba5926..e2b93e6cc 100644 --- a/src/OData2Linq/Helpers/TopSkipHelper.cs +++ b/src/OData2Linq/Helpers/TopSkipHelper.cs @@ -14,29 +14,15 @@ public static IQueryable ApplyTopWithValidation(IQueryable query, long? { if (top.Value > int.MaxValue) { - throw new ODataException( - Error.Format( - SRResources.SkipTopLimitExceeded, - int.MaxValue, - AllowedQueryOptions.Top, - top.Value)); + throw new ODataException(Error.Format(SRResources.SkipTopLimitExceeded, int.MaxValue, AllowedQueryOptions.Top, top.Value)); } if (top.Value > settings.ValidationSettings.MaxTop) { - throw new ODataException( - Error.Format( - SRResources.SkipTopLimitExceeded, - settings.ValidationSettings.MaxTop, - AllowedQueryOptions.Top, - top.Value)); + throw new ODataException(Error.Format(SRResources.SkipTopLimitExceeded, settings.ValidationSettings.MaxTop, AllowedQueryOptions.Top, top.Value)); } - IQueryable result = ExpressionHelpers.Take( - query, - (int)top.Value, - typeof(T), - settings.QuerySettings.EnableConstantParameterization) as IQueryable; + IQueryable result = ExpressionHelpers.Take(query, (int)top.Value, typeof(T), settings.QuerySettings.EnableConstantParameterization) as IQueryable; return result; } @@ -50,29 +36,15 @@ public static IQueryable ApplySkipWithValidation(IQueryable query, long { if (skip.Value > int.MaxValue) { - throw new ODataException( - Error.Format( - SRResources.SkipTopLimitExceeded, - int.MaxValue, - AllowedQueryOptions.Skip, - skip.Value)); + throw new ODataException(Error.Format(SRResources.SkipTopLimitExceeded, int.MaxValue, AllowedQueryOptions.Skip, skip.Value)); } if (skip.Value > settings.ValidationSettings.MaxSkip) { - throw new ODataException( - Error.Format( - SRResources.SkipTopLimitExceeded, - settings.ValidationSettings.MaxSkip, - AllowedQueryOptions.Skip, - skip.Value)); + throw new ODataException(Error.Format(SRResources.SkipTopLimitExceeded, settings.ValidationSettings.MaxSkip, AllowedQueryOptions.Skip, skip.Value)); } - IQueryable result = ExpressionHelpers.Skip( - query, - (int)skip.Value, - typeof(T), - settings.QuerySettings.EnableConstantParameterization) as IQueryable; + IQueryable result = ExpressionHelpers.Skip(query, (int)skip.Value, typeof(T), settings.QuerySettings.EnableConstantParameterization) as IQueryable; return result; } diff --git a/src/OData2Linq/ODataQuery.cs b/src/OData2Linq/ODataQuery.cs index 32ff71ece..f2e12e21e 100644 --- a/src/OData2Linq/ODataQuery.cs +++ b/src/OData2Linq/ODataQuery.cs @@ -12,34 +12,34 @@ public class ODataQuery : IQueryable { internal ODataQuery(IQueryable inner, IServiceProvider serviceProvider) { - this.Inner = inner ?? throw new ArgumentNullException(nameof(inner)); - this.ServiceProvider = serviceProvider; + Inner = inner ?? throw new ArgumentNullException(nameof(inner)); + ServiceProvider = serviceProvider; } - public IEdmModel EdmModel => this.ServiceProvider.GetRequiredService(); + public IEdmModel EdmModel => ServiceProvider.GetRequiredService(); - public Type ElementType => this.Inner.ElementType; + public Type ElementType => Inner.ElementType; - public Expression Expression => this.Inner.Expression; + public Expression Expression => Inner.Expression; - public IQueryProvider Provider => this.Inner.Provider; + public IQueryProvider Provider => Inner.Provider; public IQueryable Inner { get; } public IServiceProvider ServiceProvider { get; } public IEnumerator GetEnumerator() { - return this.Inner.GetEnumerator(); + return Inner.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { - return (IEnumerator)this.Inner.GetEnumerator(); + return (IEnumerator)Inner.GetEnumerator(); } public IQueryable ToOriginalQuery() { - return (IQueryable)this.Inner; + return (IQueryable)Inner; } } } diff --git a/test/OData2Linq.Tests/ExpandTests.cs b/test/OData2Linq.Tests/ExpandTests.cs index 73d21c174..e0c305ae5 100644 --- a/test/OData2Linq.Tests/ExpandTests.cs +++ b/test/OData2Linq.Tests/ExpandTests.cs @@ -142,7 +142,7 @@ public void ExpandCollectionWithFilterAndSelect() public void ExpandCollectionWithNotExpandable() { Assert.Throws( - () => SampleWithCustomKey.CreateQuery().OData().SelectExpand("Id", "NotExpandableLink")); + () => SampleWithCustomKey.CreateQuery().OData().SelectExpand(nameof(SampleWithCustomKey.Name), "NotExpandableLink")); } [Fact] From b31a26f24f5133f6f545fee928f3fafa7163c42a Mon Sep 17 00:00:00 2001 From: Arnaud Boussaer Date: Thu, 13 Feb 2025 09:04:04 +0100 Subject: [PATCH 35/53] Set odata2linq to v1.0.0 --- src/OData2Linq/OData2Linq.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/OData2Linq/OData2Linq.csproj b/src/OData2Linq/OData2Linq.csproj index 622633cc7..1e8a3022f 100644 --- a/src/OData2Linq/OData2Linq.csproj +++ b/src/OData2Linq/OData2Linq.csproj @@ -7,7 +7,7 @@ True FriendAssemblies.snk True - 0.0.6 + 1.0.0 MIT README.md https://github.com/ArnaudB88/OData2Linq From 5c69c08942f938a7e4003035e6a11818559064f8 Mon Sep 17 00:00:00 2001 From: Arnaud Boussaer Date: Thu, 13 Feb 2025 09:32:05 +0100 Subject: [PATCH 36/53] Add test for timeonly --- test/OData2Linq.Tests/DateTimeFilterTests.cs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/test/OData2Linq.Tests/DateTimeFilterTests.cs b/test/OData2Linq.Tests/DateTimeFilterTests.cs index bbf587a27..85e08ba28 100644 --- a/test/OData2Linq.Tests/DateTimeFilterTests.cs +++ b/test/OData2Linq.Tests/DateTimeFilterTests.cs @@ -12,6 +12,7 @@ public class DateTimeFilterTests private static readonly DateTime dt = new DateTime(2018, 1, 26, 0, 0, 0); private static readonly DateTimeOffset dto = new DateTimeOffset(dt, TimeZoneInfo.Local.BaseUtcOffset); private static readonly DateOnly do1 = new(2018, 1, 26); + private static readonly TimeOnly to1 = new(12, 34, 56); //private readonly DateTimeOffset dtoUtc = new DateTimeOffset(new DateTime(2018, 1, 26).ToUniversalTime()); public DateTimeFilterTests(ITestOutputHelper output) @@ -177,5 +178,17 @@ public void DateOnlyFilterWorks() Assert.True(result.First().DateOnly == do1); } + [Fact] + public void TimeOnlyFilterWorks() + { + string value = to1.ToLongTimeString(); + string filter = $"{nameof(SimpleClass.TimeOnly)} eq {value}"; + output.WriteLine(filter); + + var result = SimpleClass.CreateQuery().OData().Filter(filter).ToArray(); + Assert.Single(result); + Assert.True(result.First().TimeOnly == to1); + } + } } From f8af327cdbd79745ab5db3e9efe8b22b39ab107a Mon Sep 17 00:00:00 2001 From: Arnaud Boussaer Date: Thu, 13 Feb 2025 10:34:14 +0100 Subject: [PATCH 37/53] add CD pipeline --- .github/workflows/odata2linq-cd.yml | 34 ++++++++++++++++++++++++++++ .github/workflows/odata2linq-ci.yml | 2 +- .github/workflows/odata2linq-ci2.yml | 31 ------------------------- src/OData2Linq/OData2Linq.csproj | 3 ++- 4 files changed, 37 insertions(+), 33 deletions(-) create mode 100644 .github/workflows/odata2linq-cd.yml delete mode 100644 .github/workflows/odata2linq-ci2.yml diff --git a/.github/workflows/odata2linq-cd.yml b/.github/workflows/odata2linq-cd.yml new file mode 100644 index 000000000..a0e767429 --- /dev/null +++ b/.github/workflows/odata2linq-cd.yml @@ -0,0 +1,34 @@ +# This workflow will build a .NET project +# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-net + +name: odata2linq-cd + +on: + push: + tags: + - 'OData2Linq_*.*.*' + +jobs: + build: + + runs-on: ubuntu-latest + strategy: + matrix: + dotnet-version: ['6.0.x'] + + steps: + - uses: actions/checkout@v4 + - name: Setup dotnet ${{ matrix.dotnet-version }} + uses: actions/setup-dotnet@v4 + with: + dotnet-version: ${{ matrix.dotnet-version }} + - name: Restore dependencies + run: dotnet restore ./AspNetCoreOData.sln + - name: Build + run: dotnet build ./AspNetCoreOData.sln -c Release --no-restore + - name: Test + run: dotnet test ./AspNetCoreOData.sln -c Release --no-build --verbosity normal + - name: Pack + run: dotnet pack ./src/OData2Linq.csproj -c Release -o .\artifacts --no-build + - name: Publish to NuGet + run: dotnet nuget push .\artifacts\*.nupkg --source https://api.nuget.org/v3/index.json --api-key ${{ secrets.NUGET_API_KEY }} diff --git a/.github/workflows/odata2linq-ci.yml b/.github/workflows/odata2linq-ci.yml index d96c06816..af8e2b413 100644 --- a/.github/workflows/odata2linq-ci.yml +++ b/.github/workflows/odata2linq-ci.yml @@ -1,7 +1,7 @@ # This workflow will build a .NET project # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-net -name: .NET +name: odata2linq-ci on: push: diff --git a/.github/workflows/odata2linq-ci2.yml b/.github/workflows/odata2linq-ci2.yml deleted file mode 100644 index a315798bf..000000000 --- a/.github/workflows/odata2linq-ci2.yml +++ /dev/null @@ -1,31 +0,0 @@ -# This workflow will build a .NET project -# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-net - -name: .NET - -on: - push: - branches: [ "main" ] - pull_request: - branches: [ "main" ] - -jobs: - build: - - runs-on: ubuntu-latest - strategy: - matrix: - dotnet-version: [ '3.1.x', '6.0.x', 8.0.x ] - - steps: - - uses: actions/checkout@v4 - - name: Setup dotnet ${{ matrix.dotnet-version }} - uses: actions/setup-dotnet@v4 - with: - dotnet-version: ${{ matrix.dotnet-version }} - - name: Restore dependencies - run: dotnet restore - - name: Build - run: dotnet build --no-restore - - name: Test - run: dotnet test --no-build --verbosity normal diff --git a/src/OData2Linq/OData2Linq.csproj b/src/OData2Linq/OData2Linq.csproj index 1e8a3022f..9862483d4 100644 --- a/src/OData2Linq/OData2Linq.csproj +++ b/src/OData2Linq/OData2Linq.csproj @@ -7,7 +7,7 @@ True FriendAssemblies.snk True - 1.0.0 + 0.0.7 MIT README.md https://github.com/ArnaudB88/OData2Linq @@ -32,6 +32,7 @@ True \ + From da4d96227796d6d3b5d225ce61d0b7e54cac6a0e Mon Sep 17 00:00:00 2001 From: Arnaud Boussaer Date: Thu, 13 Feb 2025 11:21:11 +0100 Subject: [PATCH 38/53] fix unit tests if executed on a machine with UTC as timezone, specify pipelines to run on windows so no tests file because of newline characters --- .github/workflows/odata2linq-cd.yml | 2 +- .github/workflows/odata2linq-ci.yml | 2 +- src/OData2Linq/OData2Linq.csproj | 2 +- test/OData2Linq.Tests/DateTimeFilterTests.cs | 26 ++++++++++++++++---- 4 files changed, 24 insertions(+), 8 deletions(-) diff --git a/.github/workflows/odata2linq-cd.yml b/.github/workflows/odata2linq-cd.yml index a0e767429..e1e885152 100644 --- a/.github/workflows/odata2linq-cd.yml +++ b/.github/workflows/odata2linq-cd.yml @@ -11,7 +11,7 @@ on: jobs: build: - runs-on: ubuntu-latest + runs-on: windows-latest strategy: matrix: dotnet-version: ['6.0.x'] diff --git a/.github/workflows/odata2linq-ci.yml b/.github/workflows/odata2linq-ci.yml index af8e2b413..4c3d1d5b6 100644 --- a/.github/workflows/odata2linq-ci.yml +++ b/.github/workflows/odata2linq-ci.yml @@ -12,7 +12,7 @@ on: jobs: build: - runs-on: ubuntu-latest + runs-on: windows-latest strategy: matrix: dotnet-version: ['6.0.x'] diff --git a/src/OData2Linq/OData2Linq.csproj b/src/OData2Linq/OData2Linq.csproj index 9862483d4..aa6770ef5 100644 --- a/src/OData2Linq/OData2Linq.csproj +++ b/src/OData2Linq/OData2Linq.csproj @@ -7,7 +7,7 @@ True FriendAssemblies.snk True - 0.0.7 + 0.0.8 MIT README.md https://github.com/ArnaudB88/OData2Linq diff --git a/test/OData2Linq.Tests/DateTimeFilterTests.cs b/test/OData2Linq.Tests/DateTimeFilterTests.cs index 85e08ba28..a3869350d 100644 --- a/test/OData2Linq.Tests/DateTimeFilterTests.cs +++ b/test/OData2Linq.Tests/DateTimeFilterTests.cs @@ -52,7 +52,10 @@ public void LocalDateTimetNotEquaIncorrectUtcOffset() output.WriteLine(filter); var result = SimpleClass.CreateQuery().OData().Filter(filter).ToArray(); - Assert.Empty(result); + if (TimeZoneInfo.Local != TimeZoneInfo.Utc) + Assert.Empty(result); + else + Assert.Single(result); } [Fact] @@ -88,7 +91,10 @@ public void DateTimeOffsetNotEqualIncorrectUtcOffset() output.WriteLine(filter); var result = SimpleClass.CreateQuery().OData().Filter(filter).ToArray(); - Assert.Empty(result); + if (TimeZoneInfo.Local != TimeZoneInfo.Utc) + Assert.Empty(result); + else + Assert.Single(result); } [Fact] @@ -125,7 +131,10 @@ public void UtcDateTimeNotEqualIncorrectLocalOffset() output.WriteLine(filter); var result = SimpleClass.CreateQuery().OData(c => c.QuerySettings.TimeZone = TimeZoneInfo.Utc).Filter(filter).ToArray(); - Assert.Empty(result); + if (TimeZoneInfo.Local != TimeZoneInfo.Utc) + Assert.Empty(result); + else + Assert.Single(result); } [Fact] @@ -136,7 +145,10 @@ public void UtcDateTimeNotEqualIncorrectLocalOffset2() output.WriteLine(filter); var result = SimpleClass.CreateQuery().OData(c => c.QuerySettings.TimeZone = TimeZoneInfo.Utc).Filter(filter).ToArray(); - Assert.Empty(result); + if (TimeZoneInfo.Local != TimeZoneInfo.Utc) + Assert.Empty(result); + else + Assert.Single(result); } [Fact] @@ -147,7 +159,11 @@ public void UtcDateTimetNotEqualIncorrectUtcOffset() output.WriteLine(filter); var result = SimpleClass.CreateQuery().OData(c => c.QuerySettings.TimeZone = TimeZoneInfo.Utc).Filter(filter).ToArray(); - Assert.Empty(result); + + if (TimeZoneInfo.Local != TimeZoneInfo.Utc) + Assert.Empty(result); + else + Assert.Single(result); } [Fact] From f887b25720ee4f0516abf4b64ba3512ec168dea8 Mon Sep 17 00:00:00 2001 From: Arnaud Boussaer Date: Thu, 13 Feb 2025 11:46:17 +0100 Subject: [PATCH 39/53] fix unit tests --- src/OData2Linq/OData2Linq.csproj | 2 +- test/OData2Linq.Tests/DateTimeFilterTests.cs | 38 ++++++++++---------- 2 files changed, 21 insertions(+), 19 deletions(-) diff --git a/src/OData2Linq/OData2Linq.csproj b/src/OData2Linq/OData2Linq.csproj index aa6770ef5..0b9d8acd6 100644 --- a/src/OData2Linq/OData2Linq.csproj +++ b/src/OData2Linq/OData2Linq.csproj @@ -7,7 +7,7 @@ True FriendAssemblies.snk True - 0.0.8 + 0.0.9 MIT README.md https://github.com/ArnaudB88/OData2Linq diff --git a/test/OData2Linq.Tests/DateTimeFilterTests.cs b/test/OData2Linq.Tests/DateTimeFilterTests.cs index a3869350d..b38fd5690 100644 --- a/test/OData2Linq.Tests/DateTimeFilterTests.cs +++ b/test/OData2Linq.Tests/DateTimeFilterTests.cs @@ -52,10 +52,10 @@ public void LocalDateTimetNotEquaIncorrectUtcOffset() output.WriteLine(filter); var result = SimpleClass.CreateQuery().OData().Filter(filter).ToArray(); - if (TimeZoneInfo.Local != TimeZoneInfo.Utc) - Assert.Empty(result); - else + if (TimeZoneInfo.Local.Equals(TimeZoneInfo.Utc)) Assert.Single(result); + else + Assert.Empty(result); } [Fact] @@ -91,10 +91,10 @@ public void DateTimeOffsetNotEqualIncorrectUtcOffset() output.WriteLine(filter); var result = SimpleClass.CreateQuery().OData().Filter(filter).ToArray(); - if (TimeZoneInfo.Local != TimeZoneInfo.Utc) - Assert.Empty(result); - else + if (TimeZoneInfo.Local.Equals(TimeZoneInfo.Utc)) Assert.Single(result); + else + Assert.Empty(result); } [Fact] @@ -131,10 +131,10 @@ public void UtcDateTimeNotEqualIncorrectLocalOffset() output.WriteLine(filter); var result = SimpleClass.CreateQuery().OData(c => c.QuerySettings.TimeZone = TimeZoneInfo.Utc).Filter(filter).ToArray(); - if (TimeZoneInfo.Local != TimeZoneInfo.Utc) - Assert.Empty(result); - else + if (TimeZoneInfo.Local.Equals(TimeZoneInfo.Utc)) Assert.Single(result); + else + Assert.Empty(result); } [Fact] @@ -145,10 +145,10 @@ public void UtcDateTimeNotEqualIncorrectLocalOffset2() output.WriteLine(filter); var result = SimpleClass.CreateQuery().OData(c => c.QuerySettings.TimeZone = TimeZoneInfo.Utc).Filter(filter).ToArray(); - if (TimeZoneInfo.Local != TimeZoneInfo.Utc) - Assert.Empty(result); - else + if (TimeZoneInfo.Local.Equals(TimeZoneInfo.Utc)) Assert.Single(result); + else + Assert.Empty(result); } [Fact] @@ -160,10 +160,10 @@ public void UtcDateTimetNotEqualIncorrectUtcOffset() var result = SimpleClass.CreateQuery().OData(c => c.QuerySettings.TimeZone = TimeZoneInfo.Utc).Filter(filter).ToArray(); - if (TimeZoneInfo.Local != TimeZoneInfo.Utc) - Assert.Empty(result); - else + if (TimeZoneInfo.Local.Equals(TimeZoneInfo.Utc)) Assert.Single(result); + else + Assert.Empty(result); } [Fact] @@ -175,9 +175,11 @@ public void DateTimeCompare() Assert.Equal(dt, dtUtc); Assert.Equal(dtUtc, dtLocal); - Assert.NotEqual(TimeZoneInfo.Local, TimeZoneInfo.Utc); - Assert.NotEqual(TimeZoneInfo.Local.GetHashCode(), TimeZoneInfo.Utc.GetHashCode()); - + if (!TimeZoneInfo.Local.Equals(TimeZoneInfo.Utc)) + { + Assert.NotEqual(TimeZoneInfo.Local, TimeZoneInfo.Utc); + Assert.NotEqual(TimeZoneInfo.Local.GetHashCode(), TimeZoneInfo.Utc.GetHashCode()); + } Assert.Equal(TimeZoneInfo.Utc, TimeZoneInfo.Utc); Assert.Equal(TimeZoneInfo.Utc.GetHashCode(), TimeZoneInfo.Utc.GetHashCode()); } From 7d77f50a08df2ec5e3ee7bf4058d596492482a36 Mon Sep 17 00:00:00 2001 From: Arnaud Boussaer Date: Thu, 13 Feb 2025 11:55:40 +0100 Subject: [PATCH 40/53] fix last failing test --- src/OData2Linq/OData2Linq.csproj | 2 +- test/OData2Linq.Tests/DateTimeFilterTests.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/OData2Linq/OData2Linq.csproj b/src/OData2Linq/OData2Linq.csproj index 0b9d8acd6..a8a42c486 100644 --- a/src/OData2Linq/OData2Linq.csproj +++ b/src/OData2Linq/OData2Linq.csproj @@ -7,7 +7,7 @@ True FriendAssemblies.snk True - 0.0.9 + 0.0.10 MIT README.md https://github.com/ArnaudB88/OData2Linq diff --git a/test/OData2Linq.Tests/DateTimeFilterTests.cs b/test/OData2Linq.Tests/DateTimeFilterTests.cs index b38fd5690..b0a55a827 100644 --- a/test/OData2Linq.Tests/DateTimeFilterTests.cs +++ b/test/OData2Linq.Tests/DateTimeFilterTests.cs @@ -199,7 +199,7 @@ public void DateOnlyFilterWorks() [Fact] public void TimeOnlyFilterWorks() { - string value = to1.ToLongTimeString(); + string value = to1.ToString("HH:mm:ss"); string filter = $"{nameof(SimpleClass.TimeOnly)} eq {value}"; output.WriteLine(filter); From ed8996f1626a2d15d66677355782e617281ee581 Mon Sep 17 00:00:00 2001 From: Arnaud Boussaer Date: Thu, 13 Feb 2025 12:02:44 +0100 Subject: [PATCH 41/53] pipeline fix --- .github/workflows/odata2linq-cd.yml | 2 +- src/OData2Linq/OData2Linq.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/odata2linq-cd.yml b/.github/workflows/odata2linq-cd.yml index e1e885152..d29aa8f99 100644 --- a/.github/workflows/odata2linq-cd.yml +++ b/.github/workflows/odata2linq-cd.yml @@ -29,6 +29,6 @@ jobs: - name: Test run: dotnet test ./AspNetCoreOData.sln -c Release --no-build --verbosity normal - name: Pack - run: dotnet pack ./src/OData2Linq.csproj -c Release -o .\artifacts --no-build + run: dotnet pack ./src/OData2Linq/OData2Linq.csproj -c Release -o .\artifacts --no-build - name: Publish to NuGet run: dotnet nuget push .\artifacts\*.nupkg --source https://api.nuget.org/v3/index.json --api-key ${{ secrets.NUGET_API_KEY }} diff --git a/src/OData2Linq/OData2Linq.csproj b/src/OData2Linq/OData2Linq.csproj index a8a42c486..4fd0ef7c4 100644 --- a/src/OData2Linq/OData2Linq.csproj +++ b/src/OData2Linq/OData2Linq.csproj @@ -7,7 +7,7 @@ True FriendAssemblies.snk True - 0.0.10 + 0.0.11 MIT README.md https://github.com/ArnaudB88/OData2Linq From 121ac6f285d58c22c2bfc64a71177a85151b34c2 Mon Sep 17 00:00:00 2001 From: Arnaud Boussaer Date: Thu, 13 Feb 2025 12:11:25 +0100 Subject: [PATCH 42/53] Set package version to 1.0.0 --- src/OData2Linq/OData2Linq.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/OData2Linq/OData2Linq.csproj b/src/OData2Linq/OData2Linq.csproj index 4fd0ef7c4..fa0fe4bb4 100644 --- a/src/OData2Linq/OData2Linq.csproj +++ b/src/OData2Linq/OData2Linq.csproj @@ -7,7 +7,7 @@ True FriendAssemblies.snk True - 0.0.11 + 1.0.0 MIT README.md https://github.com/ArnaudB88/OData2Linq From d2010096a8e2a38a01daaf9266655d1fc602f60c Mon Sep 17 00:00:00 2001 From: Arnaud Boussaer Date: Thu, 13 Feb 2025 12:30:21 +0100 Subject: [PATCH 43/53] Add package description --- src/OData2Linq/OData2Linq.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/src/OData2Linq/OData2Linq.csproj b/src/OData2Linq/OData2Linq.csproj index fa0fe4bb4..955d319b9 100644 --- a/src/OData2Linq/OData2Linq.csproj +++ b/src/OData2Linq/OData2Linq.csproj @@ -13,6 +13,7 @@ https://github.com/ArnaudB88/OData2Linq Apply an OData filter text query to an IQueryable expression odata;filter;linq + Apply an OData filter text query to an IQueryable expression From 0c4d5eaa0fe946959a3c324f2170a72db93bf6b0 Mon Sep 17 00:00:00 2001 From: Arnaud Boussaer Date: Fri, 14 Feb 2025 09:29:42 +0100 Subject: [PATCH 44/53] fix readme nuget links --- .github/README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/README.md b/.github/README.md index f41ada1ed..d04736030 100644 --- a/.github/README.md +++ b/.github/README.md @@ -1,4 +1,4 @@ -![NuGet Version](https://img.shields.io/nuget/v/OData2Linq) +[![NuGet Version](https://img.shields.io/nuget/v/OData2Linq?label=NuGet)](https://www.nuget.org/packages/OData2Linq/) # OData2Linq Apply an OData text query (filter, order by, select, ..) to an IQueryable expression. @@ -93,11 +93,11 @@ See the [Wiki pages](https://github.com/ArnaudB88/OData2Linq/wiki) Please feel free to create issues and pull requests to the main branch. # Nuget -| Package | NuGet | Info | -|---------------------------------|----------------------------------------------------------------------------------|------------------| -| OData2Linq | ![NuGet Version](https://img.shields.io/nuget/v/OData2Linq) | OData v8 support | -| Community.OData.Linq.Json | ![NuGet Version](https://img.shields.io/nuget/v/Community.OData.Linq.Json) | Still works on odata v7 libraries. Open an issue to create a new package for odata v8. | -| Community.OData.Linq.AspNetCore | ![NuGet Version](https://img.shields.io/nuget/v/Community.OData.Linq.AspNetCore) | Still works on odata v7 libraries. Open an issue to create a new package for odata v8. | +| Package | NuGet | Info | +|---------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------|------------------| +| OData2Linq | [![NuGet Version](https://img.shields.io/nuget/v/OData2Linq?label=NuGet)](https://www.nuget.org/packages/OData2Linq/) | OData v8 support | +| Community.OData.Linq.Json | [![NuGet Version](https://img.shields.io/nuget/v/Community.OData.Linq.Json)](https://www.nuget.org/packages/Community.OData.Linq.Json) | Still works on odata v7 libraries. Open an issue to create a new package for odata v8. | +| Community.OData.Linq.AspNetCore | [![NuGet Version](https://img.shields.io/nuget/v/Community.OData.Linq.AspNetCore)](https://www.nuget.org/packages/Community.OData.Linq.AspNetCore) | Still works on odata v7 libraries. Open an issue to create a new package for odata v8. | # References This project is based on the following project: From f61574a91f2ecdd260e2a7c49ac2a43a30e76428 Mon Sep 17 00:00:00 2001 From: Arnaud Boussaer Date: Fri, 14 Feb 2025 10:29:46 +0100 Subject: [PATCH 45/53] Correctly implement a memory usage test --- test/OData2Linq.Tests/ODataTests.cs | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/test/OData2Linq.Tests/ODataTests.cs b/test/OData2Linq.Tests/ODataTests.cs index 1bcedf679..fcec77ec0 100644 --- a/test/OData2Linq.Tests/ODataTests.cs +++ b/test/OData2Linq.Tests/ODataTests.cs @@ -3,6 +3,7 @@ using OData2Linq.Tests.SampleData; using System; using System.Collections.Generic; + using System.Diagnostics; using System.Linq; using Xunit; @@ -23,19 +24,32 @@ public void WithoutKeyThrowException() Assert.Throws(() => SampleWithoutKey.CreateQuery().OData()); } - [Fact] - public void Disposable() + private static double MemoryUsageInMBStart = default; + public static IEnumerable DummyTestData() => Enumerable.Range(0, 1000).Select(i => new object[] { i }); + [Theory] + [MemberData(nameof(DummyTestData))] + public void MemoryUsageShouldNotIncrease(int iteration) { // Generate the list of items to query - var items = new List(); - items.Add(new TestItem() { Id = Guid.NewGuid(), Name = "Test", Number = 1 }); - items.Add(new TestItem() { Id = Guid.NewGuid(), Name = "Another", Number = 2 }); + var items = new List + { + new() { Id = Guid.NewGuid(), Name = "Test", Number = 1 }, + new() { Id = Guid.NewGuid(), Name = "Another", Number = 2 } + }; var odata = items.AsQueryable().OData(); var filteredItems = odata.Filter("Number eq 2"); - //odata.Dispose(); // Test that the OData object is being torn down + //((IDisposable)odata.ServiceProvider).Dispose(); //Makes no difference in memory usage Assert.Equal(1, filteredItems.Count()); + + Process currentProc = Process.GetCurrentProcess(); + var bytesInUse = currentProc.PrivateMemorySize64 / (double)1000_000; + if (MemoryUsageInMBStart == default) + MemoryUsageInMBStart = bytesInUse; + Trace.WriteLine($"Private bytes after test run: {Math.Round(bytesInUse, 2)}MB ({MemoryUsageInMBStart}MB at start)(Iteration {iteration})"); + + Assert.True(bytesInUse - MemoryUsageInMBStart < 10, "Memory usage should not increase by more than 10MB"); } } } \ No newline at end of file From 41d14629b9ecf1c7cd455a0a2d5d318b4d1b9539 Mon Sep 17 00:00:00 2001 From: Arnaud Boussaer Date: Fri, 14 Feb 2025 10:30:10 +0100 Subject: [PATCH 46/53] rename property --- test/OData2Linq.Tests/ODataTests.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/OData2Linq.Tests/ODataTests.cs b/test/OData2Linq.Tests/ODataTests.cs index fcec77ec0..b07bb38be 100644 --- a/test/OData2Linq.Tests/ODataTests.cs +++ b/test/OData2Linq.Tests/ODataTests.cs @@ -25,9 +25,9 @@ public void WithoutKeyThrowException() } private static double MemoryUsageInMBStart = default; - public static IEnumerable DummyTestData() => Enumerable.Range(0, 1000).Select(i => new object[] { i }); + public static IEnumerable Iterations() => Enumerable.Range(0, 1000).Select(i => new object[] { i }); [Theory] - [MemberData(nameof(DummyTestData))] + [MemberData(nameof(Iterations))] public void MemoryUsageShouldNotIncrease(int iteration) { // Generate the list of items to query From 015a773e99bc4819c934d2f4dd6f3d50f4be74ee Mon Sep 17 00:00:00 2001 From: Arnaud Boussaer Date: Fri, 14 Feb 2025 17:06:25 +0100 Subject: [PATCH 47/53] add comments --- test/OData2Linq.Tests/ODataTests.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/OData2Linq.Tests/ODataTests.cs b/test/OData2Linq.Tests/ODataTests.cs index b07bb38be..11e3918b1 100644 --- a/test/OData2Linq.Tests/ODataTests.cs +++ b/test/OData2Linq.Tests/ODataTests.cs @@ -30,17 +30,19 @@ public void WithoutKeyThrowException() [MemberData(nameof(Iterations))] public void MemoryUsageShouldNotIncrease(int iteration) { - // Generate the list of items to query + //Arrange var items = new List { new() { Id = Guid.NewGuid(), Name = "Test", Number = 1 }, new() { Id = Guid.NewGuid(), Name = "Another", Number = 2 } }; + //Act var odata = items.AsQueryable().OData(); var filteredItems = odata.Filter("Number eq 2"); //((IDisposable)odata.ServiceProvider).Dispose(); //Makes no difference in memory usage + //Assert Assert.Equal(1, filteredItems.Count()); Process currentProc = Process.GetCurrentProcess(); From 76fa94d4f13afa67e5de03b372806d8a36bc4f1e Mon Sep 17 00:00:00 2001 From: Arnaud Boussaer Date: Mon, 17 Feb 2025 10:43:05 +0100 Subject: [PATCH 48/53] update MS.AspNetCore.Odata readme with link to OData2Linq readme --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 634b36e6a..211b17ef9 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,7 @@ # ASP.NET Core OData 8.x + +For the OData2Linq readme, [click here](https://github.com/ArnaudB88/OData2Linq). + --- Component | Build | Status From f29b69dfe3d6d7b6fc65541b3de2ae1d891d1713 Mon Sep 17 00:00:00 2001 From: Arnaud Boussaer Date: Mon, 17 Feb 2025 11:02:50 +0100 Subject: [PATCH 49/53] fix merge conflicts --- AspNetCoreOData.sln | 26 +++++++++---------- .../ODataSampleCommon.csproj | 2 +- .../Microsoft.AspNetCore.OData.csproj | 3 ++- ...icrosoft.AspNetCore.OData.E2E.Tests.csproj | 2 +- ...crosoft.AspNetCore.OData.TestCommon.csproj | 2 +- .../Microsoft.AspNetCore.OData.Tests.csproj | 2 +- 6 files changed, 19 insertions(+), 18 deletions(-) diff --git a/AspNetCoreOData.sln b/AspNetCoreOData.sln index 7b2b729a4..0fb572be3 100644 --- a/AspNetCoreOData.sln +++ b/AspNetCoreOData.sln @@ -19,9 +19,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.OData. EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.OData.TestCommon", "test\Microsoft.AspNetCore.OData.TestCommon\Microsoft.AspNetCore.OData.TestCommon.csproj", "{9DB7ACCB-79CC-495C-A145-BC50A7733A21}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OData2Linq", "src\OData2Linq\OData2Linq.csproj", "{568A35C2-8677-41C6-9098-3AEF23286A9E}" +EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OData2Linq.Tests", "test\OData2Linq.Tests\OData2Linq.Tests.csproj", "{E7F48129-7544-4D85-ABE1-4CB037E1C780}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OData2Linq", "src\OData2Linq\OData2Linq.csproj", "{568A35C2-8677-41C6-9098-3AEF23286A9E}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OData2Linq.Benchmark", "test\OData2Linq.Benchmark\OData2Linq.Benchmark.csproj", "{11B8ED5C-E54C-46A4-A5A7-E880E5EC634E}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ODataCustomizedSample", "sample\ODataCustomizedSample\ODataCustomizedSample.csproj", "{BDC5474B-9511-4CDF-83FE-376C7130F7F0}" EndProject @@ -31,8 +33,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}") = "OData2Linq.Benchmark", "test\OData2Linq.Benchmark\OData2Linq.Benchmark.csproj", "{11B8ED5C-E54C-46A4-A5A7-E880E5EC634E}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -59,14 +59,18 @@ Global {9DB7ACCB-79CC-495C-A145-BC50A7733A21}.Debug|Any CPU.Build.0 = Debug|Any CPU {9DB7ACCB-79CC-495C-A145-BC50A7733A21}.Release|Any CPU.ActiveCfg = Release|Any CPU {9DB7ACCB-79CC-495C-A145-BC50A7733A21}.Release|Any CPU.Build.0 = Release|Any CPU - {E7F48129-7544-4D85-ABE1-4CB037E1C780}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E7F48129-7544-4D85-ABE1-4CB037E1C780}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E7F48129-7544-4D85-ABE1-4CB037E1C780}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E7F48129-7544-4D85-ABE1-4CB037E1C780}.Release|Any CPU.Build.0 = Release|Any CPU {568A35C2-8677-41C6-9098-3AEF23286A9E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {568A35C2-8677-41C6-9098-3AEF23286A9E}.Debug|Any CPU.Build.0 = Debug|Any CPU {568A35C2-8677-41C6-9098-3AEF23286A9E}.Release|Any CPU.ActiveCfg = Release|Any CPU {568A35C2-8677-41C6-9098-3AEF23286A9E}.Release|Any CPU.Build.0 = Release|Any CPU + {E7F48129-7544-4D85-ABE1-4CB037E1C780}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E7F48129-7544-4D85-ABE1-4CB037E1C780}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E7F48129-7544-4D85-ABE1-4CB037E1C780}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E7F48129-7544-4D85-ABE1-4CB037E1C780}.Release|Any CPU.Build.0 = Release|Any CPU + {11B8ED5C-E54C-46A4-A5A7-E880E5EC634E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {11B8ED5C-E54C-46A4-A5A7-E880E5EC634E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {11B8ED5C-E54C-46A4-A5A7-E880E5EC634E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {11B8ED5C-E54C-46A4-A5A7-E880E5EC634E}.Release|Any CPU.Build.0 = Release|Any CPU {BDC5474B-9511-4CDF-83FE-376C7130F7F0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {BDC5474B-9511-4CDF-83FE-376C7130F7F0}.Debug|Any CPU.Build.0 = Debug|Any CPU {BDC5474B-9511-4CDF-83FE-376C7130F7F0}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -83,10 +87,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 - {11B8ED5C-E54C-46A4-A5A7-E880E5EC634E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {11B8ED5C-E54C-46A4-A5A7-E880E5EC634E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {11B8ED5C-E54C-46A4-A5A7-E880E5EC634E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {11B8ED5C-E54C-46A4-A5A7-E880E5EC634E}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -97,13 +97,13 @@ Global {FF17DFD5-7ED0-433F-8DD9-4576E0CFFFBA} = {F994C269-55BA-44F0-9DA7-6D5A3CFA79EB} {E428817E-A93C-4C1F-9D20-F5CA8C3627C1} = {F994C269-55BA-44F0-9DA7-6D5A3CFA79EB} {9DB7ACCB-79CC-495C-A145-BC50A7733A21} = {F994C269-55BA-44F0-9DA7-6D5A3CFA79EB} - {E7F48129-7544-4D85-ABE1-4CB037E1C780} = {F994C269-55BA-44F0-9DA7-6D5A3CFA79EB} {568A35C2-8677-41C6-9098-3AEF23286A9E} = {2F0E102B-EB33-4025-BE56-7B8F9D2C4B8A} + {E7F48129-7544-4D85-ABE1-4CB037E1C780} = {F994C269-55BA-44F0-9DA7-6D5A3CFA79EB} + {11B8ED5C-E54C-46A4-A5A7-E880E5EC634E} = {F994C269-55BA-44F0-9DA7-6D5A3CFA79EB} {BDC5474B-9511-4CDF-83FE-376C7130F7F0} = {B1F86961-6958-4617-ACA4-C231F95AE099} {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} - {11B8ED5C-E54C-46A4-A5A7-E880E5EC634E} = {F994C269-55BA-44F0-9DA7-6D5A3CFA79EB} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {540C9752-AAC0-49EA-BA60-78490C90FF86} diff --git a/sample/ODataSampleCommon/ODataSampleCommon.csproj b/sample/ODataSampleCommon/ODataSampleCommon.csproj index 3e0a50a8d..3d4bc25fd 100644 --- a/sample/ODataSampleCommon/ODataSampleCommon.csproj +++ b/sample/ODataSampleCommon/ODataSampleCommon.csproj @@ -1,7 +1,7 @@  - net6.0 + net5.0 Library true diff --git a/src/Microsoft.AspNetCore.OData/Microsoft.AspNetCore.OData.csproj b/src/Microsoft.AspNetCore.OData/Microsoft.AspNetCore.OData.csproj index 5e7dbe8fa..b526c3f2a 100644 --- a/src/Microsoft.AspNetCore.OData/Microsoft.AspNetCore.OData.csproj +++ b/src/Microsoft.AspNetCore.OData/Microsoft.AspNetCore.OData.csproj @@ -1,7 +1,8 @@  - net6.0 + netcoreapp3.1;net5.0 + $(TargetFrameworks);net6.0 Microsoft.AspNetCore.OData $(OutputPath)$(AssemblyName).xml true diff --git a/test/Microsoft.AspNetCore.OData.E2E.Tests/Microsoft.AspNetCore.OData.E2E.Tests.csproj b/test/Microsoft.AspNetCore.OData.E2E.Tests/Microsoft.AspNetCore.OData.E2E.Tests.csproj index ea54445ad..b4073ca90 100644 --- a/test/Microsoft.AspNetCore.OData.E2E.Tests/Microsoft.AspNetCore.OData.E2E.Tests.csproj +++ b/test/Microsoft.AspNetCore.OData.E2E.Tests/Microsoft.AspNetCore.OData.E2E.Tests.csproj @@ -1,7 +1,7 @@  - net6.0 + netcoreapp3.1;net6.0 Microsoft.AspNetCore.OData.E2E.Tests Microsoft.AspNetCore.OData.E2E.Tests diff --git a/test/Microsoft.AspNetCore.OData.TestCommon/Microsoft.AspNetCore.OData.TestCommon.csproj b/test/Microsoft.AspNetCore.OData.TestCommon/Microsoft.AspNetCore.OData.TestCommon.csproj index 54252380b..a6922e9bd 100644 --- a/test/Microsoft.AspNetCore.OData.TestCommon/Microsoft.AspNetCore.OData.TestCommon.csproj +++ b/test/Microsoft.AspNetCore.OData.TestCommon/Microsoft.AspNetCore.OData.TestCommon.csproj @@ -1,7 +1,7 @@  - net6.0 + netcoreapp3.1;net6.0 Microsoft.AspNetCore.OData.TestCommon Microsoft.AspNetCore.OData.TestCommon diff --git a/test/Microsoft.AspNetCore.OData.Tests/Microsoft.AspNetCore.OData.Tests.csproj b/test/Microsoft.AspNetCore.OData.Tests/Microsoft.AspNetCore.OData.Tests.csproj index 88ad04d2d..d105f21ba 100644 --- a/test/Microsoft.AspNetCore.OData.Tests/Microsoft.AspNetCore.OData.Tests.csproj +++ b/test/Microsoft.AspNetCore.OData.Tests/Microsoft.AspNetCore.OData.Tests.csproj @@ -1,7 +1,7 @@  - net6.0 + netcoreapp3.1;net6.0 Microsoft.AspNetCore.OData.Tests Microsoft.AspNetCore.OData.Tests From a6f6c08bee1c48438b4f63709c7ba2223428bed6 Mon Sep 17 00:00:00 2001 From: Arnaud Boussaer Date: Mon, 17 Feb 2025 11:25:03 +0100 Subject: [PATCH 50/53] Merge with ASP.NET Core OData to v8.2.5, bump OData2Linq version to v1.1.0 --- src/OData2Linq/OData2Linq.csproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/OData2Linq/OData2Linq.csproj b/src/OData2Linq/OData2Linq.csproj index 955d319b9..612dc6a0d 100644 --- a/src/OData2Linq/OData2Linq.csproj +++ b/src/OData2Linq/OData2Linq.csproj @@ -7,7 +7,7 @@ True FriendAssemblies.snk True - 1.0.0 + 1.1.0 MIT README.md https://github.com/ArnaudB88/OData2Linq @@ -18,7 +18,7 @@ - + From bcd15fe4e0b0d30fa810cc6a1c5280ddce601e50 Mon Sep 17 00:00:00 2001 From: Arnaud Boussaer Date: Mon, 17 Feb 2025 11:29:25 +0100 Subject: [PATCH 51/53] fix merge conflict in readme --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index bdd498ff9..3a1297d8d 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ # ASP.NET Core OData 8.x +--- For the OData2Linq readme, [click here](https://github.com/ArnaudB88/OData2Linq). From 1f7df83589aa9c5456b9ba34f539f520d9d59906 Mon Sep 17 00:00:00 2001 From: Arnaud Boussaer Date: Mon, 17 Feb 2025 11:56:11 +0100 Subject: [PATCH 52/53] fix build in pipeline --- .../Microsoft.AspNetCore.OData.E2E.Tests.csproj | 2 +- .../Microsoft.AspNetCore.OData.TestCommon.csproj | 2 +- .../Microsoft.AspNetCore.OData.Tests.csproj | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/test/Microsoft.AspNetCore.OData.E2E.Tests/Microsoft.AspNetCore.OData.E2E.Tests.csproj b/test/Microsoft.AspNetCore.OData.E2E.Tests/Microsoft.AspNetCore.OData.E2E.Tests.csproj index 8c34cdb81..2ee3e4771 100644 --- a/test/Microsoft.AspNetCore.OData.E2E.Tests/Microsoft.AspNetCore.OData.E2E.Tests.csproj +++ b/test/Microsoft.AspNetCore.OData.E2E.Tests/Microsoft.AspNetCore.OData.E2E.Tests.csproj @@ -1,7 +1,7 @@  - netcoreapp3.1;net6.0 + net6.0 Microsoft.AspNetCore.OData.E2E.Tests Microsoft.AspNetCore.OData.E2E.Tests diff --git a/test/Microsoft.AspNetCore.OData.TestCommon/Microsoft.AspNetCore.OData.TestCommon.csproj b/test/Microsoft.AspNetCore.OData.TestCommon/Microsoft.AspNetCore.OData.TestCommon.csproj index a6922e9bd..54252380b 100644 --- a/test/Microsoft.AspNetCore.OData.TestCommon/Microsoft.AspNetCore.OData.TestCommon.csproj +++ b/test/Microsoft.AspNetCore.OData.TestCommon/Microsoft.AspNetCore.OData.TestCommon.csproj @@ -1,7 +1,7 @@  - netcoreapp3.1;net6.0 + net6.0 Microsoft.AspNetCore.OData.TestCommon Microsoft.AspNetCore.OData.TestCommon diff --git a/test/Microsoft.AspNetCore.OData.Tests/Microsoft.AspNetCore.OData.Tests.csproj b/test/Microsoft.AspNetCore.OData.Tests/Microsoft.AspNetCore.OData.Tests.csproj index f84ccb1f1..160ba39ee 100644 --- a/test/Microsoft.AspNetCore.OData.Tests/Microsoft.AspNetCore.OData.Tests.csproj +++ b/test/Microsoft.AspNetCore.OData.Tests/Microsoft.AspNetCore.OData.Tests.csproj @@ -1,7 +1,7 @@  - netcoreapp3.1;net6.0 + net6.0 Microsoft.AspNetCore.OData.Tests Microsoft.AspNetCore.OData.Tests From 56c379d34be0e34301bab7880b7de827e0155c1c Mon Sep 17 00:00:00 2001 From: Arnaud Boussaer Date: Mon, 17 Feb 2025 12:09:45 +0100 Subject: [PATCH 53/53] undo last commit to fix merge conflicts --- .../Microsoft.AspNetCore.OData.E2E.Tests.csproj | 2 +- .../Microsoft.AspNetCore.OData.TestCommon.csproj | 2 +- .../Microsoft.AspNetCore.OData.Tests.csproj | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/test/Microsoft.AspNetCore.OData.E2E.Tests/Microsoft.AspNetCore.OData.E2E.Tests.csproj b/test/Microsoft.AspNetCore.OData.E2E.Tests/Microsoft.AspNetCore.OData.E2E.Tests.csproj index 2ee3e4771..8c34cdb81 100644 --- a/test/Microsoft.AspNetCore.OData.E2E.Tests/Microsoft.AspNetCore.OData.E2E.Tests.csproj +++ b/test/Microsoft.AspNetCore.OData.E2E.Tests/Microsoft.AspNetCore.OData.E2E.Tests.csproj @@ -1,7 +1,7 @@  - net6.0 + netcoreapp3.1;net6.0 Microsoft.AspNetCore.OData.E2E.Tests Microsoft.AspNetCore.OData.E2E.Tests diff --git a/test/Microsoft.AspNetCore.OData.TestCommon/Microsoft.AspNetCore.OData.TestCommon.csproj b/test/Microsoft.AspNetCore.OData.TestCommon/Microsoft.AspNetCore.OData.TestCommon.csproj index 54252380b..a6922e9bd 100644 --- a/test/Microsoft.AspNetCore.OData.TestCommon/Microsoft.AspNetCore.OData.TestCommon.csproj +++ b/test/Microsoft.AspNetCore.OData.TestCommon/Microsoft.AspNetCore.OData.TestCommon.csproj @@ -1,7 +1,7 @@  - net6.0 + netcoreapp3.1;net6.0 Microsoft.AspNetCore.OData.TestCommon Microsoft.AspNetCore.OData.TestCommon diff --git a/test/Microsoft.AspNetCore.OData.Tests/Microsoft.AspNetCore.OData.Tests.csproj b/test/Microsoft.AspNetCore.OData.Tests/Microsoft.AspNetCore.OData.Tests.csproj index 160ba39ee..f84ccb1f1 100644 --- a/test/Microsoft.AspNetCore.OData.Tests/Microsoft.AspNetCore.OData.Tests.csproj +++ b/test/Microsoft.AspNetCore.OData.Tests/Microsoft.AspNetCore.OData.Tests.csproj @@ -1,7 +1,7 @@  - net6.0 + netcoreapp3.1;net6.0 Microsoft.AspNetCore.OData.Tests Microsoft.AspNetCore.OData.Tests