├── global.json ├── test ├── sql_scripts │ └── sqlserver.sql ├── OdataToEntity.Test │ ├── Common │ │ ├── ModelBoundTestKind.cs │ │ ├── QueryParameters.cs │ │ ├── EfInclude.cs │ │ └── PageNextLinkModelBoundBuilder.cs │ ├── OrderDataAdapter.cs │ ├── Program.cs │ ├── Properties │ │ └── AssemblyInfo.cs │ └── OdataToEntity.Test.csproj ├── OdataToEntity.Test.DynamicDataContext │ └── Properties │ │ └── launchSettings.json ├── OdataToEntity.Test.InMemory │ ├── ModelBoundTest.cs │ ├── Program.cs │ └── InMemoryOrderContext.cs ├── OdataToEntity.Test.Asp │ ├── OdataToEntity.Test.AspMvcServer │ │ ├── appsettings.json │ │ ├── appsettings.Development.json │ │ ├── Properties │ │ │ └── launchSettings.json │ │ ├── Program.cs │ │ ├── Controllers │ │ │ ├── MetadataController.cs │ │ │ ├── BatchController.cs │ │ │ ├── ManyColumnsViewController.cs │ │ │ ├── OrderItemsViewController.cs │ │ │ ├── ManyColumnsController.cs │ │ │ ├── CategoriesController.cs │ │ │ ├── OrderItemsController.cs │ │ │ └── ShippingAddressesController.cs │ │ ├── Startup.cs │ │ └── OdataToEntity.Test.AspMvcServer.csproj │ ├── OdataToEntity.Test.AspServer │ │ ├── appsettings.json │ │ ├── Properties │ │ │ └── launchSettings.json │ │ ├── Program.cs │ │ ├── Startup.cs │ │ └── OdataToEntity.Test.AspServer.csproj │ └── OdataToEntity.Test.AspClient │ │ ├── OeResponse.cs │ │ ├── DbFunctionAttribute.cs │ │ ├── Connected Services │ │ └── ODataClient │ │ │ └── ConnectedService.json │ │ ├── ODataClient.cs │ │ └── Program.cs ├── OdataToEntity.Test.EfCore.PostgreSql │ ├── Program.cs │ ├── OrderDataAdapter.cs │ ├── OrderContextOptions.cs │ └── OeEfCorePostgreSqlDataAdapter.cs ├── OdataToEntity.Test.DynamicDataContext.AspServer │ ├── InformationSchemaMapping.cs │ ├── Program.cs │ ├── appsettings.json │ ├── InformationSchemaMapping.json │ └── OdataToEntity.Test.DynamicDataContext.AspServer.csproj ├── OdataToEntity.Test.GraphQL │ ├── Order2DataAdapter.cs │ ├── StarWars │ │ ├── StarWarsDataAdapter.cs │ │ └── Character.cs │ ├── StarWarsFixture.cs │ ├── OeGraphQLHelper.cs │ ├── OdataToEntity.Test.GraphQL.csproj │ ├── DbFixture.cs │ └── Program.cs ├── OdataToEntity.Test.Ef6.SqlServer │ ├── Program.cs │ ├── OrderDataAdapter.cs │ └── OeEf6SqlServerDataAdapter.cs ├── OdataToEntity.Test.EfCore.SqlServer │ ├── Program.cs │ ├── OrderDataAdapter.cs │ └── OeEfCoreSqlServerDataAdapter.cs ├── OdataToEntity.Test.Linq2Db │ ├── Program.cs │ ├── OeLinq2DbSqlServerDataAdapter.cs │ └── OrderDataAdapter.cs ├── OdataToEntity.Test.Wcf │ ├── OdataToEntity.Test.WcfClient │ │ └── OrderContextOptions.cs │ └── OdataToEntity.Test.WcfService │ │ ├── OrderService.cs │ │ ├── IOdataWcf.cs │ │ ├── Program.cs │ │ └── OdataWcfService.cs └── OdataToEntity.Test.Linq2DbModel │ └── OdataToEntity.Test.Linq2DbModel.csproj ├── .gitmodules ├── source ├── OdataToEntity │ ├── Query │ │ ├── QueryOptionSetting.cs │ │ ├── SelectExpandType.cs │ │ ├── Builder │ │ │ ├── OeModelBoundKind.cs │ │ │ └── OeModelBoundFluentBuilder.cs │ │ ├── CountAttribute.cs │ │ ├── PageAttribute.cs │ │ ├── SelectAttribute.cs │ │ ├── ExpandAttribute.cs │ │ ├── FilterAttribute.cs │ │ └── OrderByAttribute.cs │ ├── ModelBuilder │ │ ├── OeValueAnnotation.cs │ │ ├── OeEdmStructuralShadowProperty.cs │ │ ├── ManyToManyJoinDescription.cs │ │ ├── OeModelBuilderHelper.cs │ │ └── OeEdmNavigationShadowProperty.cs │ ├── Infrastructure │ │ ├── OeOdataValue.cs │ │ ├── AllowNullAttribute.cs │ │ ├── FastActivator.cs │ │ └── OeDataTableHelper.cs │ ├── Parsers │ │ ├── OeEntryFactoryFactory.cs │ │ ├── Translators │ │ │ ├── OeSelectTranslatorParameters.cs │ │ │ ├── OeNextLinkSelectItem.cs │ │ │ ├── OePageSelectItem.cs │ │ │ └── OeAggregationEntryFactoryFactory.cs │ │ ├── Cache │ │ │ ├── OeQueryCacheDbParameterValue.cs │ │ │ ├── OeQueryCacheDbParameterDefinition.cs │ │ │ ├── UriCompare │ │ │ │ └── OeComparerExtension.cs │ │ │ ├── OeQueryCacheItem.cs │ │ │ ├── OeCacheContextEqualityComparer.cs │ │ │ ├── OeQueryCache.cs │ │ │ └── OeCacheContext.cs │ │ ├── OeDynamicType.cs │ │ ├── Visitors │ │ │ ├── ReplaceParameterVisitor.cs │ │ │ ├── SelectNullableVisitor.cs │ │ │ └── OeConstantToParameterVisitor.cs │ │ ├── OeEnumerableStub.cs │ │ └── OeNavigationEntryFactory.cs │ ├── Db │ │ ├── OeDataAdapterExtension.cs │ │ ├── OeBoundFunctionAttribute.cs │ │ ├── OeDataAdapter.cs │ │ ├── OeEntitySetAdapter.cs │ │ └── OeEntityDbEnumerator.cs │ ├── InMemory │ │ ├── InMemorySourceVisitor.cs │ │ ├── InMemoryScalarExecutor.cs │ │ ├── InMemoryQueryableWrapper.cs │ │ ├── InMemoryOperationAdapter.cs │ │ └── InMemoryExecutor.cs │ └── OdataToEntity.csproj ├── OdataToEntity.Linq2Db │ ├── OeLinq2DbDataAdapterExtension.cs │ ├── EntityUpdateHelper.cs │ ├── OdataToEntity.Linq2Db.csproj │ └── SetBuilder.cs ├── OdataToEntity.AspNetCore │ ├── OePageMiddleware.cs │ ├── OeHttpRequestHeaders.cs │ ├── ODataPrimitiveResult.cs │ ├── ODataResult.cs │ ├── OdataToEntity.AspNetCore.csproj │ ├── UriHelper.cs │ └── OeMetadataController.cs ├── OdataToEntity.Ef6 │ ├── OeEf6DataAdapterExtension.cs │ ├── OeEf6EnumerableToQuerableVisitor.cs │ ├── OeEf6AsyncEnumerator.cs │ ├── OdataToEntity.Ef6.csproj │ └── DummyCommandBuilder.cs ├── OdataToEntity.EfCore.DynamicDataContext │ ├── ModelBuilder │ │ ├── DynamicPropertyInfo.cs │ │ ├── DynamicDependentPropertyInfo.cs │ │ ├── DynamicConventionSetBuilder.cs │ │ └── DynamicEdmModelMetadataProvider.cs │ ├── InformationSchema │ │ ├── SqlServerModelCustomizer.cs │ │ ├── MySqlSqlGenerationHelper.cs │ │ └── ProviderSpecificSchema.cs │ ├── DynamicDbContext.cs │ ├── DynamicTypeDefinitionManagerFactory.cs │ ├── DynamicTypeDefinition.cs │ ├── DynamicMiddlewareHelper.cs │ ├── OdataToEntity.EfCore.DynamicDataContext.csproj │ └── Types │ │ └── DynamicDbContexts.cs ├── OdataToEntity.EfCore │ ├── OeEfCoreDataAdapterExtension.cs │ ├── OeMySqlEfCoreOperationAdapter.cs │ ├── Postgresql │ │ ├── OeEfCorePostgreSqlDataAdapter.cs │ │ └── OePostgreSqlEfCoreOperationAdapter.cs │ ├── OeEfCoreDataReaderAsyncEnumerator.cs │ ├── OeDbQueryAdapter.cs │ └── OdataToEntity.EfCore.csproj ├── OdataToEntity.GraphQL │ ├── GraphqlExtension.cs │ ├── OdataToEntity.GraphQL.csproj │ ├── OeGraphqlParser.cs │ └── OeSchemaBuilder.cs └── Directory.Build.targets ├── .travis.yml ├── dependencies.props ├── .editorconfig ├── LICENSE └── sln ├── OdataToEntity.Test.EfCore.SqlServer.sln ├── OdataToEntity.Test.InMemory.sln ├── OdataToEntity.Test.EfCore.PostgreSql.sln └── OdataToEntity.Test.sln /global.json: -------------------------------------------------------------------------------- 1 | { 2 | "sdk": { 3 | "version": "5.0.100-*", 4 | "rollForward": "feature" 5 | } 6 | } -------------------------------------------------------------------------------- /test/sql_scripts/sqlserver.sql: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voronov-maxim/OdataToEntity/HEAD/test/sql_scripts/sqlserver.sql -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "test/OdataToEntity.Test.Wcf/CoreWcf"] 2 | path = test/OdataToEntity.Test.Wcf/CoreWcf 3 | url = https://github.com/CoreWCF/CoreWCF.git 4 | branch = master 5 | -------------------------------------------------------------------------------- /source/OdataToEntity/Query/QueryOptionSetting.cs: -------------------------------------------------------------------------------- 1 | namespace OdataToEntity.Query 2 | { 3 | public enum QueryOptionSetting 4 | { 5 | Allowed, 6 | Disabled 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /source/OdataToEntity/Query/SelectExpandType.cs: -------------------------------------------------------------------------------- 1 | namespace OdataToEntity.Query 2 | { 3 | public enum SelectExpandType 4 | { 5 | Allowed, 6 | Automatic, 7 | Disabled 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /test/OdataToEntity.Test/Common/ModelBoundTestKind.cs: -------------------------------------------------------------------------------- 1 | namespace OdataToEntity.Test 2 | { 3 | public enum ModelBoundTestKind 4 | { 5 | No, 6 | Attribute, 7 | Fluent 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /source/OdataToEntity/Query/Builder/OeModelBoundKind.cs: -------------------------------------------------------------------------------- 1 | namespace OdataToEntity.Query.Builder 2 | { 3 | public enum OeModelBoundKind 4 | { 5 | Filter, 6 | OrderBy, 7 | Select 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /test/OdataToEntity.Test.DynamicDataContext/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "OdataToEntity.Test.DynamicDataContext": { 4 | "commandName": "Project", 5 | "commandLineArgs": "provider=mysql" 6 | } 7 | } 8 | } -------------------------------------------------------------------------------- /test/OdataToEntity.Test.InMemory/ModelBoundTest.cs: -------------------------------------------------------------------------------- 1 | namespace OdataToEntity.Test 2 | { 3 | public abstract class ModelBoundTest 4 | { 5 | protected ModelBoundTest(DbFixtureInitDb fixture) 6 | { 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /test/OdataToEntity.Test.Asp/OdataToEntity.Test.AspMvcServer/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "IncludeScopes": false, 4 | "LogLevel": { 5 | "Default": "Debug", 6 | "System": "Information", 7 | "Microsoft": "Information" 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /test/OdataToEntity.Test.Asp/OdataToEntity.Test.AspMvcServer/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "IncludeScopes": false, 4 | "LogLevel": { 5 | "Default": "Debug", 6 | "System": "Information", 7 | "Microsoft": "Information" 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /source/OdataToEntity/ModelBuilder/OeValueAnnotation.cs: -------------------------------------------------------------------------------- 1 | namespace OdataToEntity.ModelBuilder 2 | { 3 | public sealed class OeValueAnnotation 4 | { 5 | public OeValueAnnotation(T value) 6 | { 7 | Value = value; 8 | } 9 | 10 | public T Value { get; } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /test/OdataToEntity.Test.Asp/OdataToEntity.Test.AspServer/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "IncludeScopes": false, 4 | "LogLevel": { 5 | "Default": "Debug", 6 | "System": "Information", 7 | "Microsoft": "Information" 8 | } 9 | }, 10 | "server.urls": "http://localhost:5000" 11 | } 12 | -------------------------------------------------------------------------------- /source/OdataToEntity/Infrastructure/OeOdataValue.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.OData; 2 | 3 | namespace OdataToEntity.Infrastructure 4 | { 5 | public sealed class OeOdataValue : ODataValue 6 | { 7 | public OeOdataValue(T value) 8 | { 9 | Value = value; 10 | } 11 | 12 | public T Value { get; } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /source/OdataToEntity/Parsers/OeEntryFactoryFactory.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.OData.Edm; 2 | using System; 3 | 4 | namespace OdataToEntity.Parsers 5 | { 6 | public abstract class OeEntryFactoryFactory 7 | { 8 | public abstract OeEntryFactory CreateEntryFactory(IEdmEntitySet entitySet, Type clrType, OePropertyAccessor[]? skipTokenAccessors); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /source/OdataToEntity/Query/CountAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace OdataToEntity.Query 4 | { 5 | [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property)] 6 | public sealed class CountAttribute : Attribute 7 | { 8 | public bool Disabled 9 | { 10 | get; 11 | set; 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /source/OdataToEntity/Parsers/Translators/OeSelectTranslatorParameters.cs: -------------------------------------------------------------------------------- 1 | namespace OdataToEntity.Parsers.Translators 2 | { 3 | public struct OeSelectTranslatorParameters 4 | { 5 | public bool IsDatabaseNullHighestValue { get; set; } 6 | public OeMetadataLevel MetadataLevel { get; set; } 7 | public OeSkipTokenNameValue[] SkipTokenNameValues { get; set; } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /source/OdataToEntity/Query/PageAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace OdataToEntity.Query 4 | { 5 | [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property)] 6 | public sealed class PageAttribute : Attribute 7 | { 8 | public int MaxTop { get; set; } 9 | public int PageSize { get; set; } 10 | public bool NavigationNextLink { get; set; } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /test/OdataToEntity.Test.InMemory/Program.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using Xunit; 3 | 4 | [assembly: CollectionBehavior(DisableTestParallelization = true)] 5 | 6 | namespace OdataToEntity.Test.InMemory 7 | { 8 | class Program 9 | { 10 | static async Task Main() 11 | { 12 | await new RDBNull(new RDBNull_DbFixtureInitDb()).FilterStringLe(0); 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /test/OdataToEntity.Test.EfCore.PostgreSql/Program.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using Xunit; 3 | 4 | [assembly: CollectionBehavior(DisableTestParallelization = true)] 5 | 6 | namespace OdataToEntity.Test.Postgresql 7 | { 8 | class Program 9 | { 10 | static async Task Main() 11 | { 12 | await new RDBNull(new RDBNull_DbFixtureInitDb()).DbQuery(1); 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /test/OdataToEntity.Test.DynamicDataContext.AspServer/InformationSchemaMapping.cs: -------------------------------------------------------------------------------- 1 | using OdataToEntity.EfCore.DynamicDataContext.InformationSchema; 2 | using System.Collections.Generic; 3 | 4 | namespace OdataToEntity.Test.DynamicDataContext.AspServer 5 | { 6 | public sealed class InformationSchemaMapping 7 | { 8 | public IReadOnlyList? Operations { get; set; } 9 | public IReadOnlyList? Tables { get;set;} 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /test/OdataToEntity.Test.GraphQL/Order2DataAdapter.cs: -------------------------------------------------------------------------------- 1 | using OdataToEntity.Test.GraphQL.StarWars; 2 | using System; 3 | 4 | namespace OdataToEntity.Test.Model 5 | { 6 | public sealed class Order2DataAdapter : EfCore.OeEfCoreDataAdapter 7 | { 8 | public Order2DataAdapter(bool allowCache, String databaseName) : 9 | base(StarWarsContext.Create(databaseName), new Cache.OeQueryCache(allowCache)) 10 | { 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /test/OdataToEntity.Test.Ef6.SqlServer/Program.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using Xunit; 3 | 4 | [assembly: CollectionBehavior(DisableTestParallelization = true)] 5 | 6 | namespace OdataToEntity.Test.Ef6.SqlServer 7 | { 8 | class Program 9 | { 10 | static void Main(string[] args) 11 | { 12 | //EfCore.SqlServer.PerformanceCacheTest.RunTest(100); 13 | new RDBNull(new RDBNull_DbFixtureInitDb()).Table(0).GetAwaiter().GetResult(); 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /test/OdataToEntity.Test.Asp/OdataToEntity.Test.AspServer/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:5000", 7 | "sslPort": 0 8 | } 9 | }, 10 | "profiles": { 11 | "console": { 12 | "commandName": "Project", 13 | "environmentVariables": { 14 | "ASPNETCORE_ENVIRONMENT": "Development" 15 | } 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /source/OdataToEntity/Db/OeDataAdapterExtension.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.OData.Edm; 2 | using OdataToEntity.ModelBuilder; 3 | 4 | namespace OdataToEntity 5 | { 6 | public static class OeDataAdapterExtension 7 | { 8 | public static EdmModel BuildEdmModel(this Db.OeDataAdapter dataAdapter, params IEdmModel[] refModels) 9 | { 10 | var modelBuilder = new OeEdmModelBuilder(dataAdapter, new OeEdmModelMetadataProvider()); 11 | return modelBuilder.BuildEdmModel(refModels); 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /source/OdataToEntity/Parsers/Cache/OeQueryCacheDbParameterValue.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace OdataToEntity.Cache 4 | { 5 | public readonly struct OeQueryCacheDbParameterValue 6 | { 7 | public OeQueryCacheDbParameterValue(String parameterName, Object? parameterValue) 8 | { 9 | ParameterName = parameterName; 10 | ParameterValue = parameterValue; 11 | } 12 | 13 | public String ParameterName { get; } 14 | public Object? ParameterValue { get; } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /source/OdataToEntity/Parsers/Cache/OeQueryCacheDbParameterDefinition.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace OdataToEntity.Cache 4 | { 5 | public readonly struct OeQueryCacheDbParameterDefinition 6 | { 7 | public OeQueryCacheDbParameterDefinition(String parameterName, Type parameterType) 8 | { 9 | ParameterName = parameterName; 10 | ParameterType = parameterType; 11 | } 12 | 13 | public String ParameterName { get; } 14 | public Type ParameterType { get; } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /test/OdataToEntity.Test.Asp/OdataToEntity.Test.AspMvcServer/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:58907/", 7 | "sslPort": 0 8 | } 9 | }, 10 | "profiles": { 11 | "console": { 12 | "commandName": "Project", 13 | "environmentVariables": { 14 | "ASPNETCORE_ENVIRONMENT": "Development" 15 | }, 16 | "applicationUrl": "http://localhost:5000/" 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /source/OdataToEntity.Linq2Db/OeLinq2DbDataAdapterExtension.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.OData.Edm; 2 | using OdataToEntity.ModelBuilder; 3 | 4 | namespace OdataToEntity.Linq2Db 5 | { 6 | public static class OeLinq2DbDataAdapterExtension 7 | { 8 | public static EdmModel BuildEdmModelFromLinq2DbModel(this Db.OeDataAdapter dataAdapter, params IEdmModel[] refModels) 9 | { 10 | var modelBuilder = new OeEdmModelBuilder(dataAdapter, new OeLinq2DbEdmModelMetadataProvider()); 11 | return modelBuilder.BuildEdmModel(refModels); 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /test/OdataToEntity.Test.Asp/OdataToEntity.Test.AspClient/OeResponse.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | 4 | namespace OdataToEntity 5 | { 6 | public sealed class OeResponse 7 | { 8 | private readonly String _contentType; 9 | private readonly Stream _stream; 10 | 11 | public OeResponse(Stream stream, String contentType) 12 | { 13 | _stream = stream; 14 | _contentType = contentType; 15 | } 16 | 17 | public String ContentType => _contentType; 18 | public Stream Stream => _stream; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /test/OdataToEntity.Test.EfCore.SqlServer/Program.cs: -------------------------------------------------------------------------------- 1 | using OdataToEntity.Test.Model; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | using Xunit; 7 | 8 | [assembly: CollectionBehavior(DisableTestParallelization = true)] 9 | 10 | namespace OdataToEntity.Test.EfCore.SqlServer 11 | { 12 | class Program 13 | { 14 | static async Task Main() 15 | { 16 | //PerformanceCacheTest.RunTest(100); 17 | await new PLNull(new PLNull_DbFixtureInitDb()).ApplyGroupByVirtualCount(0); 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /source/OdataToEntity/Db/OeBoundFunctionAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace OdataToEntity.Db 4 | { 5 | [AttributeUsage(AttributeTargets.Method)] 6 | public sealed class OeBoundFunctionAttribute : Attribute 7 | { 8 | public OeBoundFunctionAttribute(String collectionFunctionName, String singleFunctionName) 9 | { 10 | CollectionFunctionName = collectionFunctionName; 11 | SingleFunctionName = singleFunctionName; 12 | } 13 | 14 | public String CollectionFunctionName { get; } 15 | public String SingleFunctionName { get; } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /source/OdataToEntity/Parsers/OeDynamicType.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace OdataToEntity.Parsers 5 | { 6 | public abstract class OeDynamicType 7 | { 8 | private readonly Dictionary _indexedProperties; 9 | 10 | protected OeDynamicType() 11 | { 12 | _indexedProperties = new Dictionary(); 13 | } 14 | 15 | public Object this[String name] 16 | { 17 | get => _indexedProperties[name]; 18 | set => _indexedProperties[name] = value; 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /source/OdataToEntity/ModelBuilder/OeEdmStructuralShadowProperty.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.OData.Edm; 2 | using System; 3 | using System.Reflection; 4 | 5 | namespace OdataToEntity.ModelBuilder 6 | { 7 | public sealed class OeEdmStructuralShadowProperty : EdmStructuralProperty 8 | { 9 | public OeEdmStructuralShadowProperty(IEdmStructuredType declaringType, String name, IEdmTypeReference type, PropertyInfo propertyInfo) 10 | : base(declaringType, name, type) 11 | { 12 | PropertyInfo = propertyInfo; 13 | } 14 | 15 | public PropertyInfo PropertyInfo { get; } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /test/OdataToEntity.Test.DynamicDataContext.AspServer/Program.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore; 2 | using Microsoft.AspNetCore.Hosting; 3 | using System; 4 | using System.IO; 5 | 6 | namespace OdataToEntity.Test.DynamicDataContext.AspServer 7 | { 8 | class Program 9 | { 10 | static void Main(String[] args) 11 | { 12 | var host = WebHost.CreateDefaultBuilder(args) 13 | .UseStartup() 14 | .UseKestrel() 15 | .UseContentRoot(Directory.GetCurrentDirectory()) 16 | .Build(); 17 | 18 | host.Run(); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /test/OdataToEntity.Test.Asp/OdataToEntity.Test.AspClient/DbFunctionAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace OdataToEntity.Test 4 | { 5 | [AttributeUsage(AttributeTargets.Method)] 6 | public class DbFunctionAttribute : Attribute 7 | { 8 | public DbFunctionAttribute() 9 | { 10 | } 11 | public DbFunctionAttribute(String functionName, String schema = null) 12 | { 13 | FunctionName = functionName; 14 | Schema = schema; 15 | } 16 | 17 | public virtual String FunctionName { get; set; } 18 | public virtual String Schema { get; set; } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /test/OdataToEntity.Test.Asp/OdataToEntity.Test.AspMvcServer/Program.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore; 2 | using Microsoft.AspNetCore.Hosting; 3 | using System; 4 | using System.IO; 5 | 6 | namespace OdataToEntity.Test.AspMvcServer 7 | { 8 | public static class Program 9 | { 10 | public static void Main(String[] args) 11 | { 12 | var host = WebHost.CreateDefaultBuilder(args) 13 | .UseStartup() 14 | .UseKestrel() 15 | .UseContentRoot(Directory.GetCurrentDirectory()) 16 | .Build(); 17 | 18 | host.Run(); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /source/OdataToEntity.AspNetCore/OePageMiddleware.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Http; 2 | using Microsoft.OData.Edm; 3 | using OdataToEntity.Query; 4 | 5 | namespace OdataToEntity.AspNetCore 6 | { 7 | public sealed class OePageMiddleware : OeMiddleware 8 | { 9 | public OePageMiddleware(RequestDelegate next, PathString apiPath, IEdmModel edmModel) 10 | : base(next, apiPath, edmModel) 11 | { 12 | } 13 | 14 | protected override OeModelBoundProvider? GetModelBoundProvider(HttpContext httpContext) 15 | { 16 | return httpContext.CreateModelBoundProvider(base.EdmModel); 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /source/OdataToEntity/Parsers/Cache/UriCompare/OeComparerExtension.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.OData.Edm; 2 | 3 | namespace OdataToEntity.Cache.UriCompare 4 | { 5 | public static class OeComparerExtension 6 | { 7 | public static bool IsEqual(this IEdmTypeReference @this, IEdmTypeReference edmTypeReference) 8 | { 9 | if (@this == edmTypeReference) 10 | return true; 11 | if (@this == null || edmTypeReference == null) 12 | return false; 13 | 14 | return @this.Definition == edmTypeReference.Definition && @this.IsNullable == edmTypeReference.IsNullable; 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /test/OdataToEntity.Test.Asp/OdataToEntity.Test.AspMvcServer/Controllers/MetadataController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using OdataToEntity.AspNetCore; 3 | using System.Threading.Tasks; 4 | 5 | namespace OdataToEntity.Test.AspMvcServer.Controllers 6 | { 7 | public class MetadataController : OeMetadataController 8 | { 9 | [Route("api/$metadata")] 10 | public async Task GetMetadata() 11 | { 12 | await base.WriteMetadataAsync().ConfigureAwait(false); 13 | } 14 | [Route("api/$json-schema")] 15 | public void GetJson() 16 | { 17 | base.WriteJsonSchema(); 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /test/OdataToEntity.Test.Asp/OdataToEntity.Test.AspServer/Program.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore; 2 | using Microsoft.AspNetCore.Builder; 3 | using Microsoft.AspNetCore.Hosting; 4 | using System; 5 | using System.IO; 6 | 7 | namespace OdataToEntity.AspServer 8 | { 9 | public static class Program 10 | { 11 | public static void Main(String[] args) 12 | { 13 | var host = WebHost.CreateDefaultBuilder(args) 14 | .UseStartup() 15 | .UseKestrel() 16 | .UseContentRoot(Directory.GetCurrentDirectory()) 17 | .Build(); 18 | 19 | host.Run(); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /source/OdataToEntity.Ef6/OeEf6DataAdapterExtension.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.OData.Edm; 2 | using OdataToEntity.ModelBuilder; 3 | using System.Data.Entity; 4 | 5 | namespace OdataToEntity.Ef6 6 | { 7 | public static class OeEf6DataAdapterExtension 8 | { 9 | public static EdmModel BuildEdmModelFromEf6Model(this Db.OeDataAdapter dataAdapter) 10 | { 11 | using (var context = (DbContext)dataAdapter.CreateDataContext()) 12 | { 13 | var modelBuilder = new OeEdmModelBuilder(dataAdapter, new OeEf6EdmModelMetadataProvider(context)); 14 | return modelBuilder.BuildEdmModel(); 15 | } 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /test/OdataToEntity.Test.Linq2Db/Program.cs: -------------------------------------------------------------------------------- 1 | extern alias lq2db; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using Xunit; 8 | using OdataToEntityDB = lq2db::OdataToEntity.Test.Model.OdataToEntityDB; 9 | 10 | [assembly: CollectionBehavior(DisableTestParallelization = true)] 11 | 12 | namespace OdataToEntity.Test.Linq2Db 13 | { 14 | class Program 15 | { 16 | static async Task Main() 17 | { 18 | //EfCore.SqlServer.PerformanceCacheTest.RunTest(100); 19 | await new RDBNull(new RDBNull_DbFixtureInitDb()).ReferencedModels(); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /source/OdataToEntity/Parsers/Cache/OeQueryCacheItem.cs: -------------------------------------------------------------------------------- 1 | using OdataToEntity.Parsers; 2 | using System; 3 | using System.Linq.Expressions; 4 | 5 | namespace OdataToEntity.Cache 6 | { 7 | public sealed class OeQueryCacheItem 8 | { 9 | public OeQueryCacheItem(Object query, MethodCallExpression? countExpression, OeEntryFactory? entryFactory) 10 | { 11 | Query = query; 12 | CountExpression = countExpression; 13 | EntryFactory = entryFactory; 14 | } 15 | 16 | public MethodCallExpression? CountExpression { get; } 17 | public OeEntryFactory? EntryFactory { get; } 18 | public Object Query { get; } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /test/OdataToEntity.Test.GraphQL/StarWars/StarWarsDataAdapter.cs: -------------------------------------------------------------------------------- 1 | using OdataToEntity.EfCore; 2 | using System; 3 | 4 | namespace OdataToEntity.Test.GraphQL.StarWars 5 | { 6 | public sealed class StarWarsDataAdapter : OeEfCoreDataAdapter 7 | { 8 | private readonly String _databaseName; 9 | 10 | public StarWarsDataAdapter(bool allowCache, String databaseName) : 11 | base(new Cache.OeQueryCache(allowCache)) 12 | { 13 | _databaseName = databaseName; 14 | } 15 | 16 | public override Object CreateDataContext() 17 | { 18 | return new StarWarsContext(_databaseName); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /test/OdataToEntity.Test.GraphQL/StarWarsFixture.cs: -------------------------------------------------------------------------------- 1 | namespace OdataToEntity.Test.GraphQL 2 | { 3 | public sealed class StarWarsFixture : DbFixture 4 | { 5 | public StarWarsFixture() : 6 | base(new Model.Order2DataAdapter(false, "Order2"), new StarWars.StarWarsDataAdapter(false, "StarWars")) 7 | { 8 | using (var order2Context = new Model.Order2Context(StarWars.StarWarsContext.Create("Order2"))) 9 | order2Context.Database.EnsureCreated(); 10 | 11 | using (var starWarsContext = new StarWars.StarWarsContext("StarWars")) 12 | starWarsContext.Database.EnsureCreated(); 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | dist: xenial 2 | addons: 3 | snaps: 4 | - name: dotnet-sdk 5 | confinement: classic 6 | channel: latest/stable 7 | sudo: required 8 | language: csharp 9 | mono: none 10 | dotnet: 5.0 11 | services: 12 | - postgresql 13 | before_script: 14 | - psql -c 'create database "OdataToEntity";' -U postgres 15 | - psql -d OdataToEntity -f ./test/sql_scripts/postgresql.sql -U postgres 16 | 17 | - sudo snap alias dotnet-sdk.dotnet dotnet 18 | - dotnet restore ./test/OdataToEntity.Test.EfCore.PostgreSql 19 | script: 20 | - dotnet test ./test/OdataToEntity.Test.EfCore.PostgreSql/OdataToEntity.Test.EfCore.PostgreSql.csproj -c Release 21 | global: 22 | - DOTNET_SKIP_FIRST_TIME_EXPERIENCE=1 23 | - DOTNET_CLI_TELEMETRY_OPTOUT=1 -------------------------------------------------------------------------------- /source/OdataToEntity.Ef6/OeEf6EnumerableToQuerableVisitor.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq.Expressions; 3 | 4 | namespace OdataToEntity.Ef6 5 | { 6 | internal sealed class OeEf6EnumerableToQuerableVisitor : Parsers.OeEnumerableToQuerableVisitor 7 | { 8 | protected override Expression VisitMethodCall(MethodCallExpression node) 9 | { 10 | if (node.Method.Name == "GetValueOrDefault") 11 | { 12 | Type underlyingType = Nullable.GetUnderlyingType(node.Object.Type); 13 | if (underlyingType != null) 14 | return Expression.Property(node.Object, "Value"); 15 | } 16 | 17 | return base.VisitMethodCall(node); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /source/OdataToEntity/Infrastructure/AllowNullAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace System.Diagnostics.CodeAnalysis 4 | { 5 | #if !NET5_0 6 | [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, Inherited = false)] 7 | internal sealed class AllowNullAttribute : Attribute 8 | { 9 | } 10 | 11 | [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, Inherited = false)] 12 | internal sealed class MaybeNullAttribute : Attribute 13 | { 14 | } 15 | [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, Inherited = false)] 16 | internal sealed class NotNullAttribute : Attribute 17 | { 18 | } 19 | #endif 20 | } 21 | -------------------------------------------------------------------------------- /test/OdataToEntity.Test.GraphQL/OeGraphQLHelper.cs: -------------------------------------------------------------------------------- 1 | using GraphQL; 2 | using GraphQL.NewtonsoftJson; 3 | using System; 4 | using System.IO; 5 | using System.Threading.Tasks; 6 | 7 | namespace OdataToEntity.Test.GraphQL 8 | { 9 | public static class OeGraphQLHelper 10 | { 11 | public static async Task ToStringAsync(this ExecutionResult executionResult) 12 | { 13 | using (var stream = new MemoryStream()) 14 | { 15 | await new DocumentWriter(true).WriteAsync(stream, executionResult).ConfigureAwait(false); 16 | stream.Position = 0; 17 | using (var reader = new StreamReader(stream)) 18 | return reader.ReadToEnd(); 19 | } 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /source/OdataToEntity/ModelBuilder/ManyToManyJoinDescription.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.OData.Edm; 2 | using System; 3 | 4 | namespace OdataToEntity.ModelBuilder 5 | { 6 | public sealed class ManyToManyJoinDescription 7 | { 8 | public ManyToManyJoinDescription(Type joinClassType, IEdmNavigationProperty joinNavigationProperty, IEdmNavigationProperty targetNavigationProperty) 9 | { 10 | JoinClassType = joinClassType; 11 | JoinNavigationProperty = joinNavigationProperty; 12 | TargetNavigationProperty = targetNavigationProperty; 13 | } 14 | 15 | public Type JoinClassType { get; } 16 | public IEdmNavigationProperty JoinNavigationProperty { get; } 17 | public IEdmNavigationProperty TargetNavigationProperty { get; } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /test/OdataToEntity.Test/Common/QueryParameters.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Linq.Expressions; 4 | 5 | namespace OdataToEntity.Test 6 | { 7 | public class QueryParameters 8 | { 9 | public String RequestUri { get; set; } 10 | public Expression, IQueryable>> Expression { get; set; } 11 | public bool NavigationNextLink { get; set; } 12 | public int PageSize { get; set; } 13 | } 14 | 15 | public sealed class QueryParameters : QueryParameters 16 | { 17 | } 18 | 19 | public sealed class QueryParametersScalar 20 | { 21 | public String RequestUri { get; set; } 22 | public Expression, TResult>> Expression { get; set; } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /source/OdataToEntity.AspNetCore/OeHttpRequestHeaders.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Http; 2 | using System; 3 | 4 | namespace OdataToEntity.AspNetCore 5 | { 6 | public sealed class OeHttpRequestHeaders : OeRequestHeaders 7 | { 8 | private readonly HttpResponse _response; 9 | 10 | public OeHttpRequestHeaders(OeRequestHeaders headers, HttpResponse response) : base(headers) 11 | { 12 | _response = response; 13 | _response.ContentType = base.ContentType; 14 | } 15 | 16 | protected override OeRequestHeaders Clone() => new OeHttpRequestHeaders(this, _response); 17 | 18 | public override String? ResponseContentType 19 | { 20 | get => _response.ContentType; 21 | set => _response.ContentType = value!; 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /source/OdataToEntity.EfCore.DynamicDataContext/ModelBuilder/DynamicPropertyInfo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel.DataAnnotations.Schema; 3 | 4 | namespace OdataToEntity.EfCore.DynamicDataContext.ModelBuilder 5 | { 6 | public readonly struct DynamicPropertyInfo 7 | { 8 | public DynamicPropertyInfo(String name, Type type, bool isNullable, DatabaseGeneratedOption databaseGeneratedOption) 9 | { 10 | IsNullable = isNullable; 11 | Name = name; 12 | Type = type; 13 | DatabaseGeneratedOption = databaseGeneratedOption; 14 | } 15 | 16 | public bool IsNullable { get; } 17 | public String Name { get; } 18 | public Type Type { get; } 19 | public DatabaseGeneratedOption DatabaseGeneratedOption { get; } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /test/OdataToEntity.Test.Asp/OdataToEntity.Test.AspMvcServer/Controllers/BatchController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using Microsoft.OData; 3 | using OdataToEntity.AspNetCore; 4 | using System; 5 | using System.Threading.Tasks; 6 | 7 | namespace OdataToEntity.Test.AspMvcServer.Controllers 8 | { 9 | [Route("api/$batch")] 10 | public sealed class BatchController : OeBatchController 11 | { 12 | protected override Task BatchCore() 13 | { 14 | return base.BatchCore(); 15 | } 16 | protected override void OnBeforeInvokeController(OeDataContext dataContext, ODataResource entry) 17 | { 18 | } 19 | protected override Task SaveChangesAsync(Object dataContext) 20 | { 21 | return base.SaveChangesAsync(dataContext); 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /test/OdataToEntity.Test.Wcf/OdataToEntity.Test.WcfClient/OrderContextOptions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | 3 | namespace OdataToEntity.Test.Model 4 | { 5 | internal static class OrderContextOptions 6 | { 7 | public static DbContextOptions Create(bool useRelationalNulls) 8 | { 9 | return Create(useRelationalNulls); 10 | } 11 | public static DbContextOptions Create(bool useRelationalNulls) where T : DbContext 12 | { 13 | var optionsBuilder = new DbContextOptionsBuilder(); 14 | optionsBuilder = optionsBuilder.UseSqlServer(@"Server=.\sqlexpress;Initial Catalog=OdataToEntity;Trusted_Connection=Yes;", opt => opt.UseRelationalNulls(useRelationalNulls)); 15 | return optionsBuilder.Options; 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /source/OdataToEntity/Parsers/Translators/OeNextLinkSelectItem.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.OData.UriParser; 2 | using System; 3 | 4 | namespace OdataToEntity.Parsers.Translators 5 | { 6 | public sealed class OeNextLinkSelectItem : SelectItem 7 | { 8 | public static readonly OeNextLinkSelectItem Instance = new OeNextLinkSelectItem(true); 9 | 10 | private OeNextLinkSelectItem(bool nextLink) 11 | { 12 | NextLink = nextLink; 13 | } 14 | 15 | public override void HandleWith(SelectItemHandler handler) 16 | { 17 | throw new NotImplementedException(); 18 | } 19 | public override T TranslateWith(SelectItemTranslator translator) 20 | { 21 | throw new NotImplementedException(); 22 | } 23 | 24 | public bool NextLink { get; } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /test/OdataToEntity.Test.Wcf/OdataToEntity.Test.WcfService/OrderService.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.OData.Edm; 2 | 3 | namespace OdataToEntity.Test.WcfService 4 | { 5 | public sealed class OrderServiceBehaviorAttribute : OdataWcfServiceBehaviorAttribute 6 | { 7 | public OrderServiceBehaviorAttribute() : base(typeof(Model.OrderDataAdapter)) 8 | { 9 | } 10 | 11 | protected override OdataWcfService CreateOdataWcfService(Db.OeDataAdapter dataAdapter, IEdmModel edmModel) 12 | { 13 | return new OrderService(dataAdapter, edmModel); 14 | } 15 | } 16 | 17 | [OrderServiceBehavior] 18 | public sealed class OrderService : OdataWcfService 19 | { 20 | public OrderService(Db.OeDataAdapter dataAdapter, IEdmModel edmModel) : base(dataAdapter, edmModel) 21 | { 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /source/OdataToEntity/Parsers/Translators/OePageSelectItem.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.OData.UriParser; 2 | using System; 3 | 4 | namespace OdataToEntity.Parsers.Translators 5 | { 6 | public sealed class OePageSelectItem : SelectItem 7 | { 8 | public OePageSelectItem(int pageSize) 9 | { 10 | if (pageSize <= 0) 11 | throw new ArgumentException("Must be greater zero", nameof(pageSize)); 12 | 13 | PageSize = pageSize; 14 | } 15 | 16 | public override void HandleWith(SelectItemHandler handler) 17 | { 18 | throw new NotImplementedException(); 19 | } 20 | public override T TranslateWith(SelectItemTranslator translator) 21 | { 22 | throw new NotImplementedException(); 23 | } 24 | 25 | public int PageSize { get; } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /source/OdataToEntity.EfCore/OeEfCoreDataAdapterExtension.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | using Microsoft.OData.Edm; 3 | using OdataToEntity.ModelBuilder; 4 | 5 | namespace OdataToEntity.EfCore 6 | { 7 | public static class OeEfCoreDataAdapterExtension 8 | { 9 | public static EdmModel BuildEdmModelFromEfCoreModel(this Db.OeDataAdapter dataAdapter, params IEdmModel[] refModels) 10 | { 11 | var context = (DbContext)dataAdapter.CreateDataContext(); 12 | try 13 | { 14 | var modelBuilder = new OeEdmModelBuilder(dataAdapter, new OeEfCoreEdmModelMetadataProvider(context.Model)); 15 | return modelBuilder.BuildEdmModel(refModels); 16 | } 17 | finally 18 | { 19 | dataAdapter.CloseDataContext(context); 20 | } 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /test/OdataToEntity.Test.Wcf/OdataToEntity.Test.WcfService/IOdataWcf.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Threading.Tasks; 4 | 5 | #if WCF_SERVICE 6 | using CoreWCF; 7 | #else 8 | using System.ServiceModel; 9 | #endif 10 | 11 | namespace OdataToEntity.Test.WcfService 12 | { 13 | [ServiceContract] 14 | public interface IOdataWcf 15 | { 16 | [OperationContract] 17 | Task Get(OdataWcfQuery request); 18 | [OperationContract] 19 | Task Post(OdataWcfQuery request); 20 | } 21 | 22 | [MessageContract] 23 | public sealed class OdataWcfQuery 24 | { 25 | [MessageHeader] 26 | public String ContentType { get; set; } 27 | [MessageHeader] 28 | public String Prefer { get; set; } 29 | [MessageBodyMember] 30 | public Stream Content { get; set; } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /test/OdataToEntity.Test.DynamicDataContext.AspServer/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Debug", 5 | "System": "Information", 6 | "Microsoft": "Information" 7 | } 8 | }, 9 | "Kestrel": { 10 | "EndpointDefaults": { 11 | "Protocols": "Http1AndHttp2" 12 | }, 13 | "Endpoints": { 14 | "Http": { 15 | "Url": "http://localhost:5000" 16 | } 17 | } 18 | }, 19 | "OdataToEntity": { 20 | "BasePath": "api", 21 | "Provider": "sqlserver", 22 | "ConnectionString": "Server=.\\sqlexpress;Initial Catalog=OdataToEntity;Trusted_Connection=Yes;", 23 | "UseRelationalNulls": true, 24 | "DefaultSchema": "dbo", 25 | "IncludedSchemas": [ "dbo", "test" ], 26 | "ExcludedSchemas": null, 27 | "Filter": "mapping", 28 | "InformationSchemaMappingFileName": "InformationSchemaMapping.json" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /test/OdataToEntity.Test.Asp/OdataToEntity.Test.AspClient/Connected Services/ODataClient/ConnectedService.json: -------------------------------------------------------------------------------- 1 | { 2 | "ProviderId": "Microsoft.OData.ConnectedService", 3 | "Version": "0.2.0", 4 | "GettingStartedDocument": { 5 | "Uri": "http://odata.github.io/odata.net/" 6 | }, 7 | "ExtendedData": { 8 | "EnableNamingAlias": false, 9 | "IgnoreUnexpectedElementsAndAttributes": false, 10 | "IncludeT4File": false, 11 | "ServiceName": "ODataClient", 12 | "Endpoint": "http://localhost:5000/api/$metadata", 13 | "EdmxVersion": { 14 | "Major": 4, 15 | "Minor": 0, 16 | "Build": 0, 17 | "Revision": 0, 18 | "MajorRevision": 0, 19 | "MinorRevision": 0 20 | }, 21 | "GeneratedFileNamePrefix": "ODataClient", 22 | "UseNameSpacePrefix": true, 23 | "NamespacePrefix": "ODataClient.OdataToEntity.Test.Model", 24 | "UseDataServiceCollection": false 25 | } 26 | } -------------------------------------------------------------------------------- /source/OdataToEntity.GraphQL/GraphqlExtension.cs: -------------------------------------------------------------------------------- 1 | using GraphQLParser.AST; 2 | using System; 3 | using System.Collections.Generic; 4 | 5 | namespace OdataToEntity.GraphQL 6 | { 7 | internal static class GraphqlExtension 8 | { 9 | public static String GetName(this INamedNode namedNode) 10 | { 11 | return (namedNode.Name ?? throw new InvalidOperationException("Name is null")).Value.ToString(); 12 | } 13 | public static List GetSelections(this GraphQLSelectionSet selectionSet) 14 | { 15 | return selectionSet.Selections ?? throw new InvalidOperationException("Selections is null"); 16 | } 17 | public static GraphQLSelectionSet GetSelectionSet(GraphQLFieldSelection fieldSelection) 18 | { 19 | return fieldSelection.SelectionSet ?? throw new InvalidOperationException("SelectionSet is null"); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /test/OdataToEntity.Test/Common/EfInclude.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Reflection; 4 | 5 | namespace OdataToEntity.Test 6 | { 7 | public readonly struct EfInclude 8 | { 9 | public EfInclude(PropertyInfo property, Func filter, bool isOrdered) : this(property, filter, isOrdered, null) 10 | { 11 | } 12 | public EfInclude(PropertyInfo property, Func filter, bool isOrdered, PropertyInfo parentProperty) 13 | { 14 | Property = property; 15 | Filter = filter; 16 | ParentProperty = parentProperty; 17 | IsOrdered = isOrdered; 18 | } 19 | 20 | public Func Filter { get; } 21 | public bool IsOrdered { get; } 22 | public PropertyInfo ParentProperty { get; } 23 | public PropertyInfo Property { get; } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /source/Directory.Build.targets: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | true 5 | 6 | true 7 | 8 | $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb 9 | 10 | LICENSE 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /dependencies.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5.0.0 4 | 5.0.2 5 | 9.0 6 | 5.0.8 7 | net48 8 | 13.0.1 9 | net5.0 10 | net5.0 11 | 5.0.0 12 | 5.0.7 13 | 7.9.0 14 | 2.8.0 15 | 5.0.0 16 | 16.10.0 17 | $(OdataToEntityVersion) 18 | 2.4.1 19 | 2.4.3 20 | 21 | -------------------------------------------------------------------------------- /source/OdataToEntity/Infrastructure/FastActivator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq.Expressions; 3 | using System.Reflection; 4 | 5 | namespace OdataToEntity.Infrastructure 6 | { 7 | public static class FastActivator 8 | { 9 | public static T CreateInstance() => FastActivatorImpl.Factory(); 10 | 11 | private static class FastActivatorImpl 12 | { 13 | public static readonly Func Factory = GetFactory(); 14 | 15 | private static Func GetFactory() 16 | { 17 | ConstructorInfo? ctor = typeof(T).GetConstructor(Type.EmptyTypes); 18 | if (ctor == null) 19 | throw new InvalidOperationException(typeof(T).Name + " must have default constructor or override OeDataAdapter.CreateDataContext()"); 20 | 21 | return (Func)Expression.Lambda(Expression.New(ctor)).Compile(); 22 | } 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /test/OdataToEntity.Test/OrderDataAdapter.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.OData.Edm; 2 | using System.Linq.Expressions; 3 | 4 | namespace OdataToEntity.Test.Model 5 | { 6 | public sealed class OrderDataAdapter : EfCore.OeEfCoreDataAdapter 7 | { 8 | public OrderDataAdapter() : 9 | base(OrderContextOptions.Create(OrderContext.GenerateDatabaseName()), new Cache.OeQueryCache(false)) 10 | { 11 | } 12 | 13 | protected override Expression TranslateExpression(IEdmModel edmModel, Expression expression) 14 | { 15 | return new SQLiteVisitor().Visit(expression); 16 | } 17 | } 18 | 19 | public sealed class Order2DataAdapter : EfCore.OeEfCoreDataAdapter 20 | { 21 | public Order2DataAdapter() : 22 | base(OrderContextOptions.Create(OrderContext.GenerateDatabaseName()), new Cache.OeQueryCache(false)) 23 | { 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /test/OdataToEntity.Test/Program.cs: -------------------------------------------------------------------------------- 1 | using OdataToEntity.Test.Model; 2 | using System; 3 | using System.Linq; 4 | using System.Linq.Expressions; 5 | using System.Threading.Tasks; 6 | using Xunit; 7 | 8 | [assembly: CollectionBehavior(DisableTestParallelization = true)] 9 | 10 | namespace OdataToEntity.Test 11 | { 12 | class Program 13 | { 14 | static async Task Main() 15 | { 16 | //await new PLNull(new PLNull_DbFixtureInitDb()).FilterIn(0); 17 | var fixture = new PLNull_DbFixtureInitDb(); 18 | var ctx = fixture.CreateContext(); 19 | //ctx.Orders.AsQueryable().Where(o => o.Date.Value.Year == 2016 && o.Date.Value.Month > 3 && o.Date.Value.Day < 20).ToArray(); 20 | var d = DateTimeOffset.Now; 21 | //ctx.Categories.AsQueryable().Where(o => o.DateTime.Value.Year == 2016).ToArray(); 22 | ctx.Orders.AsQueryable().Where(o => o.Date == DateTimeOffset.Now).ToArray(); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /test/OdataToEntity.Test.Linq2DbModel/OdataToEntity.Test.Linq2DbModel.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Test Linq2Db Sql Server for OdataToEntity 6 | Test Linq2Db Sql Server for OdataToEntity 7 | Maxim Voronov 8 | $(NetStandardVersion) 9 | AnyCPU 10 | portable 11 | OdataToEntity.Test.Linq2DbModel 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /source/OdataToEntity/Parsers/Visitors/ReplaceParameterVisitor.cs: -------------------------------------------------------------------------------- 1 | using System.Linq.Expressions; 2 | 3 | namespace OdataToEntity.Parsers 4 | { 5 | public sealed class ReplaceParameterVisitor : ExpressionVisitor 6 | { 7 | private readonly ParameterExpression? _parameter; 8 | private readonly Expression _source; 9 | 10 | public ReplaceParameterVisitor(Expression source) 11 | { 12 | _source = source; 13 | } 14 | public ReplaceParameterVisitor(Expression source, ParameterExpression parameter) 15 | { 16 | _source = source; 17 | _parameter = parameter; 18 | } 19 | 20 | protected override Expression VisitParameter(ParameterExpression node) 21 | { 22 | if (_parameter == null) 23 | return node.Type == _source.Type ? _source : base.VisitParameter(node); 24 | else 25 | return node == _parameter ? _source : base.VisitParameter(node); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | [*.cs] 2 | 3 | # IDE0056: Use index operator 4 | csharp_style_prefer_index_operator = true:silent 5 | 6 | # IDE0054: Use compound assignment 7 | dotnet_style_prefer_compound_assignment = true:silent 8 | 9 | # IDE0057: Use range operator 10 | csharp_style_prefer_range_operator = true:silent 11 | 12 | # CA1012: Abstract types should not have constructors 13 | dotnet_diagnostic.CA1012.severity = silent 14 | 15 | # IDE0063: Use simple 'using' statement 16 | csharp_prefer_simple_using_statement = true:silent 17 | 18 | # CA1010: Generic interface should also be implemented 19 | dotnet_diagnostic.CA1010.severity = silent 20 | 21 | # CA1062: Validate arguments of public methods 22 | dotnet_diagnostic.CA1062.severity = silent 23 | 24 | # CA1063: Implement IDisposable correctly 25 | dotnet_diagnostic.CA1063.severity = silent 26 | 27 | # CA1815: Properties should not return arrays 28 | dotnet_diagnostic.CA1815.severity = silent 29 | 30 | # CA1819: Properties should not return arrays 31 | dotnet_diagnostic.CA1819.severity = silent 32 | 33 | 34 | -------------------------------------------------------------------------------- /source/OdataToEntity/Query/Builder/OeModelBoundFluentBuilder.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.OData.Edm; 2 | using System; 3 | 4 | namespace OdataToEntity.Query.Builder 5 | { 6 | public sealed class OeModelBoundFluentBuilder 7 | { 8 | public OeModelBoundFluentBuilder(IEdmModel edmModel) 9 | { 10 | EdmModel = edmModel; 11 | ModelBoundSettingsBuilder = new OeModelBoundSettingsBuilder(); 12 | } 13 | 14 | public EntitySetConfiguration EntitySet(String entitySetName) where TEntityType : class 15 | { 16 | IEdmEntitySet entitySet = OeEdmClrHelper.GetEntitySet(EdmModel, entitySetName); 17 | return new EntitySetConfiguration(this, entitySet); 18 | } 19 | public OeModelBoundProvider BuildProvider() 20 | { 21 | return ModelBoundSettingsBuilder.Build(); 22 | } 23 | 24 | public IEdmModel EdmModel { get; } 25 | public OeModelBoundSettingsBuilder ModelBoundSettingsBuilder { get; } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /source/OdataToEntity.AspNetCore/ODataPrimitiveResult.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using Microsoft.OData; 3 | using Microsoft.OData.Edm; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Threading.Tasks; 7 | 8 | namespace OdataToEntity.AspNetCore 9 | { 10 | public sealed class ODataPrimitiveResult : ODataResult 11 | { 12 | private readonly IEdmModel _edmModel; 13 | private readonly IAsyncEnumerator _items; 14 | private readonly ODataUri _odataUri; 15 | 16 | public ODataPrimitiveResult(IEdmModel edmModel, ODataUri odataUri, IAsyncEnumerator items) 17 | { 18 | _edmModel = edmModel; 19 | _odataUri = odataUri; 20 | _items = items; 21 | } 22 | 23 | public override async Task ExecuteResultAsync(ActionContext context) 24 | { 25 | await Parsers.OePostParser.WriteCollectionAsync(_edmModel, _odataUri, (IAsyncEnumerator)_items, context.HttpContext.Response.Body).ConfigureAwait(false); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /source/OdataToEntity/InMemory/InMemorySourceVisitor.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.OData.Edm; 2 | using OdataToEntity.Parsers; 3 | using System; 4 | using System.Linq.Expressions; 5 | 6 | namespace OdataToEntity.InMemory 7 | { 8 | internal sealed class InMemorySourceVisitor : ExpressionVisitor 9 | { 10 | private readonly IEdmModel _edmModel; 11 | private readonly Object?[] _parameters; 12 | 13 | public InMemorySourceVisitor(IEdmModel edmModel, Object?[] parameters) 14 | { 15 | _edmModel = edmModel; 16 | _parameters = parameters; 17 | } 18 | 19 | protected override Expression VisitConstant(ConstantExpression node) 20 | { 21 | if (node.Value is OeEnumerableStub enumerableStub) 22 | { 23 | var entitySetAdapter = (InMemoryEntitySetAdapter)_edmModel.GetEntitySetAdapter(enumerableStub.EntitySet); 24 | return entitySetAdapter.GetEntitySet(_parameters).Expression; 25 | } 26 | 27 | return node; 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /source/OdataToEntity/Parsers/Cache/OeCacheContextEqualityComparer.cs: -------------------------------------------------------------------------------- 1 | using OdataToEntity.Cache.UriCompare; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Diagnostics.CodeAnalysis; 5 | 6 | namespace OdataToEntity.Cache 7 | { 8 | public sealed class OeCacheContextEqualityComparer : IEqualityComparer 9 | { 10 | public bool Equals([AllowNull] OeCacheContext x, [AllowNull] OeCacheContext y) 11 | { 12 | if (Object.ReferenceEquals(x, y)) 13 | return true; 14 | 15 | if (x == null || y == null) 16 | return false; 17 | 18 | var comparer = new OeCacheComparer(x.ConstantToParameterMapper); 19 | if (comparer.Compare(x, y)) 20 | { 21 | y.ParameterValues = comparer.ParameterValues; 22 | return true; 23 | } 24 | 25 | return false; 26 | } 27 | public int GetHashCode(OeCacheContext obj) 28 | { 29 | return OeCacheComparer.GetCacheCode(obj); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /source/OdataToEntity.EfCore.DynamicDataContext/InformationSchema/SqlServerModelCustomizer.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | using Microsoft.EntityFrameworkCore.Infrastructure; 3 | using System; 4 | using System.Linq.Expressions; 5 | 6 | namespace OdataToEntity.EfCore.DynamicDataContext.InformationSchema 7 | { 8 | public sealed class SqlServerModelCustomizer : IModelCustomizer 9 | { 10 | private readonly ModelCustomizer _modelCustomizer; 11 | 12 | public SqlServerModelCustomizer(ModelCustomizerDependencies dependencies) 13 | { 14 | _modelCustomizer = new ModelCustomizer(dependencies); 15 | } 16 | 17 | public void Customize(Microsoft.EntityFrameworkCore.ModelBuilder modelBuilder, DbContext context) 18 | { 19 | _modelCustomizer.Customize(modelBuilder, context); 20 | 21 | Expression> parameterFilter = t => t.DataType != "table type" && t.ParameterName != ""; 22 | modelBuilder.Model.FindEntityType(typeof(Parameter)).SetQueryFilter(parameterFilter); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /source/OdataToEntity.EfCore.DynamicDataContext/DynamicDbContext.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | using OdataToEntity.EfCore.DynamicDataContext.ModelBuilder; 3 | using System; 4 | 5 | namespace OdataToEntity.EfCore.DynamicDataContext 6 | { 7 | public abstract class DynamicDbContext : DbContext 8 | { 9 | private readonly DynamicModelBuilder? _dynamicModelBuilder; 10 | 11 | protected DynamicDbContext(DbContextOptions options) : base(options) 12 | { 13 | } 14 | public DynamicDbContext(DbContextOptions options, in DynamicModelBuilder dynamicModelBuilder) : base(options) 15 | { 16 | _dynamicModelBuilder = dynamicModelBuilder; 17 | } 18 | 19 | protected override void OnModelCreating(Microsoft.EntityFrameworkCore.ModelBuilder modelBuilder) 20 | { 21 | if (_dynamicModelBuilder == null) 22 | throw new InvalidOperationException(nameof(DynamicDbContext) + " must be create via " + nameof(DynamicTypeDefinitionManager)); 23 | 24 | _dynamicModelBuilder.Build(modelBuilder); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Maxim Voronov 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /test/OdataToEntity.Test.Asp/OdataToEntity.Test.AspMvcServer/Controllers/ManyColumnsViewController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Http; 2 | using Microsoft.AspNetCore.Mvc; 3 | using Microsoft.OData.Edm; 4 | using OdataToEntity.AspNetCore; 5 | using System.Threading.Tasks; 6 | 7 | namespace OdataToEntity.Test.AspMvcServer.Controllers 8 | { 9 | [Route("api/[controller]")] 10 | public class ManyColumnsViewController 11 | { 12 | private readonly IHttpContextAccessor _httpContextAccessor; 13 | 14 | public ManyColumnsViewController(IHttpContextAccessor httpContextAccessor) 15 | { 16 | _httpContextAccessor = httpContextAccessor; 17 | } 18 | 19 | [HttpGet] 20 | public async Task Get() 21 | { 22 | var edmModel = (IEdmModel)_httpContextAccessor.HttpContext.RequestServices.GetService(typeof(IEdmModel)); 23 | Query.OeModelBoundProvider modelBoundProvider = OeAspHelper.CreateModelBoundProvider(edmModel, 10, false); 24 | await OeAspQueryParser.Get(_httpContextAccessor.HttpContext, modelBoundProvider).ConfigureAwait(false); 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /source/OdataToEntity/InMemory/InMemoryScalarExecutor.cs: -------------------------------------------------------------------------------- 1 | using OdataToEntity.Cache; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | 6 | namespace OdataToEntity.InMemory 7 | { 8 | public sealed class InMemoryScalarExecutor 9 | { 10 | private readonly String[] _parameterNames; 11 | private readonly Object?[] _parameters; 12 | private readonly Func _query; 13 | 14 | public InMemoryScalarExecutor(Func query, IReadOnlyList parameterValues, Object?[] parameters) 15 | { 16 | _query = query; 17 | _parameterNames = parameterValues.Select(p => p.ParameterName).ToArray(); 18 | _parameters = parameters; 19 | } 20 | 21 | public TResult Execute() 22 | { 23 | return _query(); 24 | } 25 | 26 | public Object? this[String parameterName] 27 | { 28 | get => _parameters[Array.IndexOf(_parameterNames, parameterName)]; 29 | set => _parameters[Array.IndexOf(_parameterNames, parameterName)] = value; 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /test/OdataToEntity.Test.EfCore.PostgreSql/OrderDataAdapter.cs: -------------------------------------------------------------------------------- 1 | using OdataToEntity.EfCore; 2 | using OdataToEntity.EfCore.Postgresql; 3 | 4 | namespace OdataToEntity.Test.Model 5 | { 6 | public sealed class OrderDataAdapter : EfCorePostgreSqlDataAdapter 7 | { 8 | public OrderDataAdapter(bool allowCache, bool useRelationalNulls) : 9 | base(OrderContextOptions.Create(useRelationalNulls), new Cache.OeQueryCache(allowCache)) 10 | { 11 | } 12 | 13 | public static ModelBuilder.OeEdmModelMetadataProvider CreateMetadataProvider() 14 | { 15 | using (var dbContext = new OrderContext(OrderContextOptions.Create(true))) 16 | return new OeEfCoreEdmModelMetadataProvider(dbContext.Model); 17 | } 18 | } 19 | 20 | public sealed class Order2DataAdapter : EfCorePostgreSqlDataAdapter 21 | { 22 | public Order2DataAdapter(bool allowCache, bool useRelationalNulls) : 23 | base(OrderContextOptions.Create(useRelationalNulls), new Cache.OeQueryCache(allowCache)) 24 | { 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /source/OdataToEntity.EfCore.DynamicDataContext/ModelBuilder/DynamicDependentPropertyInfo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace OdataToEntity.EfCore.DynamicDataContext.ModelBuilder 5 | { 6 | public readonly struct DynamicDependentPropertyInfo 7 | { 8 | public DynamicDependentPropertyInfo(in TableFullName principalTableName, in TableFullName dependentTableName, 9 | IReadOnlyList principalPropertyNames, IReadOnlyList dependentPropertyNames, bool isCollection) 10 | { 11 | PrincipalTableName = principalTableName; 12 | DependentTableName = dependentTableName; 13 | PrincipalPropertyNames = principalPropertyNames; 14 | DependentPropertyNames = dependentPropertyNames; 15 | IsCollection = isCollection; 16 | } 17 | 18 | public TableFullName DependentTableName { get; } 19 | public IReadOnlyList DependentPropertyNames { get; } 20 | public bool IsCollection { get; } 21 | public TableFullName PrincipalTableName { get; } 22 | public IReadOnlyList PrincipalPropertyNames { get; } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /test/OdataToEntity.Test.Asp/OdataToEntity.Test.AspMvcServer/Controllers/OrderItemsViewController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Http; 2 | using Microsoft.AspNetCore.Mvc; 3 | using OdataToEntity.AspNetCore; 4 | using System.Collections.Generic; 5 | 6 | namespace OdataToEntity.Test.AspMvcServer.Controllers 7 | { 8 | [Route("api/[controller]")] 9 | public sealed class OrderItemsViewController 10 | { 11 | private readonly IHttpContextAccessor _httpContextAccessor; 12 | 13 | public OrderItemsViewController(IHttpContextAccessor httpContextAccessor) 14 | { 15 | _httpContextAccessor = httpContextAccessor; 16 | } 17 | 18 | [HttpGet] 19 | public ODataResult Get() 20 | { 21 | Query.OeModelBoundProvider modelBoundProvider = _httpContextAccessor.HttpContext.CreateModelBoundProvider(); 22 | var parser = new OeAspQueryParser(_httpContextAccessor.HttpContext, modelBoundProvider); 23 | IAsyncEnumerable orderItemsViews = parser.ExecuteReader(); 24 | return parser.OData(orderItemsViews); 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /test/OdataToEntity.Test.Asp/OdataToEntity.Test.AspClient/ODataClient.cs: -------------------------------------------------------------------------------- 1 | using OdataToEntity.Test; 2 | using System; 3 | 4 | namespace ODataClient.OdataToEntity.Test.Model 5 | { 6 | public partial class Order 7 | { 8 | public Order() 9 | { 10 | CustomerCountry = OpenTypeConverter.NotSetString; 11 | CustomerId = Int32.MinValue; 12 | Date = DateTimeOffset.MinValue; 13 | Id = Int32.MinValue; 14 | Status = (OrderStatus)Int32.MinValue; 15 | } 16 | } 17 | 18 | public abstract partial class OrderBase 19 | { 20 | public OrderBase() 21 | { 22 | AltCustomerCountry = OpenTypeConverter.NotSetString; 23 | AltCustomerId = Int32.MinValue; 24 | Name = OpenTypeConverter.NotSetString; 25 | } 26 | } 27 | 28 | public partial class OrderItem 29 | { 30 | public OrderItem() 31 | { 32 | Count = Int32.MinValue; 33 | Id = Int32.MinValue; 34 | OrderId = Int32.MinValue; 35 | Price = Decimal.MinValue; 36 | Product = OpenTypeConverter.NotSetString; 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /test/OdataToEntity.Test.EfCore.SqlServer/OrderDataAdapter.cs: -------------------------------------------------------------------------------- 1 | using OdataToEntity.EfCore; 2 | 3 | namespace OdataToEntity.Test.Model 4 | { 5 | public sealed class OrderDataAdapter : OeEfCoreSqlServerDataAdapter 6 | { 7 | public OrderDataAdapter() : this(true, true) 8 | { 9 | } 10 | public OrderDataAdapter(bool allowCache, bool useRelationalNulls) : 11 | base(OrderContextOptions.Create(useRelationalNulls), new Cache.OeQueryCache(allowCache)) 12 | { 13 | } 14 | 15 | public static ModelBuilder.OeEdmModelMetadataProvider CreateMetadataProvider() 16 | { 17 | using (var dbContext = new OrderContext(OrderContextOptions.Create(true))) 18 | return new OeEfCoreEdmModelMetadataProvider(dbContext.Model); 19 | } 20 | } 21 | 22 | public sealed class Order2DataAdapter : OeEfCoreSqlServerDataAdapter 23 | { 24 | public Order2DataAdapter(bool allowCache, bool useRelationalNulls) : 25 | base(OrderContextOptions.Create(useRelationalNulls), new Cache.OeQueryCache(allowCache)) 26 | { 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /source/OdataToEntity.EfCore/OeMySqlEfCoreOperationAdapter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace OdataToEntity.EfCore 6 | { 7 | public sealed class OeMySqlEfCoreOperationAdapter : OeEfCoreOperationAdapter 8 | { 9 | public OeMySqlEfCoreOperationAdapter(Type dataContextType) : base(dataContextType) 10 | { 11 | } 12 | public OeMySqlEfCoreOperationAdapter(Type dataContextType, bool isCaseSensitive) : base(dataContextType, isCaseSensitive) 13 | { 14 | } 15 | 16 | protected override String GetProcedureName(Object dataContext, String operationName, IReadOnlyList> parameters) 17 | { 18 | var sql = new StringBuilder("call "); 19 | sql.Append(operationName); 20 | if (parameters.Count > 0) 21 | { 22 | sql.Append('('); 23 | String[] parameterNames = GetParameterNames(dataContext, parameters); 24 | sql.Append(String.Join(",", parameterNames)); 25 | sql.Append(')'); 26 | } 27 | return sql.ToString(); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /source/OdataToEntity.EfCore/Postgresql/OeEfCorePostgreSqlDataAdapter.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | using Microsoft.OData.Edm; 3 | using System.Linq.Expressions; 4 | 5 | namespace OdataToEntity.EfCore.Postgresql 6 | { 7 | public class OeEfCorePostgreSqlDataAdapter : OeEfCoreDataAdapter where T : DbContext 8 | { 9 | public OeEfCorePostgreSqlDataAdapter() : this(null, null) 10 | { 11 | } 12 | public OeEfCorePostgreSqlDataAdapter(DbContextOptions? options, Cache.OeQueryCache? queryCache) 13 | : this(options, queryCache, new OePostgreSqlEfCoreOperationAdapter(typeof(T))) 14 | { 15 | } 16 | protected OeEfCorePostgreSqlDataAdapter(DbContextOptions? options, Cache.OeQueryCache? queryCache, OeEfCoreOperationAdapter operationAdapter) 17 | : base(options, queryCache, operationAdapter) 18 | { 19 | base.IsDatabaseNullHighestValue = true; 20 | } 21 | 22 | protected override Expression TranslateExpression(IEdmModel edmModel, Expression expression) 23 | { 24 | expression = new OeDateTimeOffsetMembersVisitor().Visit(expression); 25 | return base.TranslateExpression(edmModel, expression); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /source/OdataToEntity.EfCore/OeEfCoreDataReaderAsyncEnumerator.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore.Storage; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | 7 | namespace OdataToEntity.EfCore 8 | { 9 | public sealed class OeEfCoreDataReaderAsyncEnumerator : IAsyncEnumerable, IAsyncEnumerator 10 | { 11 | private CancellationToken _cancellationToken; 12 | private readonly RelationalDataReader _dataReader; 13 | 14 | public OeEfCoreDataReaderAsyncEnumerator(RelationalDataReader dataReader) 15 | { 16 | _dataReader = dataReader; 17 | } 18 | 19 | public ValueTask DisposeAsync() 20 | { 21 | return _dataReader.DisposeAsync(); 22 | } 23 | public IAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken) 24 | { 25 | _cancellationToken = cancellationToken; 26 | return this; 27 | } 28 | public ValueTask MoveNextAsync() 29 | { 30 | return new ValueTask(_dataReader.ReadAsync(_cancellationToken)); 31 | } 32 | 33 | public Object Current => _dataReader.DbDataReader.GetValue(0); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /source/OdataToEntity/InMemory/InMemoryQueryableWrapper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Linq.Expressions; 6 | 7 | namespace OdataToEntity.InMemory 8 | { 9 | internal sealed class InMemoryQueryableWrapper : IQueryable, IEnumerable 10 | { 11 | private readonly Object?[] _parameters; 12 | private readonly InMemoryEntitySetAdapter _entitySetAdapter; 13 | 14 | public InMemoryQueryableWrapper(InMemoryEntitySetAdapter entitySetAdapter, Object?[] parameters) 15 | { 16 | _parameters = parameters; 17 | _entitySetAdapter = entitySetAdapter; 18 | ElementType = entitySetAdapter.EntityType; 19 | } 20 | 21 | public IEnumerator GetEnumerator() 22 | { 23 | return (IEnumerator)(_entitySetAdapter.GetSource(_parameters[0]!)!).GetEnumerator(); 24 | } 25 | IEnumerator IEnumerable.GetEnumerator() 26 | { 27 | return GetEnumerator(); 28 | } 29 | 30 | public Type ElementType { get; } 31 | public Expression Expression => Expression.Constant(this); 32 | public IQueryProvider Provider => throw new NotImplementedException(); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /source/OdataToEntity/Parsers/Translators/OeAggregationEntryFactoryFactory.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.OData.Edm; 2 | using System; 3 | using System.Collections.Generic; 4 | 5 | namespace OdataToEntity.Parsers.Translators 6 | { 7 | internal sealed class OeAggregationEntryFactoryFactory : OeEntryFactoryFactory 8 | { 9 | private readonly List _aggProperties; 10 | 11 | public OeAggregationEntryFactoryFactory(List aggProperties) 12 | { 13 | _aggProperties = aggProperties; 14 | } 15 | 16 | public override OeEntryFactory CreateEntryFactory(IEdmEntitySet entitySet, Type clrType, OePropertyAccessor[]? skipTokenAccessors) 17 | { 18 | OePropertyAccessor[] accessors; 19 | if (_aggProperties.Count == 0) 20 | accessors = OePropertyAccessor.CreateFromType(clrType, entitySet); 21 | else 22 | { 23 | int groupIndex = _aggProperties.FindIndex(a => a.IsGroup); 24 | accessors = OePropertyAccessor.CreateFromTuple(clrType, _aggProperties, groupIndex); 25 | } 26 | 27 | return new OeEntryFactory(entitySet, accessors, skipTokenAccessors); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /source/OdataToEntity.Ef6/OeEf6AsyncEnumerator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Data.Entity.Infrastructure; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | 7 | namespace OdataToEntity.Ef6 8 | { 9 | public sealed class OeEf6AsyncEnumerator : IAsyncEnumerable, IAsyncEnumerator 10 | { 11 | private readonly IDbAsyncEnumerator _asyncEnumerator; 12 | private CancellationToken _cancellationToken; 13 | 14 | public OeEf6AsyncEnumerator(IDbAsyncEnumerable asyncEnumerable) 15 | { 16 | _asyncEnumerator = asyncEnumerable.GetAsyncEnumerator(); 17 | } 18 | 19 | public ValueTask DisposeAsync() 20 | { 21 | _asyncEnumerator.Dispose(); 22 | return new ValueTask(); 23 | } 24 | public IAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken) 25 | { 26 | _cancellationToken = cancellationToken; 27 | return this; 28 | } 29 | public ValueTask MoveNextAsync() 30 | { 31 | return new ValueTask(_asyncEnumerator.MoveNextAsync(_cancellationToken)); 32 | } 33 | 34 | public Object Current => _asyncEnumerator.Current; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /source/OdataToEntity/Parsers/Cache/OeQueryCache.cs: -------------------------------------------------------------------------------- 1 | using OdataToEntity.Parsers; 2 | using System; 3 | using System.Collections.Concurrent; 4 | using System.Linq.Expressions; 5 | 6 | namespace OdataToEntity.Cache 7 | { 8 | public sealed class OeQueryCache 9 | { 10 | private readonly ConcurrentDictionary _cache; 11 | 12 | public OeQueryCache() : this(true) 13 | { 14 | } 15 | public OeQueryCache(bool allowCache) 16 | { 17 | _cache = new ConcurrentDictionary(new OeCacheContextEqualityComparer()); 18 | AllowCache = allowCache; 19 | } 20 | 21 | public void AddQuery(OeCacheContext cacheContext, Object query, MethodCallExpression? countExpression, OeEntryFactory? entryFactory) 22 | { 23 | var queryCacheItem = new OeQueryCacheItem(query, countExpression, entryFactory); 24 | _cache.TryAdd(cacheContext, queryCacheItem); 25 | } 26 | public OeQueryCacheItem? GetQuery(OeCacheContext cacheContext) 27 | { 28 | _cache.TryGetValue(cacheContext, out OeQueryCacheItem? cacheItem); 29 | return cacheItem; 30 | } 31 | 32 | public bool AllowCache { get; set; } 33 | public int CacheCount => _cache.Count; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /source/OdataToEntity/Query/SelectAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace OdataToEntity.Query 5 | { 6 | [AttributeUsage(AttributeTargets.Property)] 7 | public sealed class SelectAttribute : Attribute 8 | { 9 | private SelectExpandType _selectType; 10 | 11 | public SelectAttribute(SelectExpandType selectType) 12 | { 13 | _selectType = selectType; 14 | SelectConfigurations = new Dictionary(); 15 | } 16 | public SelectAttribute(params String[] properties) 17 | { 18 | SelectConfigurations = new Dictionary(); 19 | foreach (String key in properties) 20 | SelectConfigurations[key] = SelectExpandType.Allowed; 21 | } 22 | 23 | internal Dictionary SelectConfigurations { get; } 24 | public SelectExpandType SelectType 25 | { 26 | get 27 | { 28 | return _selectType; 29 | } 30 | set 31 | { 32 | _selectType = value; 33 | foreach (String item in new List(SelectConfigurations.Keys)) 34 | SelectConfigurations[item] = _selectType; 35 | } 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /source/OdataToEntity.EfCore.DynamicDataContext/DynamicTypeDefinitionManagerFactory.cs: -------------------------------------------------------------------------------- 1 | using OdataToEntity.EfCore.DynamicDataContext.ModelBuilder; 2 | using OdataToEntity.EfCore.DynamicDataContext.Types; 3 | using System; 4 | using System.Globalization; 5 | using System.Threading; 6 | 7 | namespace OdataToEntity.EfCore.DynamicDataContext 8 | { 9 | public class DynamicTypeDefinitionManagerFactory 10 | { 11 | private static int _dynamicDbContextIndex; 12 | 13 | public virtual DynamicTypeDefinitionManager Create(DynamicMetadataProvider metadataProvider) 14 | { 15 | return DynamicTypeDefinitionManager.Create(metadataProvider, CreateDynamicDbContextType()); 16 | } 17 | protected static Type CreateDynamicDbContextType() 18 | { 19 | int dynamicDbContextIndex = Interlocked.Increment(ref _dynamicDbContextIndex); 20 | String fullName = typeof(DynamicDbContext1).Namespace + "." + nameof(DynamicDbContext) + dynamicDbContextIndex.ToString(CultureInfo.InvariantCulture); 21 | Type? dynamicDbContextType = Type.GetType(fullName); 22 | if (dynamicDbContextType == null) 23 | throw new InvalidOperationException("DynamicDbContext out range " + dynamicDbContextIndex.ToString(CultureInfo.InvariantCulture)); 24 | 25 | return dynamicDbContextType; 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /source/OdataToEntity/Query/ExpandAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace OdataToEntity.Query 5 | { 6 | [AttributeUsage(AttributeTargets.Property)] 7 | public sealed class ExpandAttribute : Attribute 8 | { 9 | private SelectExpandType _expandType; 10 | 11 | public ExpandAttribute(SelectExpandType expandType) 12 | { 13 | _expandType = expandType; 14 | ExpandConfigurations = new Dictionary(); 15 | } 16 | public ExpandAttribute(params String[] properties) 17 | { 18 | ExpandConfigurations = new Dictionary(); 19 | foreach (String key in properties) 20 | ExpandConfigurations[key] = SelectExpandType.Allowed; 21 | } 22 | 23 | internal Dictionary ExpandConfigurations { get; } 24 | public SelectExpandType ExpandType 25 | { 26 | get 27 | { 28 | return _expandType; 29 | } 30 | set 31 | { 32 | _expandType = value; 33 | foreach (String item in new List(ExpandConfigurations.Keys)) 34 | ExpandConfigurations[item] = _expandType; 35 | } 36 | } 37 | public int MaxDepth { get; set; } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /source/OdataToEntity.EfCore.DynamicDataContext/DynamicTypeDefinition.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore.Metadata; 2 | using System; 3 | using System.Collections.Generic; 4 | 5 | namespace OdataToEntity.EfCore.DynamicDataContext 6 | { 7 | public sealed class DynamicTypeDefinition 8 | { 9 | private readonly Dictionary _navigations; 10 | 11 | public DynamicTypeDefinition(Type dynamicTypeType, in TableFullName tableFullName, bool isQueryType, String tableEdmName) 12 | { 13 | DynamicTypeType = dynamicTypeType; 14 | TableFullName = tableFullName; 15 | TableEdmName = tableEdmName; 16 | IsQueryType = isQueryType; 17 | 18 | _navigations = new Dictionary(); 19 | } 20 | 21 | internal void AddNavigationProperty(INavigation navigation, Type clrType) 22 | { 23 | if (!_navigations.TryGetValue(navigation, out _)) 24 | _navigations.Add(navigation, clrType); 25 | } 26 | public Type GetNavigationPropertyClrType(INavigation navigation) 27 | { 28 | return _navigations[navigation]; 29 | } 30 | 31 | public Type DynamicTypeType { get; } 32 | public bool IsQueryType { get; } 33 | public String TableEdmName { get; } 34 | public TableFullName TableFullName { get; } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /source/OdataToEntity/Query/FilterAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace OdataToEntity.Query 5 | { 6 | [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property, AllowMultiple = true)] 7 | public sealed class FilterAttribute : Attribute 8 | { 9 | private bool _disable; 10 | 11 | public FilterAttribute(bool disabled) 12 | { 13 | _disable = disabled; 14 | FilterConfigurations = new Dictionary(); 15 | } 16 | public FilterAttribute(params String[] properties) 17 | { 18 | FilterConfigurations = new Dictionary(); 19 | foreach (String key in properties) 20 | FilterConfigurations[key] = SelectExpandType.Allowed; 21 | } 22 | 23 | public bool Disabled 24 | { 25 | get 26 | { 27 | return _disable; 28 | } 29 | set 30 | { 31 | _disable = value; 32 | foreach (String item in new List(FilterConfigurations.Keys)) 33 | FilterConfigurations[item] = _disable ? SelectExpandType.Disabled : SelectExpandType.Allowed; 34 | } 35 | } 36 | 37 | internal Dictionary FilterConfigurations { get; } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /source/OdataToEntity/Query/OrderByAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace OdataToEntity.Query 5 | { 6 | [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property, AllowMultiple = true)] 7 | public sealed class OrderByAttribute : Attribute 8 | { 9 | private bool _disable; 10 | 11 | public OrderByAttribute(bool disabled) 12 | { 13 | _disable = disabled; 14 | OrderByConfigurations = new Dictionary(); 15 | } 16 | public OrderByAttribute(params String[] properties) 17 | { 18 | OrderByConfigurations = new Dictionary(); 19 | foreach (String key in properties) 20 | OrderByConfigurations[key] = SelectExpandType.Allowed; 21 | } 22 | 23 | public bool Disabled 24 | { 25 | get 26 | { 27 | return _disable; 28 | } 29 | set 30 | { 31 | _disable = value; 32 | foreach (String item in new List(OrderByConfigurations.Keys)) 33 | OrderByConfigurations[item] = _disable ? SelectExpandType.Disabled : SelectExpandType.Allowed; 34 | } 35 | } 36 | 37 | internal Dictionary OrderByConfigurations { get; } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /test/OdataToEntity.Test.Wcf/OdataToEntity.Test.WcfService/Program.cs: -------------------------------------------------------------------------------- 1 | using CoreWCF; 2 | using CoreWCF.Configuration; 3 | using Microsoft.AspNetCore; 4 | using Microsoft.AspNetCore.Builder; 5 | using Microsoft.AspNetCore.Hosting; 6 | using Microsoft.Extensions.DependencyInjection; 7 | using System; 8 | 9 | namespace OdataToEntity.Test.WcfService 10 | { 11 | public class Startup 12 | { 13 | public void ConfigureServices(IServiceCollection services) 14 | { 15 | services.AddServiceModelServices(); 16 | } 17 | 18 | public void Configure(IApplicationBuilder app) 19 | { 20 | app.UseServiceModel(builder => 21 | { 22 | builder.AddService(); 23 | builder.AddServiceEndpoint(new NetTcpBinding(), "/OdataWcfService"); 24 | }); 25 | } 26 | } 27 | 28 | class Program 29 | { 30 | static void Main(String[] args) 31 | { 32 | using (IWebHost host = WebHost.CreateDefaultBuilder(args) 33 | .UseStartup() 34 | .UseNetTcp(5000) 35 | .Build()) 36 | { 37 | host.Start(); 38 | do 39 | Console.WriteLine("Close server press escape to exit"); 40 | while (Console.ReadKey().Key != ConsoleKey.Escape); 41 | } 42 | } 43 | } 44 | } -------------------------------------------------------------------------------- /source/OdataToEntity.GraphQL/OdataToEntity.GraphQL.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Translate and execute GraphQL query via OData 6 | Maxim Voronov 7 | $(NetStandardVersion) 8 | OdataToEntity.GraphQL 9 | $(OdataToEntityVersion) 10 | Translate GraphQL to OData 11 | $(CSharpVersion) 12 | enable 13 | 14 | 15 | 16 | MIT 17 | $(Description) 18 | $(Title) 19 | $(OdataToEntityVersion) 20 | 21 | 22 | 23 | OdataToEntity.GraphQL 24 | graphql;odata;odatatoentity 25 | https://github.com/voronov-maxim/OdataToEntity 26 | $(OdataToEntityVersion) 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /source/OdataToEntity.EfCore/Postgresql/OePostgreSqlEfCoreOperationAdapter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Threading; 4 | 5 | namespace OdataToEntity.EfCore.Postgresql 6 | { 7 | public class OePostgreSqlEfCoreOperationAdapter : OeEfCoreOperationAdapter 8 | { 9 | public OePostgreSqlEfCoreOperationAdapter(Type dataContextType) : base(dataContextType, true) 10 | { 11 | } 12 | 13 | public override IAsyncEnumerable ExecuteProcedureNonQuery(Object dataContext, String operationName, IReadOnlyList> parameters) 14 | { 15 | return base.ExecuteFunctionNonQuery(dataContext, operationName, parameters); 16 | } 17 | public override IAsyncEnumerable ExecuteProcedureReader(Object dataContext, String operationName, IReadOnlyList> parameters, Db.OeEntitySetAdapter entitySetAdapter) 18 | { 19 | return base.ExecuteFunctionReader(dataContext, operationName, parameters, entitySetAdapter); 20 | } 21 | public override IAsyncEnumerable ExecuteProcedurePrimitive(Object dataContext, String operationName, 22 | IReadOnlyList> parameters, Type returnType, CancellationToken cancellationToken) 23 | { 24 | return base.ExecuteFunctionPrimitive(dataContext, operationName, parameters, returnType, cancellationToken); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /source/OdataToEntity.EfCore.DynamicDataContext/ModelBuilder/DynamicConventionSetBuilder.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore.Metadata.Conventions; 2 | using Microsoft.EntityFrameworkCore.Metadata.Conventions.Infrastructure; 3 | using Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal; 4 | using System.Collections; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Reflection; 8 | 9 | namespace OdataToEntity.EfCore.DynamicDataContext.ModelBuilder 10 | { 11 | public sealed class DynamicConventionSetPlugin : IConventionSetPlugin 12 | { 13 | public ConventionSet ModifyConventions(ConventionSet conventionSet) 14 | { 15 | KeyDiscoveryConvention keyDiscoveryConvention = conventionSet.EntityTypeAddedConventions.OfType().Single(); 16 | foreach (PropertyInfo propertyInfo in typeof(ConventionSet).GetProperties()) 17 | if (propertyInfo.GetValue(conventionSet) is IList list) 18 | list.Remove(keyDiscoveryConvention); 19 | return conventionSet; 20 | } 21 | } 22 | 23 | public sealed class DynamicConventionSetBuilder : RuntimeConventionSetBuilder 24 | { 25 | public DynamicConventionSetBuilder(IProviderConventionSetBuilder providerConventionSetBuilder, IEnumerable plugins) 26 | : base(providerConventionSetBuilder, new[] { new DynamicConventionSetPlugin() }) 27 | { 28 | } 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /test/OdataToEntity.Test.Ef6.SqlServer/OrderDataAdapter.cs: -------------------------------------------------------------------------------- 1 | using OdataToEntity.Ef6; 2 | using OdataToEntity.Test.Ef6.SqlServer; 3 | using System; 4 | 5 | namespace OdataToEntity.Test.Model 6 | { 7 | public sealed class OrderDataAdapter : OeEf6SqlServerDataAdapter 8 | { 9 | private readonly bool _useRelationalNulls; 10 | 11 | public OrderDataAdapter(bool allowCache, bool useRelationalNulls) : base(new Cache.OeQueryCache(allowCache)) 12 | { 13 | _useRelationalNulls = useRelationalNulls; 14 | } 15 | 16 | public override Object CreateDataContext() 17 | { 18 | return new OrderEf6Context(_useRelationalNulls); 19 | } 20 | public static ModelBuilder.OeEdmModelMetadataProvider CreateMetadataProvider() 21 | { 22 | using (var dbContext = new OrderEf6Context(false)) 23 | return new OeEf6EdmModelMetadataProvider(dbContext); 24 | } 25 | } 26 | 27 | public sealed class Order2DataAdapter : OeEf6SqlServerDataAdapter 28 | { 29 | public readonly bool _useRelationalNulls; 30 | 31 | public Order2DataAdapter(bool allowCache, bool useRelationalNulls) : base(new Cache.OeQueryCache(allowCache)) 32 | { 33 | _useRelationalNulls = useRelationalNulls; 34 | } 35 | 36 | public override Object CreateDataContext() 37 | { 38 | return new Order2Ef6Context(_useRelationalNulls); 39 | } 40 | } 41 | } -------------------------------------------------------------------------------- /test/OdataToEntity.Test.Linq2Db/OeLinq2DbSqlServerDataAdapter.cs: -------------------------------------------------------------------------------- 1 | using LinqToDB.Data; 2 | using System; 3 | using System.Collections; 4 | using System.Collections.Generic; 5 | using System.Data; 6 | 7 | namespace OdataToEntity.Linq2Db 8 | { 9 | public class OeLinq2DbSqlServerDataAdapter : OeLinq2DbDataAdapter where T : DataConnection, IOeLinq2DbDataContext 10 | { 11 | private sealed class OeLinq2DbSqlServerOperationAdapter : OeLinq2DbOperationAdapter 12 | { 13 | public OeLinq2DbSqlServerOperationAdapter(Type dataContextType) 14 | : base(dataContextType) 15 | { 16 | } 17 | 18 | protected override Object GetParameterCore(KeyValuePair parameter, String parameterName, int parameterIndex) 19 | { 20 | if (!(parameter.Value is String) && parameter.Value is IEnumerable list) 21 | { 22 | DataTable table = Infrastructure.OeDataTableHelper.GetDataTable(list); 23 | return new DataParameter(parameter.Key, table) { DbType = parameter.Key }; 24 | } 25 | 26 | return parameter.Value; 27 | } 28 | } 29 | 30 | public OeLinq2DbSqlServerDataAdapter() : this(null) 31 | { 32 | } 33 | public OeLinq2DbSqlServerDataAdapter(Cache.OeQueryCache queryCache) 34 | : base(queryCache, new OeLinq2DbSqlServerOperationAdapter(typeof(T))) 35 | { 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /source/OdataToEntity/ModelBuilder/OeModelBuilderHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel.DataAnnotations; 4 | using System.Reflection; 5 | 6 | namespace OdataToEntity.ModelBuilder 7 | { 8 | public static class OeModelBuilderHelper 9 | { 10 | public static PropertyInfo? GetConventionalKeyProperty(Type clrType) 11 | { 12 | PropertyInfo? key = clrType.GetPropertyIgnoreCaseOrNull("id"); 13 | if (key != null) 14 | return key; 15 | 16 | key = clrType.GetPropertyIgnoreCaseOrNull(clrType.Name + "id"); 17 | if (key != null) 18 | return key; 19 | 20 | return clrType.GetPropertyIgnoreCaseOrNull(clrType.Name + "_id"); 21 | } 22 | public static IReadOnlyList GetKeyProperties(Type entityType) 23 | { 24 | List? keys = null; 25 | foreach (PropertyInfo property in entityType.GetProperties()) 26 | if (property.IsDefined(typeof(KeyAttribute))) 27 | { 28 | if (keys == null) 29 | keys = new List(); 30 | 31 | keys.Add(property); 32 | } 33 | 34 | if (keys != null) 35 | return keys; 36 | 37 | PropertyInfo? key = GetConventionalKeyProperty(entityType); 38 | return key == null ? Array.Empty() : new[] { key }; 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /test/OdataToEntity.Test.Asp/OdataToEntity.Test.AspClient/Program.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.OData.Client; 2 | using ODataClient.OdataToEntity.Test.Model; 3 | using System; 4 | 5 | namespace OdataToEntity.Test.AspClient 6 | { 7 | internal sealed class NC_PLNull : SelectTest 8 | { 9 | public NC_PLNull() : base(new DbFixtureInitDb()) 10 | { 11 | } 12 | } 13 | 14 | internal sealed class NC_PLNull_ManyColumns : ManyColumnsTest 15 | { 16 | public NC_PLNull_ManyColumns() : base(new ManyColumnsFixtureInitDb()) 17 | { 18 | } 19 | } 20 | 21 | class Program 22 | { 23 | private static Container CreateContainer() 24 | { 25 | return new Container(new Uri("http://localhost:5000/api")) { MergeOption = MergeOption.OverwriteChanges }; 26 | } 27 | 28 | static void Main(String[] args) 29 | { 30 | System.Threading.Thread.Sleep(1000); 31 | 32 | DbFixtureInitDb.ContainerFactory = CreateContainer; 33 | 34 | DbFixtureInitDb.RunTest(new BatchTest()).GetAwaiter().GetResult(); 35 | DbFixtureInitDb.RunTest(new NC_PLNull()).GetAwaiter().GetResult(); 36 | DbFixtureInitDb.RunTest(new NC_PLNull_ManyColumns()).GetAwaiter().GetResult(); 37 | DbFixtureInitDb.RunTest(new ProcedureTest()).GetAwaiter().GetResult(); 38 | 39 | Console.WriteLine(); 40 | Console.WriteLine("Press any key to continue..."); 41 | Console.ReadKey(); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /source/OdataToEntity.Ef6/OdataToEntity.Ef6.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Classes bridge from OdataToEntity to Entity Framework 6.3.0. Abstraction layer access to DataContext Entity Framework 6.3.0. 6 | Maxim Voronov 7 | $(NetStandardVersion);$(FrameworkVersion) 8 | AnyCPU 9 | portable 10 | OdataToEntity.Ef6 11 | Entity Framework 6 adapter for OdataEntity 12 | 13 | 14 | 15 | MIT 16 | $(Description) 17 | $(Title) 18 | $(OdataToEntityVersion) 19 | 20 | 21 | 22 | OdataToEntity.Ef6 23 | odata;data;services;odatatoentity;ef;entity framework;entity 24 | https://github.com/voronov-maxim/OdataToEntity 25 | $(OdataToEntityVersion) 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /source/OdataToEntity/InMemory/InMemoryOperationAdapter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Threading; 4 | 5 | namespace OdataToEntity.InMemory 6 | { 7 | internal sealed class InMemoryOperationAdapter : Db.OeOperationAdapter 8 | { 9 | public static readonly Db.OeOperationAdapter Instance = new InMemoryOperationAdapter(); 10 | 11 | private InMemoryOperationAdapter() : base(typeof(Object), false) 12 | { 13 | } 14 | 15 | protected override IAsyncEnumerable ExecuteNonQuery(Object dataContext, String sql, IReadOnlyList> parameters) 16 | { 17 | throw new NotSupportedException(); 18 | } 19 | protected override IAsyncEnumerable ExecutePrimitive(Object dataContext, String sql, IReadOnlyList> parameters, 20 | Type returnType, CancellationToken cancellationToken) 21 | { 22 | throw new NotSupportedException(); 23 | } 24 | protected override IAsyncEnumerable ExecuteReader(Object dataContext, String sql, IReadOnlyList> parameters, 25 | Db.OeEntitySetAdapter entitySetAdapter) 26 | { 27 | throw new NotSupportedException(); 28 | } 29 | protected override String[] GetParameterNames(Object dataContext, IReadOnlyList> parameters) 30 | { 31 | throw new NotSupportedException(); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /source/OdataToEntity.EfCore.DynamicDataContext/DynamicMiddlewareHelper.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.OData.Edm; 2 | using OdataToEntity.EfCore.DynamicDataContext.InformationSchema; 3 | 4 | namespace OdataToEntity.EfCore.DynamicDataContext 5 | { 6 | public static class DynamicMiddlewareHelper 7 | { 8 | public static IEdmModel CreateEdmModel(ProviderSpecificSchema providerSchema, InformationSchemaSettings? informationSchemaSettings) 9 | { 10 | return CreateEdmModel(providerSchema, informationSchemaSettings ?? new InformationSchemaSettings(), new DynamicTypeDefinitionManagerFactory()); 11 | } 12 | public static IEdmModel CreateEdmModel(ProviderSpecificSchema providerSchema, InformationSchemaSettings informationSchemaSettings, DynamicTypeDefinitionManagerFactory factory) 13 | { 14 | using (var metadataProvider = providerSchema.CreateMetadataProvider(informationSchemaSettings)) 15 | { 16 | DynamicTypeDefinitionManager typeDefinitionManager = factory.Create(metadataProvider); 17 | var dataAdapter = new DynamicDataAdapter(typeDefinitionManager); 18 | return dataAdapter.BuildEdmModel(metadataProvider); 19 | } 20 | } 21 | public static IEdmModel CreateEdmModelViaEmit(ProviderSpecificSchema providerSchema, InformationSchemaSettings informationSchemaSettings) 22 | { 23 | return CreateEdmModel(providerSchema, informationSchemaSettings, new EmitDynamicTypeDefinitionManagerFactory()); 24 | } 25 | } 26 | } 27 | 28 | -------------------------------------------------------------------------------- /test/OdataToEntity.Test.EfCore.PostgreSql/OrderContextOptions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | using Microsoft.OData.Edm; 3 | using OdataToEntity.EfCore; 4 | 5 | namespace OdataToEntity.Test.Model 6 | { 7 | public static class OrderContextOptions 8 | { 9 | public static EdmModel BuildDbEdmModel(IEdmModel _, bool useRelationalNulls) 10 | { 11 | var orderDataAdapter = new OeEfCoreDataAdapter(Create(useRelationalNulls)) { IsDatabaseNullHighestValue = true }; 12 | IEdmModel orderEdmModel = orderDataAdapter.BuildEdmModelFromEfCoreModel(); 13 | var order2DataAdapter = new OeEfCoreDataAdapter(Create(useRelationalNulls)) { IsDatabaseNullHighestValue = true }; 14 | return order2DataAdapter.BuildEdmModelFromEfCoreModel(orderEdmModel); 15 | } 16 | public static DbContextOptions Create(bool useRelationalNulls) 17 | { 18 | return Create(useRelationalNulls); 19 | } 20 | public static DbContextOptions Create(bool useRelationalNulls) where T : DbContext 21 | { 22 | Npgsql.NpgsqlConnection.GlobalTypeMapper.MapComposite("dbo.string_list"); 23 | 24 | var optionsBuilder = new DbContextOptionsBuilder(); 25 | optionsBuilder.UseNpgsql(@"Host=localhost;Port=5432;Database=OdataToEntity;Pooling=true", opt => opt.UseRelationalNulls(useRelationalNulls)); 26 | return optionsBuilder.Options; 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /source/OdataToEntity/ModelBuilder/OeEdmNavigationShadowProperty.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.OData.Edm; 2 | using System.Reflection; 3 | 4 | namespace OdataToEntity.ModelBuilder 5 | { 6 | public sealed class OeEdmNavigationShadowProperty : IEdmNavigationProperty 7 | { 8 | private readonly IEdmNavigationProperty _edmNavigationProperty; 9 | private IEdmNavigationProperty _partner; 10 | 11 | public OeEdmNavigationShadowProperty(IEdmNavigationProperty edmNavigationProperty, PropertyInfo propertyInfo) 12 | { 13 | _edmNavigationProperty = edmNavigationProperty; 14 | PropertyInfo = propertyInfo; 15 | 16 | _partner = edmNavigationProperty.Partner; 17 | } 18 | 19 | internal void SetPartner(IEdmNavigationProperty partner) 20 | { 21 | _partner = partner; 22 | } 23 | 24 | public bool ContainsTarget => _edmNavigationProperty.ContainsTarget; 25 | public IEdmStructuredType DeclaringType => _edmNavigationProperty.DeclaringType; 26 | public string Name => _edmNavigationProperty.Name; 27 | public EdmOnDeleteAction OnDelete => _edmNavigationProperty.OnDelete; 28 | public IEdmNavigationProperty Partner => _partner; 29 | public PropertyInfo PropertyInfo { get; } 30 | public EdmPropertyKind PropertyKind => _edmNavigationProperty.PropertyKind; 31 | public IEdmReferentialConstraint ReferentialConstraint => _edmNavigationProperty.ReferentialConstraint; 32 | public IEdmTypeReference Type => _edmNavigationProperty.Type; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /test/OdataToEntity.Test/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("OdataToEntity.Test")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("OdataToEntity.Test")] 13 | [assembly: AssemblyCopyright("Copyright © 2016")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("5552ddd1-daec-4960-bc4b-01852c9f982a")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /source/OdataToEntity/Db/OeDataAdapter.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.OData.Edm; 2 | using OdataToEntity.Parsers; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Threading; 6 | using System.Threading.Tasks; 7 | 8 | namespace OdataToEntity.Db 9 | { 10 | public abstract class OeDataAdapter 11 | { 12 | public OeDataAdapter(Cache.OeQueryCache? queryCache, OeOperationAdapter operationAdapter) 13 | { 14 | QueryCache = queryCache ?? new Cache.OeQueryCache(true); 15 | OperationAdapter = operationAdapter; 16 | } 17 | 18 | public abstract void CloseDataContext(Object dataContext); 19 | public abstract Object CreateDataContext(); 20 | public abstract IAsyncEnumerable Execute(Object dataContext, OeQueryContext queryContext); 21 | public abstract TResult ExecuteScalar(Object dataContext, OeQueryContext queryContext); 22 | public abstract Task SaveChangesAsync(Object dataContext, CancellationToken cancellationToken); 23 | protected internal virtual void SetEdmModel(IEdmModel edmModel) { } 24 | 25 | public abstract Type DataContextType { get; } 26 | public abstract OeEntitySetAdapterCollection EntitySetAdapters { get; } 27 | public bool IsCaseSensitive 28 | { 29 | get => OperationAdapter.IsCaseSensitive; 30 | set => OperationAdapter.IsCaseSensitive = value; 31 | } 32 | public bool IsDatabaseNullHighestValue { get; set; } 33 | public OeOperationAdapter OperationAdapter { get; } 34 | protected Cache.OeQueryCache QueryCache { get; } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /source/OdataToEntity.EfCore.DynamicDataContext/OdataToEntity.EfCore.DynamicDataContext.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Access database without data context. 6 | Maxim Voronov 7 | $(NetStandardVersion) 8 | AnyCPU 9 | portable 10 | OdataToEntity.EfCore.DynamicDataContext 11 | $(OdataToEntityVersion) 12 | Dynamic data context for OdataEntity 13 | $(CSharpVersion) 14 | enable 15 | 16 | 17 | 18 | MIT 19 | $(Description) 20 | $(Title) 21 | $(OdataToEntityVersion) 22 | 23 | 24 | 25 | EF1001 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /source/OdataToEntity.Linq2Db/EntityUpdateHelper.cs: -------------------------------------------------------------------------------- 1 | #nullable enable 2 | using System; 3 | using System.Linq.Expressions; 4 | using System.Reflection; 5 | 6 | namespace OdataToEntity.Linq2Db 7 | { 8 | internal static class EntityUpdateHelper 9 | { 10 | public static SetBuilder GetSetBuilder(PropertyInfo updatedProperty) 11 | { 12 | Type setBuilderType = typeof(SetBuilder<,>).MakeGenericType(typeof(T), updatedProperty.PropertyType); 13 | return (SetBuilder)Activator.CreateInstance(setBuilderType, new Object[] { updatedProperty })!; 14 | } 15 | public static Expression> GetWhere(PropertyInfo[] primaryKey, T entity) 16 | { 17 | if (primaryKey.Length == 0) 18 | throw new InvalidOperationException("Missing primary key"); 19 | 20 | ParameterExpression parameter = Expression.Parameter(typeof(T)); 21 | BinaryExpression? where = null; 22 | foreach (PropertyInfo propertyInfo in primaryKey) 23 | { 24 | MemberExpression propertyExpression = Expression.Property(parameter, propertyInfo); 25 | MemberExpression valueExpression = Expression.Property(Expression.Constant(entity), propertyInfo); 26 | BinaryExpression equal = Expression.Equal(propertyExpression, valueExpression); 27 | if (where == null) 28 | where = equal; 29 | else 30 | where = Expression.AndAlso(where, equal); 31 | } 32 | return Expression.Lambda>(where!, parameter); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /source/OdataToEntity.GraphQL/OeGraphqlParser.cs: -------------------------------------------------------------------------------- 1 | using GraphQL; 2 | using GraphQL.Types; 3 | using Microsoft.OData; 4 | using Microsoft.OData.Edm; 5 | using System; 6 | using System.Threading.Tasks; 7 | 8 | namespace OdataToEntity.GraphQL 9 | { 10 | public readonly struct OeGraphqlParser 11 | { 12 | public OeGraphqlParser(IEdmModel edmModel) 13 | { 14 | EdmModel = edmModel; 15 | Schema = new OeSchemaBuilder(edmModel).Build(); 16 | Schema.Initialize(); 17 | } 18 | 19 | public Task Execute(String query) 20 | { 21 | return Execute(query, null); 22 | } 23 | public async Task Execute(String query, Inputs? inputs) 24 | { 25 | Schema schema = Schema; 26 | return await new DocumentExecuter().ExecuteAsync(options => 27 | { 28 | options.Inputs = inputs; 29 | options.Query = query; 30 | options.Schema = schema; 31 | options.ThrowOnUnhandledException = true; 32 | }).ConfigureAwait(false); 33 | } 34 | public Uri GetOdataUri(String query) 35 | { 36 | var context = new ResolveFieldContext() 37 | { 38 | Schema = Schema 39 | }; 40 | var translator = new OeGraphqlAstToODataUri(EdmModel, context); 41 | ODataUri odataUri = translator.Translate(query); 42 | return odataUri.BuildUri(ODataUrlKeyDelimiter.Parentheses); 43 | } 44 | 45 | public IEdmModel EdmModel { get; } 46 | public Schema Schema { get; } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /test/OdataToEntity.Test.Ef6.SqlServer/OeEf6SqlServerDataAdapter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Data; 5 | using System.Data.Entity; 6 | using System.Data.SqlClient; 7 | 8 | namespace OdataToEntity.Ef6 9 | { 10 | public class OeEf6SqlServerDataAdapter : OeEf6DataAdapter where T : DbContext 11 | { 12 | private sealed class OeEf6SqlServerOperationAdapter : OeEf6OperationAdapter 13 | { 14 | public OeEf6SqlServerOperationAdapter(Type dataContextType) 15 | : base(dataContextType) 16 | { 17 | } 18 | 19 | protected override Object GetParameterCore(KeyValuePair parameter, String parameterName, int parameterIndex) 20 | { 21 | if (!(parameter.Value is String) && parameter.Value is IEnumerable list) 22 | { 23 | DataTable table = Infrastructure.OeDataTableHelper.GetDataTable(list); 24 | if (parameterName == null) 25 | parameterName = "@p" + parameterIndex.ToString(System.Globalization.CultureInfo.InvariantCulture); 26 | return new SqlParameter(parameterName, table) { TypeName = parameter.Key }; 27 | } 28 | 29 | return parameter.Value; 30 | } 31 | } 32 | 33 | public OeEf6SqlServerDataAdapter() : this(null) 34 | { 35 | } 36 | public OeEf6SqlServerDataAdapter(Cache.OeQueryCache queryCache) 37 | : base(queryCache, new OeEf6SqlServerOperationAdapter(typeof(T))) 38 | { 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /source/OdataToEntity.AspNetCore/ODataResult.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Http; 2 | using Microsoft.AspNetCore.Mvc; 3 | using OdataToEntity.Parsers; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Threading.Tasks; 7 | 8 | namespace OdataToEntity.AspNetCore 9 | { 10 | public class ODataResult : IActionResult 11 | { 12 | private readonly IAsyncEnumerator? _entities; 13 | private readonly OeQueryContext? _queryContext; 14 | 15 | protected ODataResult() 16 | { 17 | } 18 | public ODataResult(OeQueryContext queryContext, IAsyncEnumerator entities) 19 | { 20 | _queryContext = queryContext; 21 | _entities = entities; 22 | } 23 | 24 | public virtual async Task ExecuteResultAsync(ActionContext context) 25 | { 26 | if (_queryContext == null || _entities == null) 27 | throw new InvalidOperationException("Derive class must override ExecuteResultAsync"); 28 | 29 | if (_queryContext.EntryFactory == null) 30 | throw new InvalidOperationException("QueryContext.EntryFactory must be not null"); 31 | 32 | HttpContext httpContext = context.HttpContext; 33 | OeEntryFactory entryFactoryFromTuple = _queryContext.EntryFactory.GetEntryFactoryFromTuple(_queryContext.EdmModel, _queryContext.ODataUri.OrderBy); 34 | await Writers.OeGetWriter.SerializeAsync(_queryContext, (IAsyncEnumerator)_entities, 35 | httpContext.Request.ContentType, httpContext.Response.Body, entryFactoryFromTuple, null, httpContext.RequestAborted).ConfigureAwait(false); 36 | } 37 | } 38 | } -------------------------------------------------------------------------------- /source/OdataToEntity.Linq2Db/OdataToEntity.Linq2Db.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Classes bridge from OdataToEntity to Linq2Db. Abstraction layer access to DataContext Entity Linq2Db 6 | Maxim Voronov 7 | $(NetStandardVersion) 8 | AnyCPU 9 | portable 10 | OdataToEntity.Linq2Db 11 | Linq2Db adapter for OdataEntity 12 | $(CSharpVersion) 13 | enable 14 | 15 | 16 | 17 | MIT 18 | $(Description) 19 | $(Title) 20 | $(OdataToEntityVersion) 21 | 22 | 23 | 24 | OdataToEntity.Linq2Db 25 | odata;data;services;odatatoentity;linq2db;entity 26 | https://github.com/voronov-maxim/OdataToEntity 27 | $(OdataToEntityVersion) 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /source/OdataToEntity.AspNetCore/OdataToEntity.AspNetCore.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Extension for easy create Asp Mvc .Net Core service 6 | Maxim Voronov 7 | $(NetCoreAppVersion) 8 | AnyCPU 9 | portable 10 | OdataToEntity.AspNetCore 11 | OdataToEnity Asp Mvc .Net Core extension 12 | $(CSharpVersion) 13 | enable 14 | 15 | 16 | 17 | MIT 18 | $(Description) 19 | $(Title) 20 | $(OdataToEntityVersion) 21 | 22 | 23 | 24 | OdataToEntity.AspNetCore 25 | odata;data;services;odatalib;edmlib;asp;mvc;controller;core 26 | https://github.com/voronov-maxim/OdataToEntity 27 | $(OdataToEntityVersion) 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /test/OdataToEntity.Test.Linq2Db/OrderDataAdapter.cs: -------------------------------------------------------------------------------- 1 | extern alias lq2db; 2 | 3 | using OdataToEntity.EfCore; 4 | using OdataToEntity.Linq2Db; 5 | 6 | using OdataToEntityDB = lq2db::OdataToEntity.Test.Model.OdataToEntityDB; 7 | using Order2Connection = lq2db::OdataToEntity.Test.Model.Order2Connection; 8 | 9 | namespace OdataToEntity.Test.Model 10 | { 11 | public sealed class OrderDataAdapter : OeLinq2DbSqlServerDataAdapter 12 | { 13 | private sealed class OrderDbDataAdapter : OeEfCoreDataAdapter 14 | { 15 | public OrderDbDataAdapter(bool useRelationalNulls) : base(new Cache.OeQueryCache(false)) 16 | { 17 | } 18 | } 19 | 20 | public OrderDataAdapter(bool allowCache, bool useRelationalNulls) : base(new Cache.OeQueryCache(allowCache)) 21 | { 22 | LinqToDB.Common.Configuration.Linq.CompareNullsAsValues = !useRelationalNulls; 23 | } 24 | 25 | public static ModelBuilder.OeEdmModelMetadataProvider CreateMetadataProvider() 26 | { 27 | return new OeLinq2DbEdmModelMetadataProvider(); 28 | } 29 | } 30 | 31 | public sealed class Order2DataAdapter : OeLinq2DbSqlServerDataAdapter 32 | { 33 | private sealed class Order2DbDataAdapter : OeEfCoreDataAdapter 34 | { 35 | public Order2DbDataAdapter(bool useRelationalNulls) : base(new Cache.OeQueryCache(false)) 36 | { 37 | } 38 | } 39 | 40 | public Order2DataAdapter(bool allowCache, bool useRelationalNulls) : base(new Cache.OeQueryCache(allowCache)) 41 | { 42 | LinqToDB.Common.Configuration.Linq.CompareNullsAsValues = !useRelationalNulls; 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /source/OdataToEntity/Parsers/Cache/OeCacheContext.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.OData; 2 | using Microsoft.OData.UriParser; 3 | using OdataToEntity.Parsers; 4 | using System; 5 | using System.Collections.Generic; 6 | 7 | namespace OdataToEntity.Cache 8 | { 9 | public sealed class OeCacheContext 10 | { 11 | private static readonly Dictionary Empty = new Dictionary(); 12 | 13 | public OeCacheContext(OeQueryContext queryContext) 14 | { 15 | ODataUri = queryContext.ODataUri; 16 | ParseNavigationSegments = queryContext.ParseNavigationSegments; 17 | MetadataLevel = queryContext.MetadataLevel; 18 | SkipTokenNameValues = queryContext.SkipTokenNameValues; 19 | ConstantToParameterMapper = Empty; 20 | ParameterValues = Array.Empty(); 21 | } 22 | public OeCacheContext(OeQueryContext queryContext, IReadOnlyDictionary constantToParameterMapper) 23 | : this(queryContext) 24 | { 25 | ConstantToParameterMapper = constantToParameterMapper; 26 | } 27 | 28 | 29 | public IReadOnlyDictionary ConstantToParameterMapper { get; } 30 | public OeMetadataLevel MetadataLevel { get; } 31 | public bool NavigationNextLink { get; } 32 | public ODataUri ODataUri { get; } 33 | public IReadOnlyList ParameterValues { get; set; } 34 | public IReadOnlyList ParseNavigationSegments { get; } 35 | public OeSkipTokenNameValue[] SkipTokenNameValues { get; } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /test/OdataToEntity.Test.GraphQL/OdataToEntity.Test.GraphQL.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Exe 6 | $(NetCoreAppVersion) 7 | AnyCPU 8 | false 9 | $(CSharpVersion) 10 | 11 | 12 | 13 | OdataToEntity.Test.GraphQL.Program 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /test/OdataToEntity.Test.Asp/OdataToEntity.Test.AspServer/Startup.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Builder; 2 | using Microsoft.AspNetCore.Hosting; 3 | using Microsoft.Extensions.Configuration; 4 | using Microsoft.Extensions.DependencyInjection; 5 | using Microsoft.Extensions.Logging; 6 | using OdataToEntity.AspNetCore; 7 | using OdataToEntity.EfCore; 8 | using OdataToEntity.Test.Model; 9 | 10 | namespace OdataToEntity.AspServer 11 | { 12 | public class Startup 13 | { 14 | public Startup(IWebHostEnvironment env) 15 | { 16 | var builder = new ConfigurationBuilder() 17 | .SetBasePath(env.ContentRootPath) 18 | .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) 19 | .AddEnvironmentVariables(); 20 | Configuration = builder.Build(); 21 | } 22 | 23 | public IConfigurationRoot Configuration { get; } 24 | 25 | // This method gets called by the runtime. Use this method to add services to the container. 26 | public void ConfigureServices(IServiceCollection services) 27 | { 28 | services.AddLogging(loggingBuilder => 29 | { 30 | loggingBuilder.AddConsole(); 31 | loggingBuilder.AddDebug(); 32 | loggingBuilder.AddConfiguration(Configuration.GetSection("Logging")); 33 | }); 34 | } 35 | 36 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 37 | public void Configure(IApplicationBuilder app) 38 | { 39 | var dataAdapter = new OrderDataAdapter(true, true); 40 | app.UseOdataToEntityMiddleware("/api", dataAdapter.BuildEdmModelFromEfCoreModel()); 41 | } 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /source/OdataToEntity.EfCore/OeDbQueryAdapter.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | using Microsoft.EntityFrameworkCore.Metadata; 3 | using Microsoft.OData; 4 | using System; 5 | using System.Linq; 6 | 7 | namespace OdataToEntity.EfCore 8 | { 9 | internal sealed class OeDbQueryAdapter : Db.OeEntitySetAdapter, IFromSql 10 | { 11 | private readonly Type _clrEntityType; 12 | 13 | public OeDbQueryAdapter(Type clrEntityType, String entitySetName) 14 | { 15 | _clrEntityType = clrEntityType; 16 | EntitySetName = entitySetName; 17 | } 18 | 19 | public override void AddEntity(Object dataContext, ODataResourceBase entry) 20 | { 21 | throw new NotSupportedException(); 22 | } 23 | public override void AttachEntity(Object dataContext, ODataResourceBase entry) 24 | { 25 | throw new NotSupportedException(); 26 | } 27 | public IQueryable FromSql(Object dataContext, String sql, Object?[] parameters) 28 | { 29 | var dbContext = (DbContext)dataContext; 30 | IEntityType entityType = dbContext.Model.FindEntityType(EntityType); 31 | return OeDbSetAdapter.FromSql(dataContext, EntityType, entityType, sql, parameters); 32 | } 33 | public override IQueryable GetEntitySet(Object dataContext) 34 | { 35 | return OeDbSetAdapter.GetEntitySet(dataContext, EntityType); 36 | } 37 | public override void RemoveEntity(Object dataContext, ODataResourceBase entry) 38 | { 39 | throw new NotSupportedException(); 40 | } 41 | 42 | public override Type EntityType => _clrEntityType; 43 | public override String EntitySetName { get; } 44 | public override bool IsDbQuery => true; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /source/OdataToEntity.EfCore.DynamicDataContext/Types/DynamicDbContexts.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | using OdataToEntity.EfCore.DynamicDataContext.ModelBuilder; 3 | 4 | namespace OdataToEntity.EfCore.DynamicDataContext.Types 5 | { 6 | public sealed class DynamicDbContext1 : DynamicDbContext 7 | { 8 | public DynamicDbContext1(DbContextOptions options) 9 | : base(options) 10 | { 11 | } 12 | public DynamicDbContext1(DbContextOptions options, in DynamicModelBuilder dynamicModelBuilder) 13 | : base(options, dynamicModelBuilder) 14 | { 15 | } 16 | } 17 | 18 | public sealed class DynamicDbContext2 : DynamicDbContext 19 | { 20 | public DynamicDbContext2(DbContextOptions options) 21 | : base(options) 22 | { 23 | } 24 | public DynamicDbContext2(DbContextOptions options, in DynamicModelBuilder dynamicModelBuilder) 25 | : base(options, dynamicModelBuilder) 26 | { 27 | } 28 | } 29 | 30 | public sealed class DynamicDbContext3 : DynamicDbContext 31 | { 32 | public DynamicDbContext3(DbContextOptions options) 33 | : base(options) 34 | { 35 | } 36 | public DynamicDbContext3(DbContextOptions options, in DynamicModelBuilder dynamicModelBuilder) 37 | : base(options, dynamicModelBuilder) 38 | { 39 | } 40 | } 41 | 42 | public sealed class DynamicDbContext4 : DynamicDbContext 43 | { 44 | public DynamicDbContext4(DbContextOptions options) 45 | : base(options) 46 | { 47 | } 48 | public DynamicDbContext4(DbContextOptions options, in DynamicModelBuilder dynamicModelBuilder) 49 | : base(options, dynamicModelBuilder) 50 | { 51 | } 52 | } 53 | } -------------------------------------------------------------------------------- /test/OdataToEntity.Test.DynamicDataContext.AspServer/InformationSchemaMapping.json: -------------------------------------------------------------------------------- 1 | { 2 | "Operations": [ 3 | { 4 | "DbName": "dbo.GetOrders", 5 | "ResultTableDbName": "dbo.Orders" 6 | }, 7 | { 8 | "DbName": "dbo.TableFunction", 9 | "ResultTableDbName": "dbo.Orders" 10 | }, 11 | { 12 | "DbName": "dbo.TableFunctionWithParameters", 13 | "ResultTableDbName": "dbo.Orders" 14 | } 15 | ], 16 | "Tables": [ 17 | { 18 | "DbName": "dbo.Categories", 19 | "EdmName": "Categories" 20 | }, 21 | { 22 | "DbName": "dbo.Customers", 23 | "EdmName": "Customers", 24 | "Navigations": [ 25 | { 26 | "ConstraintName": "FK_Orders_AltCustomers", 27 | "NavigationName": "AltOrders" 28 | }, 29 | { 30 | "NavigationName": "ShippingAddresses", 31 | "ManyToManyTarget": "ShippingAddresses" 32 | } 33 | ] 34 | }, 35 | { 36 | "DbName": "dbo.CustomerShippingAddress", 37 | "EdmName": "CustomerShippingAddress" 38 | }, 39 | { 40 | "DbName": "dbo.ManyColumns", 41 | "EdmName": "ManyColumns" 42 | }, 43 | { 44 | "DbName": "dbo.Orders", 45 | "EdmName": "Orders", 46 | "Navigations": [ 47 | { 48 | "NavigationName": "Items", 49 | "TargetTableName": "dbo.OrderItems" 50 | }, 51 | { 52 | "ConstraintName": "FK_Orders_AltCustomers", 53 | "NavigationName": "AltCustomer" 54 | } 55 | ] 56 | }, 57 | { 58 | "DbName": "dbo.OrderItems", 59 | "EdmName": "OrderItems" 60 | }, 61 | { 62 | "DbName": "dbo.ShippingAddresses", 63 | "EdmName": "ShippingAddresses" 64 | }, 65 | { 66 | "DbName": "dbo.OrderItemsView", 67 | "EdmName": "OrderItemsView" 68 | } 69 | ] 70 | } 71 | -------------------------------------------------------------------------------- /source/OdataToEntity.AspNetCore/UriHelper.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Http; 2 | using System; 3 | 4 | namespace OdataToEntity.AspNetCore 5 | { 6 | internal static class UriHelper 7 | { 8 | public static Uri GetBaseUri(HttpRequest request) 9 | { 10 | var uriBuilder = new UriBuilder(request.Scheme, request.Host.Host, request.Host.Port.GetValueOrDefault()); 11 | if (request.PathBase.HasValue) 12 | uriBuilder.Path = request.PathBase; 13 | else 14 | { 15 | if (request.Path.Value != null && request.Path.Value.Length > 1) 16 | { 17 | int i = request.Path.Value.IndexOf('('); 18 | if (i == -1) 19 | { 20 | i = request.Path.Value.IndexOf("/$", StringComparison.Ordinal); 21 | if (i == -1) 22 | i = request.Path.Value.Length; 23 | else if (request.Path.Value.EndsWith("/$batch", StringComparison.OrdinalIgnoreCase)) 24 | i++; 25 | 26 | i = request.Path.Value.LastIndexOf('/', i - 1); 27 | } 28 | else 29 | i = request.Path.Value.IndexOf('/', 1); 30 | 31 | if (i > 1) 32 | uriBuilder.Path = request.Path.Value.Substring(1, i - 1); 33 | } 34 | } 35 | return uriBuilder.Uri; 36 | } 37 | public static Uri GetUri(HttpRequest request) 38 | { 39 | var path = request.PathBase.Add(request.Path); 40 | var uriBuilder = new UriBuilder(request.Scheme, request.Host.Host, request.Host.Port.GetValueOrDefault(), path, request.QueryString.Value); 41 | return uriBuilder.Uri; 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /test/OdataToEntity.Test.EfCore.SqlServer/OeEfCoreSqlServerDataAdapter.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | using System; 3 | using System.Collections; 4 | using System.Collections.Generic; 5 | using System.Data; 6 | 7 | namespace OdataToEntity.EfCore 8 | { 9 | public class OeEfCoreSqlServerDataAdapter : OeEfCoreDataAdapter where T : DbContext 10 | { 11 | private sealed class OeEfCoreSqlServerOperationAdapter : OeEfCoreOperationAdapter 12 | { 13 | public OeEfCoreSqlServerOperationAdapter(Type dataContextType) 14 | : base(dataContextType) 15 | { 16 | } 17 | 18 | protected override Object GetParameterCore(KeyValuePair parameter, String parameterName, int parameterIndex) 19 | { 20 | 21 | if (!(parameter.Value is String) && parameter.Value is IEnumerable list) 22 | { 23 | DataTable table = Infrastructure.OeDataTableHelper.GetDataTable(list); 24 | if (parameterName == null) 25 | parameterName = "@p" + parameterIndex.ToString(System.Globalization.CultureInfo.InvariantCulture); 26 | return new Microsoft.Data.SqlClient.SqlParameter(parameterName, table) { TypeName = parameter.Key }; 27 | } 28 | 29 | return parameter.Value; 30 | } 31 | } 32 | 33 | public OeEfCoreSqlServerDataAdapter() : this(null, null) 34 | { 35 | } 36 | public OeEfCoreSqlServerDataAdapter(Cache.OeQueryCache queryCache) : this(null, queryCache) 37 | { 38 | } 39 | public OeEfCoreSqlServerDataAdapter(DbContextOptions options, Cache.OeQueryCache queryCache) 40 | : base(options, queryCache, new OeEfCoreSqlServerOperationAdapter(typeof(T))) 41 | { 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /source/OdataToEntity.AspNetCore/OeMetadataController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using Microsoft.OData; 3 | using Microsoft.OData.Edm; 4 | using System.IO; 5 | using System.Threading.Tasks; 6 | 7 | namespace OdataToEntity.AspNetCore 8 | { 9 | public class OeMetadataController : ControllerBase 10 | { 11 | protected void WriteJsonSchema() 12 | { 13 | base.HttpContext.Response.ContentType = "application/schema+json"; 14 | WriteJsonSchema(base.HttpContext.GetEdmModel(), base.HttpContext.Response.Body); 15 | } 16 | private static void WriteJsonSchema(IEdmModel edmModel, Stream stream) 17 | { 18 | using (var memoryStream = new MemoryStream()) //kestrel allow only async operation 19 | { 20 | var schemaGenerator = new ModelBuilder.OeJsonSchemaGenerator(edmModel); 21 | schemaGenerator.Generate(memoryStream); 22 | memoryStream.Position = 0; 23 | memoryStream.CopyToAsync(stream); 24 | } 25 | } 26 | protected Task WriteMetadataAsync() 27 | { 28 | base.HttpContext.Response.ContentType = "application/xml"; 29 | return WriteMetadataAsync(base.HttpContext.GetEdmModel(), base.HttpContext.Response.Body); 30 | } 31 | private static async Task WriteMetadataAsync(IEdmModel edmModel, Stream stream) 32 | { 33 | var writerSettings = new ODataMessageWriterSettings(); 34 | writerSettings.EnableMessageStreamDisposal = false; 35 | IODataResponseMessage message = new Infrastructure.OeInMemoryMessage(stream, null); 36 | using (var writer = new ODataMessageWriter((IODataResponseMessageAsync)message, writerSettings, edmModel)) 37 | await writer.WriteMetadataDocumentAsync().ConfigureAwait(false); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /source/OdataToEntity.EfCore.DynamicDataContext/InformationSchema/MySqlSqlGenerationHelper.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore.Storage; 2 | using System; 3 | using System.Text; 4 | 5 | namespace OdataToEntity.EfCore.DynamicDataContext.InformationSchema 6 | { 7 | public class MySqlSqlGenerationHelper : RelationalSqlGenerationHelper 8 | { 9 | public MySqlSqlGenerationHelper(RelationalSqlGenerationHelperDependencies dependencies) 10 | : base(dependencies) 11 | { 12 | } 13 | 14 | public override String EscapeIdentifier(String identifier) 15 | { 16 | return identifier.Replace("`", "``"); 17 | } 18 | public override void EscapeIdentifier(StringBuilder builder, String identifier) 19 | { 20 | int length = builder.Length; 21 | builder.Append(identifier); 22 | builder.Replace("`", "``", length, identifier.Length); 23 | } 24 | public override String DelimitIdentifier(String identifier) 25 | { 26 | return "`" + EscapeIdentifier(identifier) + "`"; 27 | } 28 | public override void DelimitIdentifier(StringBuilder builder, String identifier) 29 | { 30 | builder.Append('`'); 31 | EscapeIdentifier(builder, identifier); 32 | builder.Append('`'); 33 | } 34 | public override String DelimitIdentifier(String name, String schema) 35 | { 36 | var builder = new StringBuilder(); 37 | DelimitIdentifier(builder, name, schema); 38 | return builder.ToString(); 39 | } 40 | public override void DelimitIdentifier(StringBuilder builder, String name, String schema) 41 | { 42 | builder.Append(schema); 43 | builder.Append('.'); 44 | DelimitIdentifier(builder, name); 45 | } 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /test/OdataToEntity.Test.DynamicDataContext.AspServer/OdataToEntity.Test.DynamicDataContext.AspServer.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Exe 6 | $(NetCoreAppVersion) 7 | $(CSharpVersion) 8 | enable 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | PreserveNewest 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | PreserveNewest 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /source/OdataToEntity.Ef6/DummyCommandBuilder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Data; 3 | using System.Data.Common; 4 | using System.Reflection; 5 | 6 | namespace OdataToEntity.Ef6 7 | { 8 | internal sealed class DummyCommandBuilder : DbCommandBuilder 9 | { 10 | private readonly Func _getParameterName; 11 | 12 | public DummyCommandBuilder(DbConnection connection) 13 | { 14 | DbProviderFactory providerFactory = DbProviderFactories.GetFactory(connection); 15 | DbCommandBuilder commandBuilder = providerFactory.CreateCommandBuilder(); 16 | Func func = GetParameterName; 17 | MethodInfo methodInfo = func.GetMethodInfo().GetBaseDefinition(); 18 | _getParameterName = (Func)Delegate.CreateDelegate(typeof(Func), commandBuilder, methodInfo); 19 | } 20 | 21 | public String GetDbParameterName(int parameterOrdinal) 22 | { 23 | return _getParameterName(parameterOrdinal); 24 | } 25 | protected override void ApplyParameterInfo(DbParameter parameter, DataRow row, StatementType statementType, bool whereClause) 26 | { 27 | throw new NotImplementedException(); 28 | } 29 | protected override String GetParameterName(String parameterName) 30 | { 31 | throw new NotImplementedException(); 32 | } 33 | protected override String GetParameterName(int parameterOrdinal) 34 | { 35 | throw new NotImplementedException(); 36 | } 37 | protected override String GetParameterPlaceholder(int parameterOrdinal) 38 | { 39 | throw new NotImplementedException(); 40 | } 41 | protected override void SetRowUpdatingHandler(DbDataAdapter adapter) 42 | { 43 | throw new NotImplementedException(); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /test/OdataToEntity.Test.InMemory/InMemoryOrderContext.cs: -------------------------------------------------------------------------------- 1 | using OdataToEntity.Test.Model; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | 6 | namespace OdataToEntity.Test.InMemory 7 | { 8 | public sealed class InMemoryOrderContext : IDisposable 9 | { 10 | void IDisposable.Dispose() { } 11 | 12 | public List Categories { get; } = new List(); 13 | public List Customers { get; } = new List(); 14 | public List CustomerShippingAddress { get; } = new List(); 15 | public List ManyColumns { get; } = new List(); 16 | public List ManyColumnsView { get; } = new List(); 17 | public List Orders { get; } = new List(); 18 | public List OrderItems { get; } = new List(); 19 | public IEnumerable OrderItemsView => 20 | Orders 21 | .GroupJoin(OrderItems, o => o.Id, i => i.OrderId, (outer, inner) => new { outer, inner }) 22 | .SelectMany(g => g.inner, (source, collection) => new { source.outer, collection }) 23 | .OrderBy(g => g.outer.Id) 24 | .ThenBy(g => (g.outer == null) ? 0 : g.outer.Id) 25 | .Select(g => new OrderItemsView() { Name = g.outer.Name, Product = g.collection?.Product }); 26 | public List ShippingAddresses { get; } = new List(); 27 | } 28 | 29 | public sealed class InMemoryOrder2Context : IDisposable 30 | { 31 | void IDisposable.Dispose() { } 32 | 33 | public List ManyColumns2 { get; } = new List(); 34 | public List Orders2 { get; } = new List(); 35 | public List Customer2 { get; } = new List(); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /test/OdataToEntity.Test.EfCore.PostgreSql/OeEfCorePostgreSqlDataAdapter.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | using System; 3 | using System.Collections; 4 | using System.Collections.Generic; 5 | 6 | namespace OdataToEntity.EfCore.Postgresql 7 | { 8 | public sealed class StringList 9 | { 10 | public String item { get; set; } 11 | } 12 | 13 | public class EfCorePostgreSqlDataAdapter : OeEfCorePostgreSqlDataAdapter where T : DbContext 14 | { 15 | private sealed class EfCorePostgreSqlOperationAdapter : OePostgreSqlEfCoreOperationAdapter 16 | { 17 | public EfCorePostgreSqlOperationAdapter(Type dataContextType) : base(dataContextType) 18 | { 19 | } 20 | 21 | protected override Object GetParameterCore(KeyValuePair parameter, String parameterName, int parameterIndex) 22 | { 23 | if (!(parameter.Value is String) && parameter.Value is IEnumerable list) 24 | { 25 | var stringListList = new List(); 26 | foreach (String item in list) 27 | stringListList.Add(new StringList() { item = item }); 28 | 29 | if (parameterName == null) 30 | parameterName = "p" + parameterIndex.ToString(System.Globalization.CultureInfo.InvariantCulture); 31 | return new Npgsql.NpgsqlParameter(parameterName, stringListList); 32 | } 33 | 34 | return parameter.Value; 35 | } 36 | } 37 | 38 | public EfCorePostgreSqlDataAdapter() : this(null, null) 39 | { 40 | } 41 | public EfCorePostgreSqlDataAdapter(DbContextOptions options, Cache.OeQueryCache queryCache) 42 | : base(options, queryCache, new EfCorePostgreSqlOperationAdapter(typeof(T))) 43 | { 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /test/OdataToEntity.Test.Asp/OdataToEntity.Test.AspMvcServer/Startup.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Builder; 2 | using Microsoft.AspNetCore.Hosting; 3 | using Microsoft.Extensions.Configuration; 4 | using Microsoft.Extensions.DependencyInjection; 5 | using Microsoft.Extensions.Logging; 6 | using OdataToEntity.AspNetCore; 7 | using OdataToEntity.EfCore; 8 | using OdataToEntity.Test.Model; 9 | 10 | namespace OdataToEntity.Test.AspMvcServer 11 | { 12 | public class Startup 13 | { 14 | public Startup(IWebHostEnvironment env) 15 | { 16 | var builder = new ConfigurationBuilder() 17 | .SetBasePath(env.ContentRootPath) 18 | .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) 19 | .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true) 20 | .AddEnvironmentVariables(); 21 | Configuration = builder.Build(); 22 | } 23 | 24 | public IConfigurationRoot Configuration { get; } 25 | 26 | // This method gets called by the runtime. Use this method to add services to the container. 27 | public void ConfigureServices(IServiceCollection services) 28 | { 29 | var dataAdapter = new OrderDataAdapter(true, true); 30 | services.AddOdataToEntityMvc(dataAdapter.BuildEdmModelFromEfCoreModel()); 31 | 32 | services.AddLogging(loggingBuilder => 33 | { 34 | loggingBuilder.AddConsole(); 35 | loggingBuilder.AddDebug(); 36 | loggingBuilder.AddConfiguration(Configuration.GetSection("Logging")); 37 | }); 38 | } 39 | 40 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 41 | public void Configure(IApplicationBuilder app) 42 | { 43 | app.UseRouting(); 44 | app.UseEndpoints(b => b.MapControllers()); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /source/OdataToEntity.EfCore/OdataToEntity.EfCore.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Classes bridge from ODataToEntity to Entity Framework Core. Abstraction layer access to DataContext Entity Framework Core. 6 | Maxim Voronov 7 | $(NetStandardVersion) 8 | AnyCPU 9 | portable 10 | OdataToEntity.EfCore 11 | $(OdataToEntityVersion) 12 | Entity Framework Core adapter for OdataEntity 13 | $(CSharpVersion) 14 | enable 15 | 16 | 17 | 18 | MIT 19 | $(Description) 20 | $(Title) 21 | $(OdataToEntityVersion) 22 | 23 | 24 | 25 | OdataToEntity.EfCore 26 | odata;data;services;odatatoentity;ef;entity framework core;entity;core 27 | https://github.com/voronov-maxim/OdataToEntity 28 | $(OdataToEntityVersion) 29 | 30 | 31 | 32 | EF1001 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /test/OdataToEntity.Test.GraphQL/DbFixture.cs: -------------------------------------------------------------------------------- 1 | using GraphQL; 2 | using GraphQL.Types; 3 | using Microsoft.OData.Edm; 4 | using Microsoft.OData.Edm.Csdl; 5 | using OdataToEntity.EfCore; 6 | using OdataToEntity.GraphQL; 7 | using System; 8 | using System.IO; 9 | using System.Text; 10 | using System.Threading.Tasks; 11 | using System.Xml; 12 | 13 | namespace OdataToEntity.Test.GraphQL 14 | { 15 | public abstract class DbFixture : IDisposable 16 | { 17 | private readonly OeGraphqlParser _graphqlParser; 18 | 19 | protected DbFixture(Db.OeDataAdapter dataAdapter1, Db.OeDataAdapter dataAdapter2) 20 | { 21 | EdmModel refModel = dataAdapter2.BuildEdmModelFromEfCoreModel(); 22 | EdmModel = dataAdapter1.BuildEdmModelFromEfCoreModel(refModel); 23 | 24 | Schema = new OeSchemaBuilder(EdmModel).Build(); 25 | _graphqlParser = new OeGraphqlParser(EdmModel); 26 | } 27 | 28 | public void Dispose() 29 | { 30 | Schema.Dispose(); 31 | } 32 | public Task Execute(String query) 33 | { 34 | return Execute(query, null); 35 | } 36 | public async Task Execute(String query, Inputs inputs) 37 | { 38 | return await (await _graphqlParser.Execute(query, inputs)).ToStringAsync(); 39 | } 40 | public static String GetCsdlSchema(IEdmModel edmModel) 41 | { 42 | using (var stream = new MemoryStream()) 43 | { 44 | using (XmlWriter xmlWriter = XmlWriter.Create(stream, new XmlWriterSettings() { Indent = true })) 45 | { 46 | if (!CsdlWriter.TryWriteCsdl(edmModel, xmlWriter, CsdlTarget.OData, out _)) 47 | return null; 48 | } 49 | 50 | return Encoding.UTF8.GetString(stream.ToArray()); 51 | } 52 | } 53 | 54 | protected EdmModel EdmModel { get; } 55 | protected Schema Schema { get; } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /source/OdataToEntity/InMemory/InMemoryExecutor.cs: -------------------------------------------------------------------------------- 1 | using OdataToEntity.Cache; 2 | using System; 3 | using System.Collections; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Threading; 7 | 8 | namespace OdataToEntity.InMemory 9 | { 10 | public sealed class InMemoryExecutor : IEnumerable, IEnumerator, IDisposable 11 | { 12 | private IEnumerator? _enumerator; 13 | private readonly SemaphoreSlim _mutex; 14 | private readonly String[] _parameterNames; 15 | private readonly Object?[] _parameters; 16 | private readonly Func _query; 17 | 18 | public InMemoryExecutor(Func query, IReadOnlyList parameterValues, Object?[] parameters) 19 | { 20 | _query = query; 21 | _parameterNames = parameterValues.Select(p => p.ParameterName).ToArray(); 22 | _parameters = parameters; 23 | 24 | _mutex = new SemaphoreSlim(1, 1); 25 | } 26 | 27 | public void Dispose() 28 | { 29 | _mutex.Release(); 30 | } 31 | public IEnumerator GetEnumerator() 32 | { 33 | _enumerator = _query().GetEnumerator(); 34 | return this; 35 | } 36 | public bool MoveNext() 37 | { 38 | return _enumerator!.MoveNext(); 39 | } 40 | public void Reset() 41 | { 42 | _enumerator!.Reset(); 43 | } 44 | public void SetDataContext(Object dataContext) 45 | { 46 | _parameters[0] = dataContext; 47 | } 48 | public void Wait() 49 | { 50 | _mutex.Wait(); 51 | } 52 | 53 | public Object Current => _enumerator!.Current; 54 | public Object? this[String parameterName] 55 | { 56 | get => _parameters[Array.IndexOf(_parameterNames, parameterName) + 1]; 57 | set => _parameters[Array.IndexOf(_parameterNames, parameterName) + 1] = value; 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /source/OdataToEntity/OdataToEntity.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Classes to serialize, deserialize OData JSON payloads. Supports OData v4 only. Enables construction of OData services via Entity Framework (Linq2Db) DataContext (with/without controller). 6 | Maxim Voronov 7 | $(NetStandardVersion);$(FrameworkVersion) 8 | AnyCPU 9 | portable 10 | OdataToEntity 11 | OData via Entity Framework (Linq2Db) 12 | $(CSharpVersion) 13 | enable 14 | 15 | 16 | 17 | MIT 18 | $(Description) 19 | $(Title) 20 | $(OdataToEntityVersion) 21 | 22 | 23 | 24 | OdataToEntity 25 | odata;data;services;odatalib;edmlib;ef;entity framework;linq2db;entity;core 26 | https://github.com/voronov-maxim/OdataToEntity 27 | $(OdataToEntityVersion) 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /source/OdataToEntity/Parsers/Visitors/SelectNullableVisitor.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq.Expressions; 3 | using System.Reflection; 4 | 5 | namespace OdataToEntity.Parsers 6 | { 7 | public sealed class SelectNullableVisitor : ExpressionVisitor 8 | { 9 | protected override Expression VisitMember(MemberExpression node) 10 | { 11 | if (node.Expression is UnaryExpression unaryExpression && unaryExpression.NodeType == ExpressionType.Convert) 12 | { 13 | Type? underlyingType = Nullable.GetUnderlyingType(unaryExpression.Operand.Type); 14 | if (underlyingType != null) 15 | { 16 | MethodInfo methodInfo = unaryExpression.Operand.Type.GetMethod(nameof(Nullable.GetValueOrDefault), Type.EmptyTypes)!; 17 | MethodCallExpression getValueOrDefaultExpression = Expression.Call(unaryExpression.Operand, methodInfo); 18 | return Expression.MakeMemberAccess(getValueOrDefaultExpression, node.Member); 19 | } 20 | } 21 | return node; 22 | } 23 | protected override Expression VisitMethodCall(MethodCallExpression node) 24 | { 25 | if (node.Method.IsStatic && node.Arguments.Count == 1 && node.Arguments[0].NodeType == ExpressionType.Convert) 26 | { 27 | var unaryExpression = (UnaryExpression)node.Arguments[0]; 28 | Type? underlyingType = Nullable.GetUnderlyingType(unaryExpression.Operand.Type); 29 | if (underlyingType != null) 30 | { 31 | MethodInfo methodInfo = unaryExpression.Operand.Type.GetMethod(nameof(Nullable.GetValueOrDefault), Type.EmptyTypes)!; 32 | MethodCallExpression getValueOrDefaultExpression = Expression.Call(unaryExpression.Operand, methodInfo); 33 | return node.Update(node.Object!, new[] { getValueOrDefaultExpression }); 34 | } 35 | } 36 | return node; 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /source/OdataToEntity/Db/OeEntitySetAdapter.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.OData; 2 | using Microsoft.OData.Edm; 3 | using System; 4 | using System.Collections.ObjectModel; 5 | using System.Linq; 6 | 7 | namespace OdataToEntity.Db 8 | { 9 | public abstract class OeEntitySetAdapter 10 | { 11 | public abstract void AddEntity(Object dataContext, ODataResourceBase entity); 12 | public abstract void AttachEntity(Object dataContext, ODataResourceBase entity); 13 | public abstract IQueryable GetEntitySet(Object dataContext); 14 | public abstract void RemoveEntity(Object dataContext, ODataResourceBase entity); 15 | public virtual void UpdateEntityAfterSave(Object dataContext, ODataResourceBase entity) { } 16 | 17 | public abstract Type EntityType { get; } 18 | public abstract String EntitySetName { get; } 19 | public virtual bool IsDbQuery => false; 20 | } 21 | 22 | public sealed class OeEntitySetAdapterCollection : ReadOnlyCollection 23 | { 24 | public OeEntitySetAdapterCollection(OeEntitySetAdapter[] entitySetAdapters) : base(entitySetAdapters) 25 | { 26 | } 27 | 28 | public OeEntitySetAdapter? Find(Type clrType) 29 | { 30 | var entitySetAdapters = (OeEntitySetAdapter[])base.Items; 31 | foreach (OeEntitySetAdapter entitySetAdapter in entitySetAdapters) 32 | if (entitySetAdapter.EntityType == clrType) 33 | return entitySetAdapter; 34 | 35 | return null; 36 | } 37 | public OeEntitySetAdapter Find(IEdmEntitySet entitySet) 38 | { 39 | var entitySetAdapters = (OeEntitySetAdapter[])base.Items; 40 | foreach (OeEntitySetAdapter entitySetAdapter in entitySetAdapters) 41 | if (String.Compare(entitySetAdapter.EntitySetName, entitySet.Name, StringComparison.OrdinalIgnoreCase) == 0) 42 | return entitySetAdapter; 43 | 44 | throw new InvalidOperationException("EntitySet " + entitySet.Name + " not found"); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /source/OdataToEntity/Infrastructure/OeDataTableHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Data; 5 | using System.Linq; 6 | using System.Reflection; 7 | 8 | namespace OdataToEntity.Infrastructure 9 | { 10 | public static class OeDataTableHelper 11 | { 12 | public static DataTable GetDataTable(IEnumerable source) 13 | { 14 | Type itemType; 15 | if (source is IEnumerable list) 16 | itemType = list.First().GetType(); 17 | else 18 | itemType = Parsers.OeExpressionHelper.GetCollectionItemType(source.GetType()); 19 | 20 | if (Parsers.OeExpressionHelper.IsPrimitiveType(itemType)) 21 | return GetPrimitiveDataTable(itemType, source); 22 | 23 | PropertyInfo[] properties = itemType.GetProperties(); 24 | var table = new DataTable(); 25 | foreach (PropertyInfo property in properties) 26 | table.Columns.Add(property.Name, property.PropertyType); 27 | Object?[] values = new Object[properties.Length]; 28 | 29 | table.BeginLoadData(); 30 | foreach (Object item in source) 31 | { 32 | for (int i = 0; i < properties.Length; i++) 33 | values[i] = properties[i].GetValue(item, null); 34 | table.LoadDataRow(values, true); 35 | } 36 | table.EndLoadData(); 37 | 38 | return table; 39 | } 40 | private static DataTable GetPrimitiveDataTable(Type itemType, IEnumerable source) 41 | { 42 | var table = new DataTable(); 43 | table.Columns.Add("value", itemType); 44 | var values = new Object[1]; 45 | 46 | table.BeginLoadData(); 47 | foreach (Object item in source) 48 | { 49 | values[0] = item; 50 | table.LoadDataRow(values, true); 51 | } 52 | table.EndLoadData(); 53 | 54 | return table; 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /test/OdataToEntity.Test.Asp/OdataToEntity.Test.AspMvcServer/Controllers/ManyColumnsController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Http; 2 | using Microsoft.AspNetCore.Mvc; 3 | using OdataToEntity.AspNetCore; 4 | using System; 5 | using System.Collections.Generic; 6 | 7 | namespace OdataToEntity.Test.AspMvcServer.Controllers 8 | { 9 | [Route("api/[controller]")] 10 | public sealed class ManyColumnsController 11 | { 12 | private readonly IHttpContextAccessor _httpContextAccessor; 13 | 14 | public ManyColumnsController(IHttpContextAccessor httpContextAccessor) 15 | { 16 | _httpContextAccessor = httpContextAccessor; 17 | } 18 | 19 | [HttpDelete("{id}")] 20 | public void Delete(OeDataContext dataContext, Model.ManyColumns manyColumns) 21 | { 22 | dataContext.Update(manyColumns); 23 | } 24 | [HttpGet] 25 | public ODataResult Get() 26 | { 27 | Query.OeModelBoundProvider modelBoundProvider = _httpContextAccessor.HttpContext.CreateModelBoundProvider(); 28 | var parser = new OeAspQueryParser(_httpContextAccessor.HttpContext, modelBoundProvider); 29 | IAsyncEnumerable manyColumns = parser.ExecuteReader(); 30 | return parser.OData(manyColumns); 31 | } 32 | [HttpGet("{id}")] 33 | public ODataResult Get(int id) 34 | { 35 | var parser = new OeAspQueryParser(_httpContextAccessor.HttpContext); 36 | IAsyncEnumerable manyColumns = parser.ExecuteReader(); 37 | return parser.OData(manyColumns); 38 | } 39 | [HttpPatch] 40 | public void Patch(OeDataContext dataContext, IDictionary manyColumnsProperties) 41 | { 42 | dataContext.Update(manyColumnsProperties); 43 | } 44 | [HttpPost] 45 | public void Post(OeDataContext dataContext, Model.ManyColumns manyColumns) 46 | { 47 | dataContext.Update(manyColumns); 48 | } 49 | } 50 | } -------------------------------------------------------------------------------- /test/OdataToEntity.Test.Asp/OdataToEntity.Test.AspMvcServer/OdataToEntity.Test.AspMvcServer.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Test Asp Mvc sertver for OdataToEntity 6 | Test Asp Mvc server for OdataToEntity 7 | Maxim Voronov 8 | $(NetCoreAppVersion) 9 | AnyCPU 10 | portable 11 | OdataToEntity.Test.AspMvcServer 12 | false 13 | 14 | 15 | 16 | Exe 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | PreserveNewest 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /source/OdataToEntity.Linq2Db/SetBuilder.cs: -------------------------------------------------------------------------------- 1 | using LinqToDB; 2 | using LinqToDB.Linq; 3 | using System; 4 | using System.Linq; 5 | using System.Linq.Expressions; 6 | using System.Reflection; 7 | 8 | namespace OdataToEntity.Linq2Db 9 | { 10 | public abstract class SetBuilder 11 | { 12 | public SetBuilder(PropertyInfo updatedProperty) 13 | { 14 | UpdatedProperty = updatedProperty; 15 | } 16 | 17 | public override bool Equals(Object? obj) 18 | { 19 | if (obj is SetBuilder setBuilder) 20 | return UpdatedProperty == setBuilder.UpdatedProperty; 21 | return false; 22 | } 23 | public override int GetHashCode() => UpdatedProperty.GetHashCode(); 24 | public abstract IUpdatable GetSet(IQueryable source, T entity); 25 | public abstract IUpdatable GetSet(IUpdatable source, T entity); 26 | 27 | protected PropertyInfo UpdatedProperty { get; } 28 | } 29 | 30 | public sealed class SetBuilder : SetBuilder 31 | { 32 | private readonly Expression> _extract; 33 | 34 | public SetBuilder(PropertyInfo updatedProperty) : base(updatedProperty) 35 | { 36 | ParameterExpression parameter = Expression.Parameter(typeof(T)); 37 | MemberExpression propertyExpression = Expression.Property(parameter, updatedProperty); 38 | _extract = Expression.Lambda>(propertyExpression, parameter); 39 | } 40 | 41 | public override IUpdatable GetSet(IQueryable source, T entity) 42 | { 43 | MemberExpression valueExpression = Expression.Property(Expression.Constant(entity), base.UpdatedProperty); 44 | return source.Set(_extract, Expression.Lambda>(valueExpression)); 45 | } 46 | public override IUpdatable GetSet(IUpdatable source, T entity) 47 | { 48 | MemberExpression valueExpression = Expression.Property(Expression.Constant(entity), base.UpdatedProperty); 49 | return source.Set(_extract, Expression.Lambda>(valueExpression)); 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /test/OdataToEntity.Test/OdataToEntity.Test.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Test Ef core in memory for OdataToEntity 6 | Test Ef core in memory for OdataToEntity 7 | Maxim Voronov 8 | $(NetCoreAppVersion) 9 | AnyCPU 10 | portable 11 | OdataToEntity.Test 12 | false 13 | $(CSharpVersion) 14 | 15 | 16 | 17 | OdataToEntity.Test.Program 18 | 19 | 20 | 21 | IGNORE_RDBNull 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | PreserveNewest 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /sln/OdataToEntity.Test.EfCore.SqlServer.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.26228.4 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OdataToEntity", "..\source\OdataToEntity\OdataToEntity.csproj", "{70ED8CE4-4FA6-418A-A5BC-8697F5CA0B03}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OdataToEntity.EfCore", "..\source\OdataToEntity.EfCore\OdataToEntity.EfCore.csproj", "{94E8BF46-2821-4890-9E07-D1FA5C797C92}" 9 | EndProject 10 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OdataToEntity.Test.EfCore.SqlServer", "..\test\OdataToEntity.Test.EfCore.SqlServer\OdataToEntity.Test.EfCore.SqlServer.csproj", "{654912C0-BED9-4FB1-B1E3-8AF7F3D72B78}" 11 | EndProject 12 | Global 13 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 14 | Debug|Any CPU = Debug|Any CPU 15 | Release|Any CPU = Release|Any CPU 16 | EndGlobalSection 17 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 18 | {70ED8CE4-4FA6-418A-A5BC-8697F5CA0B03}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 19 | {70ED8CE4-4FA6-418A-A5BC-8697F5CA0B03}.Debug|Any CPU.Build.0 = Debug|Any CPU 20 | {70ED8CE4-4FA6-418A-A5BC-8697F5CA0B03}.Release|Any CPU.ActiveCfg = Release|Any CPU 21 | {70ED8CE4-4FA6-418A-A5BC-8697F5CA0B03}.Release|Any CPU.Build.0 = Release|Any CPU 22 | {94E8BF46-2821-4890-9E07-D1FA5C797C92}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 23 | {94E8BF46-2821-4890-9E07-D1FA5C797C92}.Debug|Any CPU.Build.0 = Debug|Any CPU 24 | {94E8BF46-2821-4890-9E07-D1FA5C797C92}.Release|Any CPU.ActiveCfg = Release|Any CPU 25 | {94E8BF46-2821-4890-9E07-D1FA5C797C92}.Release|Any CPU.Build.0 = Release|Any CPU 26 | {654912C0-BED9-4FB1-B1E3-8AF7F3D72B78}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 27 | {654912C0-BED9-4FB1-B1E3-8AF7F3D72B78}.Debug|Any CPU.Build.0 = Debug|Any CPU 28 | {654912C0-BED9-4FB1-B1E3-8AF7F3D72B78}.Release|Any CPU.ActiveCfg = Release|Any CPU 29 | {654912C0-BED9-4FB1-B1E3-8AF7F3D72B78}.Release|Any CPU.Build.0 = Release|Any CPU 30 | EndGlobalSection 31 | GlobalSection(SolutionProperties) = preSolution 32 | HideSolutionNode = FALSE 33 | EndGlobalSection 34 | EndGlobal 35 | -------------------------------------------------------------------------------- /test/OdataToEntity.Test.Asp/OdataToEntity.Test.AspMvcServer/Controllers/CategoriesController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Http; 2 | using Microsoft.AspNetCore.Mvc; 3 | using OdataToEntity.AspNetCore; 4 | using System; 5 | using System.Collections.Generic; 6 | 7 | namespace OdataToEntity.Test.AspMvcServer.Controllers 8 | { 9 | [Route("api/[controller]")] 10 | public sealed class CategoriesController 11 | { 12 | private readonly IHttpContextAccessor _httpContextAccessor; 13 | 14 | public CategoriesController(IHttpContextAccessor httpContextAccessor) 15 | { 16 | _httpContextAccessor = httpContextAccessor; 17 | } 18 | 19 | [HttpDelete("{id}")] 20 | public void Delete(OeDataContext dataContext, Model.Category category) 21 | { 22 | dataContext.Update(category); 23 | } 24 | [HttpGet] 25 | public ODataResult Get() 26 | { 27 | Query.OeModelBoundProvider modelBoundProvider = _httpContextAccessor.HttpContext.CreateModelBoundProvider(); 28 | var parser = new OeAspQueryParser(_httpContextAccessor.HttpContext, modelBoundProvider); 29 | IAsyncEnumerable categories = parser.ExecuteReader(); 30 | return parser.OData(categories); 31 | } 32 | [HttpGet("{id}")] 33 | public ODataResult Get(int id) 34 | { 35 | Query.OeModelBoundProvider modelBoundProvider = _httpContextAccessor.HttpContext.CreateModelBoundProvider(); 36 | var parser = new OeAspQueryParser(_httpContextAccessor.HttpContext, modelBoundProvider); 37 | IAsyncEnumerable categories = parser.ExecuteReader(); 38 | return parser.OData(categories); 39 | } 40 | [HttpPatch] 41 | public void Patch(OeDataContext dataContext, IDictionary categoryProperties) 42 | { 43 | dataContext.Update(categoryProperties); 44 | } 45 | [HttpPost] 46 | public void Post(OeDataContext dataContext, Model.Category category) 47 | { 48 | dataContext.Update(category); 49 | } 50 | } 51 | } -------------------------------------------------------------------------------- /source/OdataToEntity/Parsers/Visitors/OeConstantToParameterVisitor.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.OData.UriParser; 2 | using OdataToEntity.Cache; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Globalization; 6 | using System.Linq.Expressions; 7 | 8 | namespace OdataToEntity.Parsers 9 | { 10 | public class OeConstantToParameterVisitor : OeConstantToVariableVisitor 11 | { 12 | private readonly Dictionary _constantToParameterMapper; 13 | private OeQueryCacheDbParameterValue[]? _parameterValues; 14 | 15 | public OeConstantToParameterVisitor() 16 | { 17 | _constantToParameterMapper = new Dictionary(); 18 | } 19 | 20 | protected override IReadOnlyList TranslateParameters( 21 | IReadOnlyList constantExpressions, 22 | IReadOnlyDictionary constantsMappings) 23 | { 24 | var parameters = new ParameterExpression[constantExpressions.Count]; 25 | _parameterValues = new OeQueryCacheDbParameterValue[constantExpressions.Count]; 26 | for (int i = 0; i < constantExpressions.Count; i++) 27 | { 28 | ConstantExpression constantExpression = constantExpressions[i]; 29 | String parameterName = "__p_" + i.ToString(CultureInfo.InvariantCulture); 30 | 31 | ConstantNode constantNode = constantsMappings[constantExpression]; 32 | _constantToParameterMapper.Add(constantNode, new OeQueryCacheDbParameterDefinition(parameterName, constantExpression.Type)); 33 | 34 | _parameterValues[i] = new OeQueryCacheDbParameterValue(parameterName, constantExpression.Value); 35 | parameters[i] = Expression.Parameter(constantExpression.Type, parameterName); 36 | } 37 | return parameters; 38 | } 39 | 40 | public IReadOnlyDictionary ConstantToParameterMapper => _constantToParameterMapper; 41 | public IReadOnlyList ParameterValues => _parameterValues ?? Array.Empty(); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /sln/OdataToEntity.Test.InMemory.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.30907.101 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OdataToEntity.Test.InMemory", "..\test\OdataToEntity.Test.InMemory\OdataToEntity.Test.InMemory.csproj", "{BF0FAA07-1DA7-4CFC-9865-61CD21478E7F}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OdataToEntity", "..\source\OdataToEntity\OdataToEntity.csproj", "{9F27CEAC-3CBC-462C-BBDB-877F52668C37}" 9 | EndProject 10 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OdataToEntity.EfCore", "..\source\OdataToEntity.EfCore\OdataToEntity.EfCore.csproj", "{23CAD1DD-8025-4CB5-BB0D-EA21D9F19C42}" 11 | EndProject 12 | Global 13 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 14 | Debug|Any CPU = Debug|Any CPU 15 | Release|Any CPU = Release|Any CPU 16 | EndGlobalSection 17 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 18 | {BF0FAA07-1DA7-4CFC-9865-61CD21478E7F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 19 | {BF0FAA07-1DA7-4CFC-9865-61CD21478E7F}.Debug|Any CPU.Build.0 = Debug|Any CPU 20 | {BF0FAA07-1DA7-4CFC-9865-61CD21478E7F}.Release|Any CPU.ActiveCfg = Release|Any CPU 21 | {BF0FAA07-1DA7-4CFC-9865-61CD21478E7F}.Release|Any CPU.Build.0 = Release|Any CPU 22 | {9F27CEAC-3CBC-462C-BBDB-877F52668C37}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 23 | {9F27CEAC-3CBC-462C-BBDB-877F52668C37}.Debug|Any CPU.Build.0 = Debug|Any CPU 24 | {9F27CEAC-3CBC-462C-BBDB-877F52668C37}.Release|Any CPU.ActiveCfg = Release|Any CPU 25 | {9F27CEAC-3CBC-462C-BBDB-877F52668C37}.Release|Any CPU.Build.0 = Release|Any CPU 26 | {23CAD1DD-8025-4CB5-BB0D-EA21D9F19C42}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 27 | {23CAD1DD-8025-4CB5-BB0D-EA21D9F19C42}.Debug|Any CPU.Build.0 = Debug|Any CPU 28 | {23CAD1DD-8025-4CB5-BB0D-EA21D9F19C42}.Release|Any CPU.ActiveCfg = Release|Any CPU 29 | {23CAD1DD-8025-4CB5-BB0D-EA21D9F19C42}.Release|Any CPU.Build.0 = Release|Any CPU 30 | EndGlobalSection 31 | GlobalSection(SolutionProperties) = preSolution 32 | HideSolutionNode = FALSE 33 | EndGlobalSection 34 | GlobalSection(ExtensibilityGlobals) = postSolution 35 | SolutionGuid = {53232889-3289-4315-A459-AE597E6910F2} 36 | EndGlobalSection 37 | EndGlobal 38 | -------------------------------------------------------------------------------- /source/OdataToEntity.EfCore.DynamicDataContext/InformationSchema/ProviderSpecificSchema.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | using Microsoft.EntityFrameworkCore.Internal; 3 | using OdataToEntity.EfCore.DynamicDataContext.ModelBuilder; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq.Expressions; 7 | 8 | namespace OdataToEntity.EfCore.DynamicDataContext.InformationSchema 9 | { 10 | public abstract class ProviderSpecificSchema : IDisposable 11 | { 12 | private readonly DbContextPool _schemaContextPool; 13 | 14 | protected ProviderSpecificSchema(DbContextOptions dynamicDbContextOptions, DbContextPool schemaContextPool) 15 | { 16 | DynamicDbContextOptions = dynamicDbContextOptions; 17 | _schemaContextPool = schemaContextPool; 18 | } 19 | 20 | public DynamicMetadataProvider CreateMetadataProvider() 21 | { 22 | return CreateMetadataProvider(new InformationSchemaSettings()); 23 | } 24 | public DynamicMetadataProvider CreateMetadataProvider(InformationSchemaSettings informationSchemaSettings) 25 | { 26 | DefaultSchema = informationSchemaSettings.DefaultSchema; 27 | return new DynamicMetadataProvider(this, informationSchemaSettings); 28 | } 29 | public void Dispose() 30 | { 31 | _schemaContextPool.Dispose(); 32 | } 33 | public abstract Type? GetColumnClrType(String dataType); 34 | public abstract IReadOnlyList GetDbGeneratedColumns(); 35 | public abstract String GetParameterName(String parameterName); 36 | public SchemaContext GetSchemaContext() 37 | { 38 | return (SchemaContext)new DbContextLease(_schemaContextPool, true).Context; 39 | } 40 | 41 | public String? DefaultSchema { get; private set; } 42 | public bool IsCaseSensitive { get; protected set; } 43 | public DbContextOptions DynamicDbContextOptions { get; } 44 | public ExpressionVisitor? ExpressionVisitor { get; protected set; } 45 | public bool IsDatabaseNullHighestValue { get; protected set; } 46 | public OeEfCoreOperationAdapter OperationAdapter { get; protected set; } = null!; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /sln/OdataToEntity.Test.EfCore.PostgreSql.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.26730.15 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OdataToEntity.Test.EfCore.PostgreSql", "..\test\OdataToEntity.Test.EfCore.PostgreSql\OdataToEntity.Test.EfCore.PostgreSql.csproj", "{5942E850-0AF2-4DFE-8839-0B69F6DD376B}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OdataToEntity.EfCore", "..\source\OdataToEntity.EfCore\OdataToEntity.EfCore.csproj", "{212CF837-3F9F-44F5-877B-1164FFB5CCE5}" 9 | EndProject 10 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OdataToEntity", "..\source\OdataToEntity\OdataToEntity.csproj", "{38424859-C27B-4835-A260-D94869C0B9E9}" 11 | EndProject 12 | Global 13 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 14 | Debug|Any CPU = Debug|Any CPU 15 | Release|Any CPU = Release|Any CPU 16 | EndGlobalSection 17 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 18 | {5942E850-0AF2-4DFE-8839-0B69F6DD376B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 19 | {5942E850-0AF2-4DFE-8839-0B69F6DD376B}.Debug|Any CPU.Build.0 = Debug|Any CPU 20 | {5942E850-0AF2-4DFE-8839-0B69F6DD376B}.Release|Any CPU.ActiveCfg = Release|Any CPU 21 | {5942E850-0AF2-4DFE-8839-0B69F6DD376B}.Release|Any CPU.Build.0 = Release|Any CPU 22 | {212CF837-3F9F-44F5-877B-1164FFB5CCE5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 23 | {212CF837-3F9F-44F5-877B-1164FFB5CCE5}.Debug|Any CPU.Build.0 = Debug|Any CPU 24 | {212CF837-3F9F-44F5-877B-1164FFB5CCE5}.Release|Any CPU.ActiveCfg = Release|Any CPU 25 | {212CF837-3F9F-44F5-877B-1164FFB5CCE5}.Release|Any CPU.Build.0 = Release|Any CPU 26 | {38424859-C27B-4835-A260-D94869C0B9E9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 27 | {38424859-C27B-4835-A260-D94869C0B9E9}.Debug|Any CPU.Build.0 = Debug|Any CPU 28 | {38424859-C27B-4835-A260-D94869C0B9E9}.Release|Any CPU.ActiveCfg = Release|Any CPU 29 | {38424859-C27B-4835-A260-D94869C0B9E9}.Release|Any CPU.Build.0 = Release|Any CPU 30 | EndGlobalSection 31 | GlobalSection(SolutionProperties) = preSolution 32 | HideSolutionNode = FALSE 33 | EndGlobalSection 34 | GlobalSection(ExtensibilityGlobals) = postSolution 35 | SolutionGuid = {DBD539EA-039A-43EE-A338-409F22BCBD20} 36 | EndGlobalSection 37 | EndGlobal 38 | -------------------------------------------------------------------------------- /source/OdataToEntity/Parsers/OeEnumerableStub.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.OData.Edm; 2 | using System; 3 | using System.Collections; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Linq.Expressions; 7 | using System.Reflection; 8 | 9 | namespace OdataToEntity.Parsers 10 | { 11 | public class OeEnumerableStub 12 | { 13 | private sealed class OeEnumerableStubImpl : OeEnumerableStub, IEnumerable, IQueryable 14 | { 15 | private OeEnumerableStubImpl(IEdmEntitySet entitySet) : base(typeof(T), entitySet) 16 | { 17 | } 18 | 19 | public static OeEnumerableStub Create(IEdmEntitySet entitySet) 20 | { 21 | return new OeEnumerableStubImpl(entitySet); 22 | } 23 | public IEnumerator GetEnumerator() 24 | { 25 | throw new NotSupportedException("IEnumerable stub source"); 26 | } 27 | IEnumerator IEnumerable.GetEnumerator() 28 | { 29 | throw new NotSupportedException("IEnumerable Stub source"); 30 | } 31 | 32 | public Expression Expression => throw new NotSupportedException("IEnumerable stub source"); 33 | public IQueryProvider Provider => throw new NotSupportedException("IEnumerable stub source"); 34 | } 35 | 36 | protected OeEnumerableStub(Type entityType, IEdmEntitySet entitySet) 37 | { 38 | ElementType = entityType; 39 | EntitySet = entitySet; 40 | } 41 | 42 | private static OeEnumerableStub Create(Type entityType, IEdmEntitySet entitySet) 43 | { 44 | Type stubType = typeof(OeEnumerableStubImpl<>).MakeGenericType(entityType); 45 | MethodInfo createMethodInfo = stubType.GetMethod(nameof(OeEnumerableStubImpl.Create))!; 46 | return (OeEnumerableStub)createMethodInfo.Invoke(null, new Object[] { entitySet })!; 47 | } 48 | public static ConstantExpression CreateEnumerableStubExpression(Type entityType, IEdmEntitySet entitySet) 49 | { 50 | return Expression.Constant(Create(entityType, entitySet), typeof(IEnumerable<>).MakeGenericType(entityType)); 51 | } 52 | 53 | public Type ElementType { get; } 54 | public IEdmEntitySet EntitySet { get; } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /source/OdataToEntity.GraphQL/OeSchemaBuilder.cs: -------------------------------------------------------------------------------- 1 | using GraphQL.Types; 2 | using Microsoft.OData.Edm; 3 | using System.Collections.Generic; 4 | 5 | namespace OdataToEntity.GraphQL 6 | { 7 | public readonly struct OeSchemaBuilder 8 | { 9 | private readonly IEdmModel _edmModel; 10 | private readonly OeGraphTypeBuilder _graphTypeBuilder; 11 | 12 | public OeSchemaBuilder(IEdmModel edmModel) 13 | { 14 | _edmModel = edmModel; 15 | _graphTypeBuilder = new OeGraphTypeBuilder(edmModel); 16 | } 17 | 18 | public Schema Build() 19 | { 20 | return new Schema() 21 | { 22 | Query = CreateQuery() 23 | }; 24 | } 25 | private static List CreateEntityFields(IEdmModel edmModel, OeGraphTypeBuilder graphTypeBuilder) 26 | { 27 | Db.OeDataAdapter dataAdapter = edmModel.GetDataAdapter(edmModel.EntityContainer); 28 | var entityFields = new List(dataAdapter.EntitySetAdapters.Count); 29 | foreach (Db.OeEntitySetAdapter entitySetAdapter in dataAdapter.EntitySetAdapters) 30 | { 31 | FieldType entityField = new FieldType() 32 | { 33 | Name = entitySetAdapter.EntitySetName, 34 | Resolver = new OeEntitySetResolver(edmModel), 35 | ResolvedType = graphTypeBuilder.CreateListGraphType(entitySetAdapter.EntityType) 36 | }; 37 | 38 | entityFields.Add(entityField); 39 | } 40 | return entityFields; 41 | } 42 | private ObjectGraphType CreateQuery() 43 | { 44 | var entityFields = new List(CreateEntityFields(_edmModel, _graphTypeBuilder)); 45 | foreach (IEdmModel refModel in _edmModel.ReferencedModels) 46 | if (refModel.EntityContainer != null) 47 | entityFields.AddRange(CreateEntityFields(refModel, _graphTypeBuilder)); 48 | 49 | var query = new ObjectGraphType(); 50 | foreach (FieldType entityField in entityFields) 51 | { 52 | _graphTypeBuilder.AddNavigationProperties(entityField); 53 | query.AddField(entityField); 54 | } 55 | return query; 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /test/OdataToEntity.Test/Common/PageNextLinkModelBoundBuilder.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.OData.Edm; 2 | using OdataToEntity.Query; 3 | using OdataToEntity.Query.Builder; 4 | 5 | namespace OdataToEntity.Test 6 | { 7 | public readonly struct PageNextLinkModelBoundBuilder 8 | { 9 | private readonly IEdmModel _edmModel; 10 | private readonly bool _sqlite; 11 | 12 | public PageNextLinkModelBoundBuilder(IEdmModel edmModel, bool sqlite) 13 | { 14 | _edmModel = edmModel; 15 | _sqlite = sqlite; 16 | } 17 | 18 | private void Build(IEdmModel edmModel, OeModelBoundSettingsBuilder modelBoundSettingsBuilder, int pageSize, bool navigationNextLink) 19 | { 20 | if (edmModel.EntityContainer != null) 21 | foreach (IEdmEntitySet entitySet in edmModel.EntityContainer.EntitySets()) 22 | { 23 | IEdmEntityType entityType = entitySet.EntityType(); 24 | modelBoundSettingsBuilder.SetPageSize(pageSize, entityType); 25 | 26 | foreach (IEdmNavigationProperty navigationProperty in entityType.NavigationProperties()) 27 | { 28 | if (navigationNextLink) 29 | modelBoundSettingsBuilder.SetNavigationNextLink(navigationNextLink, navigationProperty); 30 | 31 | if (navigationProperty.Type.IsCollection()) 32 | { 33 | 34 | if (_sqlite) 35 | modelBoundSettingsBuilder.SetPageSize(-1, navigationProperty); 36 | } 37 | } 38 | } 39 | 40 | foreach (IEdmModel refModel in edmModel.ReferencedModels) 41 | Build(refModel, modelBoundSettingsBuilder, pageSize, navigationNextLink); 42 | } 43 | public OeModelBoundProvider BuildProvider(int pageSize, bool navigationNextLink) 44 | { 45 | if (pageSize > 0 || navigationNextLink) 46 | { 47 | var modelBoundSettingsBuilder = new OeModelBoundSettingsBuilder(); 48 | Build(_edmModel, modelBoundSettingsBuilder, pageSize, navigationNextLink); 49 | return modelBoundSettingsBuilder.Build(); 50 | } 51 | 52 | return null; 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /sln/OdataToEntity.Test.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.27130.2026 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OdataToEntity.Test", "..\test\OdataToEntity.Test\OdataToEntity.Test.csproj", "{5552DDD1-DAEC-4960-BC4B-01852C9F982A}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OdataToEntity", "..\source\OdataToEntity\OdataToEntity.csproj", "{70ED8CE4-4FA6-418A-A5BC-8697F5CA0B03}" 9 | EndProject 10 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OdataToEntity.EfCore", "..\source\OdataToEntity.EfCore\OdataToEntity.EfCore.csproj", "{94E8BF46-2821-4890-9E07-D1FA5C797C92}" 11 | EndProject 12 | Global 13 | GlobalSection(Performance) = preSolution 14 | HasPerformanceSessions = true 15 | EndGlobalSection 16 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 17 | Debug|Any CPU = Debug|Any CPU 18 | Release|Any CPU = Release|Any CPU 19 | EndGlobalSection 20 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 21 | {5552DDD1-DAEC-4960-BC4B-01852C9F982A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 22 | {5552DDD1-DAEC-4960-BC4B-01852C9F982A}.Debug|Any CPU.Build.0 = Debug|Any CPU 23 | {5552DDD1-DAEC-4960-BC4B-01852C9F982A}.Release|Any CPU.ActiveCfg = Release|Any CPU 24 | {5552DDD1-DAEC-4960-BC4B-01852C9F982A}.Release|Any CPU.Build.0 = Release|Any CPU 25 | {70ED8CE4-4FA6-418A-A5BC-8697F5CA0B03}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 26 | {70ED8CE4-4FA6-418A-A5BC-8697F5CA0B03}.Debug|Any CPU.Build.0 = Debug|Any CPU 27 | {70ED8CE4-4FA6-418A-A5BC-8697F5CA0B03}.Release|Any CPU.ActiveCfg = Release|Any CPU 28 | {70ED8CE4-4FA6-418A-A5BC-8697F5CA0B03}.Release|Any CPU.Build.0 = Release|Any CPU 29 | {94E8BF46-2821-4890-9E07-D1FA5C797C92}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 30 | {94E8BF46-2821-4890-9E07-D1FA5C797C92}.Debug|Any CPU.Build.0 = Debug|Any CPU 31 | {94E8BF46-2821-4890-9E07-D1FA5C797C92}.Release|Any CPU.ActiveCfg = Release|Any CPU 32 | {94E8BF46-2821-4890-9E07-D1FA5C797C92}.Release|Any CPU.Build.0 = Release|Any CPU 33 | EndGlobalSection 34 | GlobalSection(SolutionProperties) = preSolution 35 | HideSolutionNode = FALSE 36 | EndGlobalSection 37 | GlobalSection(ExtensibilityGlobals) = postSolution 38 | SolutionGuid = {25DA765F-BA57-449B-B110-8335E96DAF65} 39 | EndGlobalSection 40 | EndGlobal 41 | -------------------------------------------------------------------------------- /test/OdataToEntity.Test.Asp/OdataToEntity.Test.AspServer/OdataToEntity.Test.AspServer.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Test Asp sertver for OdataToEntity 6 | Test Asp server for OdataToEntity 7 | Maxim Voronov 8 | $(NetCoreAppVersion) 9 | AnyCPU 10 | portable 11 | OdataToEntity.Test.AspServer 12 | false 13 | 14 | 15 | 16 | Exe 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | PreserveNewest 30 | 31 | 32 | 33 | 34 | 35 | PreserveNewest 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /test/OdataToEntity.Test.Asp/OdataToEntity.Test.AspMvcServer/Controllers/OrderItemsController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Http; 2 | using Microsoft.AspNetCore.Mvc; 3 | using OdataToEntity.AspNetCore; 4 | using System; 5 | using System.Collections.Generic; 6 | 7 | namespace OdataToEntity.Test.AspMvcServer.Controllers 8 | { 9 | [Route("api/[controller]")] 10 | public sealed class OrderItemsController 11 | { 12 | private readonly IHttpContextAccessor _httpContextAccessor; 13 | 14 | public OrderItemsController(IHttpContextAccessor httpContextAccessor) 15 | { 16 | _httpContextAccessor = httpContextAccessor; 17 | } 18 | 19 | [HttpDelete("{id}")] 20 | public void Delete(OeDataContext dataContext, Model.OrderItem orderItem) 21 | { 22 | dataContext.Update(orderItem); 23 | } 24 | [HttpGet] 25 | public ODataResult Get() 26 | { 27 | Query.OeModelBoundProvider modelBoundProvider = _httpContextAccessor.HttpContext.CreateModelBoundProvider(); 28 | var parser = new OeAspQueryParser(_httpContextAccessor.HttpContext, modelBoundProvider); 29 | IAsyncEnumerable orderItems = parser.ExecuteReader(); 30 | return parser.OData(orderItems); 31 | } 32 | [HttpGet("{id}")] 33 | public ODataResult Get(int id) 34 | { 35 | var parser = new OeAspQueryParser(_httpContextAccessor.HttpContext); 36 | IAsyncEnumerable orderItems = parser.ExecuteReader(); 37 | return parser.OData(orderItems); 38 | } 39 | [HttpGet("{id}/Order/Customer")] 40 | public ODataResult OrderCustomer(int id) 41 | { 42 | var parser = new OeAspQueryParser(_httpContextAccessor.HttpContext); 43 | IAsyncEnumerable customers = parser.ExecuteReader(); 44 | return parser.OData(customers); 45 | } 46 | [HttpPatch] 47 | public void Patch(OeDataContext dataContext, IDictionary orderItemProperties) 48 | { 49 | dataContext.Update(orderItemProperties); 50 | } 51 | [HttpPost] 52 | public void Post(OeDataContext dataContext, Model.OrderItem orderItem) 53 | { 54 | dataContext.Update(orderItem); 55 | } 56 | } 57 | } -------------------------------------------------------------------------------- /source/OdataToEntity.EfCore.DynamicDataContext/ModelBuilder/DynamicEdmModelMetadataProvider.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore.Metadata; 2 | using OdataToEntity.Infrastructure; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Reflection; 6 | 7 | namespace OdataToEntity.EfCore.DynamicDataContext.ModelBuilder 8 | { 9 | public sealed class DynamicEdmModelMetadataProvider : OeEfCoreEdmModelMetadataProvider 10 | { 11 | private readonly DynamicTypeDefinitionManager _typeDefinitionManager; 12 | private readonly DynamicMetadataProvider _dynamicMetadataProvider; 13 | 14 | public DynamicEdmModelMetadataProvider(IModel model, DynamicMetadataProvider dynamicMetadataProvider, DynamicTypeDefinitionManager typeDefinitionManager) : base(model) 15 | { 16 | _dynamicMetadataProvider = dynamicMetadataProvider; 17 | _typeDefinitionManager = typeDefinitionManager; 18 | } 19 | 20 | public override IReadOnlyList GetManyToManyProperties(Type clrType) 21 | { 22 | List? properties = null; 23 | TableFullName tableFullName = _typeDefinitionManager.GetDynamicTypeDefinition(clrType).TableFullName; 24 | foreach ((String navigationName, TableFullName manyToManyTarget) in _dynamicMetadataProvider.GetManyToManyProperties(tableFullName)) 25 | { 26 | if (properties == null) 27 | properties = new List(); 28 | 29 | Type itemType = _typeDefinitionManager.GetDynamicTypeDefinition(manyToManyTarget).DynamicTypeType; 30 | Type propertyType = typeof(IEnumerable<>).MakeGenericType(itemType); 31 | properties.Add(new OeShadowPropertyInfo(clrType, propertyType, navigationName)); 32 | } 33 | return properties ?? (IReadOnlyList)Array.Empty(); 34 | } 35 | protected override PropertyInfo GetPropertyInfo(IPropertyBase efProperty) 36 | { 37 | Type propertyClrType; 38 | if (efProperty is INavigation navigation) 39 | propertyClrType = _typeDefinitionManager.GetDynamicTypeDefinition(navigation.DeclaringEntityType.ClrType).GetNavigationPropertyClrType(navigation); 40 | else 41 | propertyClrType = efProperty.ClrType; 42 | return base.CreateShadowProperty(efProperty, propertyClrType); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /test/OdataToEntity.Test.GraphQL/Program.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.OData.Edm; 2 | using OdataToEntity.EfCore; 3 | using OdataToEntity.GraphQL; 4 | using System; 5 | using System.Threading.Tasks; 6 | 7 | namespace OdataToEntity.Test.GraphQL 8 | { 9 | class Program 10 | { 11 | static async Task Main(String[] args) 12 | { 13 | var tests = new StarWarsTests(new StarWarsFixture()); 14 | await tests.can_query_for_friends_of_humans(); 15 | } 16 | 17 | private static async Task Test() 18 | { 19 | using (var starWarsContext = new StarWars.StarWarsContext("StarWars")) 20 | starWarsContext.Database.EnsureCreated(); 21 | 22 | using (var order2Context = new Model.Order2Context(StarWars.StarWarsContext.Create("Order2"))) 23 | order2Context.Database.EnsureCreated(); 24 | 25 | String starWarsQuery = @" 26 | { 27 | droid(id: ""4"") { 28 | name 29 | } 30 | } 31 | "; 32 | String order2Query = @" 33 | { 34 | orders2 { 35 | name 36 | customer { 37 | name 38 | } 39 | } 40 | } 41 | "; 42 | 43 | var order2DataAdapter2 = new Model.Order2DataAdapter(false, "Order2"); 44 | EdmModel refModel = order2DataAdapter2.BuildEdmModelFromEfCoreModel(); 45 | 46 | var starWarsDataAdapter1 = new StarWars.StarWarsDataAdapter(false, "StarWars"); 47 | EdmModel rootModel = starWarsDataAdapter1.BuildEdmModelFromEfCoreModel(refModel); 48 | 49 | var parser = new OeGraphqlParser(rootModel); 50 | 51 | String starWarsOdataUri = Uri.UnescapeDataString(parser.GetOdataUri(starWarsQuery).OriginalString); 52 | Console.WriteLine(starWarsOdataUri); 53 | 54 | String starWarsJson = await (await parser.Execute(starWarsQuery)).ToStringAsync(); 55 | Console.WriteLine(starWarsJson); 56 | 57 | String order2OdataUri = Uri.UnescapeDataString(parser.GetOdataUri(order2Query).OriginalString); 58 | Console.WriteLine(order2OdataUri); 59 | 60 | String order2Json = await (await parser.Execute(order2Query)).ToStringAsync(); 61 | Console.WriteLine(order2Json); 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /source/OdataToEntity/Db/OeEntityDbEnumerator.cs: -------------------------------------------------------------------------------- 1 | using OdataToEntity.Parsers; 2 | using System; 3 | using System.Collections; 4 | using System.Collections.Generic; 5 | using System.Threading; 6 | using System.Threading.Tasks; 7 | 8 | namespace OdataToEntity.Db 9 | { 10 | public sealed class OeEntityDbEnumerator : IOeDbEnumerator 11 | { 12 | private readonly IAsyncEnumerator _asyncEnumerator; 13 | private readonly OeEntityDbEnumerator? _parentEnumerator; 14 | 15 | public OeEntityDbEnumerator(IAsyncEnumerator asyncEnumerator, OeEntryFactory entryFactory) 16 | { 17 | _asyncEnumerator = asyncEnumerator; 18 | EntryFactory = entryFactory; 19 | } 20 | public OeEntityDbEnumerator(IAsyncEnumerator asyncEnumerator, OeNavigationEntryFactory entryFactory, OeEntityDbEnumerator parentEnumerator) 21 | : this(asyncEnumerator, entryFactory) 22 | { 23 | _parentEnumerator = parentEnumerator; 24 | } 25 | 26 | public void ClearBuffer() 27 | { 28 | } 29 | public IOeDbEnumerator CreateChild(OeNavigationEntryFactory entryFactory, CancellationToken cancellationToken) 30 | { 31 | IAsyncEnumerable asyncEnumerable; 32 | Object? navigationValue = entryFactory.GetValue(Current); 33 | if (navigationValue is IEnumerable enumerable) 34 | asyncEnumerable = Infrastructure.AsyncEnumeratorHelper.ToAsyncEnumerable(enumerable); 35 | else 36 | asyncEnumerable = Infrastructure.AsyncEnumeratorHelper.ToAsyncEnumerable(Task.FromResult(navigationValue)); 37 | 38 | IAsyncEnumerator asyncEnumerator = asyncEnumerable.GetAsyncEnumerator(cancellationToken); 39 | Infrastructure.AsyncEnumeratorHelper.GetResult(asyncEnumerator.MoveNextAsync()); 40 | return new OeEntityDbEnumerator(asyncEnumerator, entryFactory, this); 41 | } 42 | public ValueTask DisposeAsync() 43 | { 44 | return _asyncEnumerator.DisposeAsync(); 45 | } 46 | public ValueTask MoveNextAsync() 47 | { 48 | return _asyncEnumerator.MoveNextAsync(); 49 | } 50 | 51 | public Object? Current => _asyncEnumerator.Current; 52 | public OeEntryFactory EntryFactory { get; } 53 | IOeDbEnumerator? IOeDbEnumerator.ParentEnumerator => _parentEnumerator; 54 | public Object? RawValue => _asyncEnumerator.Current; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /source/OdataToEntity/Parsers/OeNavigationEntryFactory.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.OData.Edm; 2 | using Microsoft.OData.UriParser; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq.Expressions; 6 | 7 | namespace OdataToEntity.Parsers 8 | { 9 | public class OeNavigationEntryFactory : OeEntryFactory 10 | { 11 | public OeNavigationEntryFactory( 12 | IEdmEntitySetBase entitySet, 13 | OePropertyAccessor[] accessors, 14 | OePropertyAccessor[]? skipTokenAccessors, 15 | IReadOnlyList navigationLinks, 16 | LambdaExpression? linkAccessor, 17 | IEdmNavigationProperty edmNavigationProperty, 18 | ExpandedReferenceSelectItem navigationSelectItem, 19 | bool nextLink) 20 | : base(entitySet, accessors, skipTokenAccessors, navigationLinks, linkAccessor) 21 | { 22 | EdmNavigationProperty = edmNavigationProperty; 23 | NavigationSelectItem = navigationSelectItem; 24 | NextLink = nextLink; 25 | } 26 | 27 | protected override OeNavigationEntryFactory CreateEntryFactoryFromTuple(IEdmModel edmModel, OeEntryFactory parentEntryFactory) 28 | { 29 | OePropertyAccessor[] accessors = base.GetAccessorsFromTuple(edmModel); 30 | var navigationLinks = new OeNavigationEntryFactory[NavigationLinks.Count]; 31 | for (int i = 0; i < NavigationLinks.Count; i++) 32 | navigationLinks[i] = NavigationLinks[i].CreateEntryFactoryFromTuple(edmModel, this); 33 | 34 | ParameterExpression parameter = Expression.Parameter(typeof(Object)); 35 | UnaryExpression typedParameter = Expression.Convert(parameter, edmModel.GetClrType(parentEntryFactory.EntitySet)); 36 | MemberExpression navigationPropertyExpression = Expression.Property(typedParameter, EdmNavigationProperty.Name); 37 | LambdaExpression linkAccessor = Expression.Lambda(navigationPropertyExpression, parameter); 38 | 39 | return new OeNavigationEntryFactory( 40 | EntitySet, 41 | accessors, 42 | null, 43 | navigationLinks, 44 | linkAccessor, 45 | EdmNavigationProperty, 46 | NavigationSelectItem, 47 | false); 48 | } 49 | 50 | public IEdmNavigationProperty EdmNavigationProperty { get; } 51 | public ExpandedReferenceSelectItem NavigationSelectItem { get; } 52 | public bool NextLink { get; } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /test/OdataToEntity.Test.Wcf/OdataToEntity.Test.WcfService/OdataWcfService.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.OData.Edm; 2 | using OdataToEntity.Db; 3 | using System; 4 | using System.IO; 5 | using System.Threading; 6 | using System.Threading.Tasks; 7 | 8 | namespace OdataToEntity.Test.WcfService 9 | { 10 | public class OdataWcfService : IOdataWcf 11 | { 12 | private readonly static Uri _baseUri = new Uri("http://dummy"); 13 | private readonly IEdmModel _edmModel; 14 | 15 | public OdataWcfService(OeDataAdapter dataAdapter, IEdmModel edmModel) 16 | { 17 | DataAdapter = dataAdapter; 18 | _edmModel = edmModel; 19 | } 20 | 21 | public async Task Get(OdataWcfQuery request) 22 | { 23 | OeRequestHeaders headers = OeRequestHeaders.Parse(request.ContentType, request.Prefer); 24 | headers.ResponseContentType = headers.ContentType; 25 | 26 | Query.OeModelBoundProvider modelBoundProvider = null; 27 | if (headers.MaxPageSize > 0) 28 | { 29 | var pageNextLinkModelBoundBuilder = new PageNextLinkModelBoundBuilder(_edmModel, false); 30 | modelBoundProvider = pageNextLinkModelBoundBuilder.BuildProvider(headers.MaxPageSize, false); 31 | } 32 | var parser = new OeParser(_baseUri, _edmModel, modelBoundProvider, null); 33 | 34 | String query = new StreamReader(request.Content).ReadToEnd(); 35 | var uri = new Uri(_baseUri, new Uri(query, UriKind.Relative)); 36 | var responseStream = new MemoryStream(); 37 | 38 | await parser.ExecuteGetAsync(uri, headers, responseStream, CancellationToken.None); 39 | responseStream.Position = 0; 40 | return new OdataWcfQuery() 41 | { 42 | Content = responseStream, 43 | ContentType = headers.ResponseContentType 44 | }; 45 | } 46 | public async Task Post(OdataWcfQuery request) 47 | { 48 | var parser = new OeParser(_baseUri, _edmModel); 49 | var responseStream = new MemoryStream(); 50 | await parser.ExecuteBatchAsync(request.Content, responseStream, request.ContentType, CancellationToken.None); 51 | responseStream.Position = 0; 52 | return new OdataWcfQuery() 53 | { 54 | Content = responseStream, 55 | ContentType = request.ContentType 56 | }; 57 | } 58 | 59 | public OeDataAdapter DataAdapter { get; } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /test/OdataToEntity.Test.Asp/OdataToEntity.Test.AspMvcServer/Controllers/ShippingAddressesController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Http; 2 | using Microsoft.AspNetCore.Mvc; 3 | using OdataToEntity.AspNetCore; 4 | using System; 5 | using System.Collections.Generic; 6 | 7 | namespace OdataToEntity.Test.AspMvcServer.Controllers 8 | { 9 | [Route("api/[controller]")] 10 | public sealed class ShippingAddressesController 11 | { 12 | private readonly IHttpContextAccessor _httpContextAccessor; 13 | 14 | public ShippingAddressesController(IHttpContextAccessor httpContextAccessor) 15 | { 16 | _httpContextAccessor = httpContextAccessor; 17 | } 18 | 19 | [HttpDelete("{id}")] 20 | public void Delete(OeDataContext dataContext, Model.ShippingAddress shippingAddress) 21 | { 22 | dataContext.Update(shippingAddress); 23 | } 24 | [HttpGet] 25 | public ODataResult Get() 26 | { 27 | Query.OeModelBoundProvider modelBoundProvider = _httpContextAccessor.HttpContext.CreateModelBoundProvider(); 28 | var parser = new OeAspQueryParser(_httpContextAccessor.HttpContext, modelBoundProvider); 29 | IAsyncEnumerable shippingAddresses = parser.ExecuteReader(); 30 | return parser.OData(shippingAddresses); 31 | } 32 | [HttpGet("{id}")] 33 | public ODataResult Get(int id) 34 | { 35 | var parser = new OeAspQueryParser(_httpContextAccessor.HttpContext); 36 | IAsyncEnumerable shippingAddresses = parser.ExecuteReader(); 37 | return parser.OData(shippingAddresses); 38 | } 39 | [HttpGet("{id}/Order/Customer")] 40 | public ODataResult OrderCustomer(int id) 41 | { 42 | var parser = new OeAspQueryParser(_httpContextAccessor.HttpContext); 43 | IAsyncEnumerable customers = parser.ExecuteReader(); 44 | return parser.OData(customers); 45 | } 46 | [HttpPatch] 47 | public void Patch(OeDataContext dataContext, IDictionary shippingAddressProperties) 48 | { 49 | dataContext.Update(shippingAddressProperties); 50 | } 51 | [HttpPost] 52 | public void Post(OeDataContext dataContext, Model.ShippingAddress shippingAddress) 53 | { 54 | dataContext.Update(shippingAddress); 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /test/OdataToEntity.Test.GraphQL/StarWars/Character.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel.DataAnnotations; 4 | using System.ComponentModel.DataAnnotations.Schema; 5 | 6 | namespace OdataToEntity.Test.GraphQL.StarWars 7 | { 8 | public abstract class Hero 9 | { 10 | [Column(TypeName = "varchar(16)")] 11 | public String Id { get; set; } 12 | public String Name { get; set; } 13 | 14 | [InverseProperty(nameof(StarWars.CharacterToCharacter.Character))] 15 | public ICollection CharacterToCharacter { get; set; } 16 | [NotMapped] 17 | public ICollection Friends { get; set; } 18 | 19 | public ICollection CharacterToEpisode { get; set; } 20 | [NotMapped] 21 | public ICollection AppearsIn { get; set; } 22 | 23 | public Actor Actor { get; set; } 24 | public int ActorId { get; set; } 25 | 26 | public Actor Voice { get; set; } 27 | public int? VoiceId { get; set; } 28 | } 29 | 30 | public sealed class Human : Hero 31 | { 32 | public String HomePlanet { get; set; } 33 | } 34 | 35 | public sealed class Droid : Hero 36 | { 37 | public String PrimaryFunction { get; set; } 38 | } 39 | 40 | public sealed class CharacterToCharacter 41 | { 42 | public Hero Character { get; set; } 43 | public String CharacterId { get; set; } 44 | [ForeignKey(nameof(FriendId))] 45 | public Hero FriendTo { get; set; } 46 | public String FriendId { get; set; } 47 | } 48 | 49 | public sealed class CharacterToEpisode 50 | { 51 | public Hero Character { get; set; } 52 | [Column(TypeName = "varchar(16)")] 53 | public String CharacterId { get; set; } 54 | public EpisodeEnum Episode { get; set; } 55 | public Episodes EpisodeId { get; set; } 56 | } 57 | 58 | public enum Episodes 59 | { 60 | NEWHOPE = 4, 61 | EMPIRE = 5, 62 | JEDI = 6 63 | } 64 | 65 | [Table("Episodes")] 66 | public sealed class EpisodeEnum 67 | { 68 | public String Name { get; set; } 69 | public String Description { get; set; } 70 | [Key] 71 | public Episodes Value { get; set; } 72 | } 73 | 74 | public sealed class Actor 75 | { 76 | [DatabaseGenerated(DatabaseGeneratedOption.None)] 77 | public int Id { get; set; } 78 | public String Name { get; set; } 79 | public DateTime Birthday { get; set; } 80 | } 81 | } 82 | --------------------------------------------------------------------------------