diff --git a/src/Examples/DapperExample/TranslationToSql/DataModel/FromEntitiesDataModelService.cs b/src/Examples/DapperExample/TranslationToSql/DataModel/FromEntitiesDataModelService.cs index b0ba38f3a5..711ad8517c 100644 --- a/src/Examples/DapperExample/TranslationToSql/DataModel/FromEntitiesDataModelService.cs +++ b/src/Examples/DapperExample/TranslationToSql/DataModel/FromEntitiesDataModelService.cs @@ -38,12 +38,12 @@ public void Initialize(DbContext dbContext) Initialize(); } - private void ScanForeignKeys(IModel entityModel) + private void ScanForeignKeys(IReadOnlyModel entityModel) { foreach (RelationshipAttribute relationship in ResourceGraph.GetResourceTypes().SelectMany(resourceType => resourceType.Relationships)) { - IEntityType? leftEntityType = entityModel.FindEntityType(relationship.LeftType.ClrType); - INavigation? navigation = leftEntityType?.FindNavigation(relationship.Property.Name); + IReadOnlyEntityType? leftEntityType = entityModel.FindEntityType(relationship.LeftType.ClrType); + IReadOnlyNavigation? navigation = leftEntityType?.FindNavigation(relationship.Property.Name); if (navigation != null) { @@ -57,7 +57,7 @@ private void ScanForeignKeys(IModel entityModel) } } - private void ScanColumnNullability(IModel entityModel) + private void ScanColumnNullability(IReadOnlyModel entityModel) { foreach (ResourceType resourceType in ResourceGraph.GetResourceTypes()) { @@ -65,15 +65,15 @@ private void ScanColumnNullability(IModel entityModel) } } - private void ScanColumnNullability(ResourceType resourceType, IModel entityModel) + private void ScanColumnNullability(ResourceType resourceType, IReadOnlyModel entityModel) { - IEntityType? entityType = entityModel.FindEntityType(resourceType.ClrType); + IReadOnlyEntityType? entityType = entityModel.FindEntityType(resourceType.ClrType); if (entityType != null) { foreach (AttrAttribute attribute in resourceType.Attributes) { - IProperty? property = entityType.FindProperty(attribute.Property.Name); + IReadOnlyProperty? property = entityType.FindProperty(attribute.Property.Name); if (property != null) { diff --git a/src/Examples/NoEntityFrameworkExample/Data/InMemoryModel.cs b/src/Examples/NoEntityFrameworkExample/Data/InMemoryModel.cs deleted file mode 100644 index c81aa07b8f..0000000000 --- a/src/Examples/NoEntityFrameworkExample/Data/InMemoryModel.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System.Reflection; -using JsonApiDotNetCore.Configuration; -using Microsoft.EntityFrameworkCore.Metadata; - -namespace NoEntityFrameworkExample.Data; - -internal sealed class InMemoryModel : RuntimeModel -{ - public InMemoryModel(IResourceGraph resourceGraph) - { - foreach (ResourceType resourceType in resourceGraph.GetResourceTypes()) - { - RuntimeEntityType entityType = AddEntityType(resourceType.ClrType.FullName!, resourceType.ClrType); - SetEntityProperties(entityType, resourceType); - } - } - - private static void SetEntityProperties(RuntimeEntityType entityType, ResourceType resourceType) - { - foreach (PropertyInfo property in resourceType.ClrType.GetProperties()) - { - entityType.AddProperty(property.Name, property.PropertyType, property); - } - } -} diff --git a/src/Examples/NoEntityFrameworkExample/Data/ResourceGraphExtensions.cs b/src/Examples/NoEntityFrameworkExample/Data/ResourceGraphExtensions.cs new file mode 100644 index 0000000000..ff35f0ab0d --- /dev/null +++ b/src/Examples/NoEntityFrameworkExample/Data/ResourceGraphExtensions.cs @@ -0,0 +1,32 @@ +using System.Reflection; +using JsonApiDotNetCore.Configuration; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace NoEntityFrameworkExample.Data; + +internal static class ResourceGraphExtensions +{ + public static IReadOnlyModel ToEntityModel(this IResourceGraph resourceGraph) + { + var modelBuilder = new ModelBuilder(); + + foreach (ResourceType resourceType in resourceGraph.GetResourceTypes()) + { + IncludeResourceType(resourceType, modelBuilder); + } + + return modelBuilder.Model; + } + + private static void IncludeResourceType(ResourceType resourceType, ModelBuilder builder) + { + EntityTypeBuilder entityTypeBuilder = builder.Entity(resourceType.ClrType); + + foreach (PropertyInfo property in resourceType.ClrType.GetProperties()) + { + entityTypeBuilder.Property(property.PropertyType, property.Name); + } + } +} diff --git a/src/Examples/NoEntityFrameworkExample/Program.cs b/src/Examples/NoEntityFrameworkExample/Program.cs index 8546e939e8..f21d116e5f 100755 --- a/src/Examples/NoEntityFrameworkExample/Program.cs +++ b/src/Examples/NoEntityFrameworkExample/Program.cs @@ -1,5 +1,6 @@ using JsonApiDotNetCore.Configuration; using NoEntityFrameworkExample; +using NoEntityFrameworkExample.Data; WebApplicationBuilder builder = WebApplication.CreateBuilder(args); @@ -20,6 +21,12 @@ #endif }, discovery => discovery.AddCurrentAssembly()); +builder.Services.AddSingleton(serviceProvider => +{ + var resourceGraph = serviceProvider.GetRequiredService(); + return resourceGraph.ToEntityModel(); +}); + WebApplication app = builder.Build(); // Configure the HTTP request pipeline. diff --git a/src/Examples/NoEntityFrameworkExample/QueryLayerToLinqConverter.cs b/src/Examples/NoEntityFrameworkExample/QueryLayerToLinqConverter.cs index 29d5f999e9..a67a694aef 100644 --- a/src/Examples/NoEntityFrameworkExample/QueryLayerToLinqConverter.cs +++ b/src/Examples/NoEntityFrameworkExample/QueryLayerToLinqConverter.cs @@ -7,9 +7,9 @@ namespace NoEntityFrameworkExample; -internal sealed class QueryLayerToLinqConverter(IModel model, IQueryableBuilder queryableBuilder) +internal sealed class QueryLayerToLinqConverter(IReadOnlyModel entityModel, IQueryableBuilder queryableBuilder) { - private readonly IModel _model = model; + private readonly IReadOnlyModel _entityModel = entityModel; private readonly IQueryableBuilder _queryableBuilder = queryableBuilder; public IEnumerable ApplyQueryLayer(QueryLayer queryLayer, IEnumerable resources) @@ -21,7 +21,7 @@ public IEnumerable ApplyQueryLayer(QueryLayer queryLayer, // Convert QueryLayer into LINQ expression. IQueryable source = ((IEnumerable)resources).AsQueryable(); - var context = QueryableBuilderContext.CreateRoot(source, typeof(Enumerable), _model, null); + var context = QueryableBuilderContext.CreateRoot(source, typeof(Enumerable), _entityModel, null); Expression expression = _queryableBuilder.ApplyQuery(queryLayer, context); // Insert null checks to prevent a NullReferenceException during execution of expressions such as: diff --git a/src/Examples/NoEntityFrameworkExample/Repositories/InMemoryResourceRepository.cs b/src/Examples/NoEntityFrameworkExample/Repositories/InMemoryResourceRepository.cs index 243b484a9b..9d0852ad7f 100644 --- a/src/Examples/NoEntityFrameworkExample/Repositories/InMemoryResourceRepository.cs +++ b/src/Examples/NoEntityFrameworkExample/Repositories/InMemoryResourceRepository.cs @@ -4,7 +4,7 @@ using JsonApiDotNetCore.Queries.QueryableBuilding; using JsonApiDotNetCore.Repositories; using JsonApiDotNetCore.Resources; -using NoEntityFrameworkExample.Data; +using Microsoft.EntityFrameworkCore.Metadata; namespace NoEntityFrameworkExample.Repositories; @@ -19,19 +19,12 @@ namespace NoEntityFrameworkExample.Repositories; /// /// The resource identifier type. /// -public abstract class InMemoryResourceRepository : IResourceReadRepository +public abstract class InMemoryResourceRepository(IResourceGraph resourceGraph, IQueryableBuilder queryableBuilder, IReadOnlyModel entityModel) + : IResourceReadRepository where TResource : class, IIdentifiable { - private readonly ResourceType _resourceType; - private readonly QueryLayerToLinqConverter _queryLayerToLinqConverter; - - protected InMemoryResourceRepository(IResourceGraph resourceGraph, IQueryableBuilder queryableBuilder) - { - _resourceType = resourceGraph.GetResourceType(); - - var model = new InMemoryModel(resourceGraph); - _queryLayerToLinqConverter = new QueryLayerToLinqConverter(model, queryableBuilder); - } + private readonly ResourceType _resourceType = resourceGraph.GetResourceType(); + private readonly QueryLayerToLinqConverter _queryLayerToLinqConverter = new(entityModel, queryableBuilder); /// public Task> GetAsync(QueryLayer queryLayer, CancellationToken cancellationToken) diff --git a/src/Examples/NoEntityFrameworkExample/Repositories/PersonRepository.cs b/src/Examples/NoEntityFrameworkExample/Repositories/PersonRepository.cs index 897af592b7..8e2725379c 100644 --- a/src/Examples/NoEntityFrameworkExample/Repositories/PersonRepository.cs +++ b/src/Examples/NoEntityFrameworkExample/Repositories/PersonRepository.cs @@ -1,14 +1,15 @@ using JetBrains.Annotations; using JsonApiDotNetCore.Configuration; using JsonApiDotNetCore.Queries.QueryableBuilding; +using Microsoft.EntityFrameworkCore.Metadata; using NoEntityFrameworkExample.Data; using NoEntityFrameworkExample.Models; namespace NoEntityFrameworkExample.Repositories; [UsedImplicitly(ImplicitUseKindFlags.InstantiatedNoFixedConstructorSignature)] -public sealed class PersonRepository(IResourceGraph resourceGraph, IQueryableBuilder queryableBuilder) - : InMemoryResourceRepository(resourceGraph, queryableBuilder) +public sealed class PersonRepository(IResourceGraph resourceGraph, IQueryableBuilder queryableBuilder, IReadOnlyModel entityModel) + : InMemoryResourceRepository(resourceGraph, queryableBuilder, entityModel) { protected override IEnumerable GetDataSource() { diff --git a/src/Examples/NoEntityFrameworkExample/Repositories/TagRepository.cs b/src/Examples/NoEntityFrameworkExample/Repositories/TagRepository.cs index 30658fb68d..81a28ed6bc 100644 --- a/src/Examples/NoEntityFrameworkExample/Repositories/TagRepository.cs +++ b/src/Examples/NoEntityFrameworkExample/Repositories/TagRepository.cs @@ -1,14 +1,15 @@ using JetBrains.Annotations; using JsonApiDotNetCore.Configuration; using JsonApiDotNetCore.Queries.QueryableBuilding; +using Microsoft.EntityFrameworkCore.Metadata; using NoEntityFrameworkExample.Data; using NoEntityFrameworkExample.Models; namespace NoEntityFrameworkExample.Repositories; [UsedImplicitly(ImplicitUseKindFlags.InstantiatedNoFixedConstructorSignature)] -public sealed class TagRepository(IResourceGraph resourceGraph, IQueryableBuilder queryableBuilder) - : InMemoryResourceRepository(resourceGraph, queryableBuilder) +public sealed class TagRepository(IResourceGraph resourceGraph, IQueryableBuilder queryableBuilder, IReadOnlyModel entityModel) + : InMemoryResourceRepository(resourceGraph, queryableBuilder, entityModel) { protected override IEnumerable GetDataSource() { diff --git a/src/Examples/NoEntityFrameworkExample/Repositories/TodoItemRepository.cs b/src/Examples/NoEntityFrameworkExample/Repositories/TodoItemRepository.cs index 41774b0c8f..335d7c5c5a 100644 --- a/src/Examples/NoEntityFrameworkExample/Repositories/TodoItemRepository.cs +++ b/src/Examples/NoEntityFrameworkExample/Repositories/TodoItemRepository.cs @@ -1,14 +1,15 @@ using JetBrains.Annotations; using JsonApiDotNetCore.Configuration; using JsonApiDotNetCore.Queries.QueryableBuilding; +using Microsoft.EntityFrameworkCore.Metadata; using NoEntityFrameworkExample.Data; using NoEntityFrameworkExample.Models; namespace NoEntityFrameworkExample.Repositories; [UsedImplicitly(ImplicitUseKindFlags.InstantiatedNoFixedConstructorSignature)] -public sealed class TodoItemRepository(IResourceGraph resourceGraph, IQueryableBuilder queryableBuilder) - : InMemoryResourceRepository(resourceGraph, queryableBuilder) +public sealed class TodoItemRepository(IResourceGraph resourceGraph, IQueryableBuilder queryableBuilder, IReadOnlyModel entityModel) + : InMemoryResourceRepository(resourceGraph, queryableBuilder, entityModel) { protected override IEnumerable GetDataSource() { diff --git a/src/Examples/NoEntityFrameworkExample/Services/InMemoryResourceService.cs b/src/Examples/NoEntityFrameworkExample/Services/InMemoryResourceService.cs index 67c7a4138c..e9b37560fc 100644 --- a/src/Examples/NoEntityFrameworkExample/Services/InMemoryResourceService.cs +++ b/src/Examples/NoEntityFrameworkExample/Services/InMemoryResourceService.cs @@ -7,7 +7,7 @@ using JsonApiDotNetCore.Resources; using JsonApiDotNetCore.Resources.Annotations; using JsonApiDotNetCore.Services; -using NoEntityFrameworkExample.Data; +using Microsoft.EntityFrameworkCore.Metadata; namespace NoEntityFrameworkExample.Services; @@ -30,32 +30,19 @@ namespace NoEntityFrameworkExample.Services; /// /// The resource identifier type. /// -public abstract class InMemoryResourceService : IResourceQueryService +public abstract class InMemoryResourceService( + IJsonApiOptions options, IResourceGraph resourceGraph, IQueryLayerComposer queryLayerComposer, IPaginationContext paginationContext, + IEnumerable constraintProviders, IQueryableBuilder queryableBuilder, IReadOnlyModel entityModel, + ILoggerFactory loggerFactory) : IResourceQueryService where TResource : class, IIdentifiable { - private readonly IJsonApiOptions _options; - private readonly IQueryLayerComposer _queryLayerComposer; - private readonly IPaginationContext _paginationContext; - private readonly IEnumerable _constraintProviders; - private readonly ILogger> _logger; - private readonly ResourceType _resourceType; - private readonly QueryLayerToLinqConverter _queryLayerToLinqConverter; - - protected InMemoryResourceService(IJsonApiOptions options, IResourceGraph resourceGraph, IQueryLayerComposer queryLayerComposer, - IPaginationContext paginationContext, IEnumerable constraintProviders, IQueryableBuilder queryableBuilder, - ILoggerFactory loggerFactory) - { - _options = options; - _queryLayerComposer = queryLayerComposer; - _paginationContext = paginationContext; - _constraintProviders = constraintProviders; - - _logger = loggerFactory.CreateLogger>(); - _resourceType = resourceGraph.GetResourceType(); - - var model = new InMemoryModel(resourceGraph); - _queryLayerToLinqConverter = new QueryLayerToLinqConverter(model, queryableBuilder); - } + private readonly IJsonApiOptions _options = options; + private readonly IQueryLayerComposer _queryLayerComposer = queryLayerComposer; + private readonly IPaginationContext _paginationContext = paginationContext; + private readonly IEnumerable _constraintProviders = constraintProviders; + private readonly ILogger> _logger = loggerFactory.CreateLogger>(); + private readonly ResourceType _resourceType = resourceGraph.GetResourceType(); + private readonly QueryLayerToLinqConverter _queryLayerToLinqConverter = new(entityModel, queryableBuilder); /// public Task> GetAsync(CancellationToken cancellationToken) diff --git a/src/Examples/NoEntityFrameworkExample/Services/TodoItemService.cs b/src/Examples/NoEntityFrameworkExample/Services/TodoItemService.cs index 294d23978c..d38cca9c94 100644 --- a/src/Examples/NoEntityFrameworkExample/Services/TodoItemService.cs +++ b/src/Examples/NoEntityFrameworkExample/Services/TodoItemService.cs @@ -3,6 +3,7 @@ using JsonApiDotNetCore.Queries; using JsonApiDotNetCore.Queries.QueryableBuilding; using JsonApiDotNetCore.Resources; +using Microsoft.EntityFrameworkCore.Metadata; using NoEntityFrameworkExample.Data; using NoEntityFrameworkExample.Models; @@ -11,8 +12,8 @@ namespace NoEntityFrameworkExample.Services; [UsedImplicitly(ImplicitUseKindFlags.InstantiatedNoFixedConstructorSignature)] public sealed class TodoItemService( IJsonApiOptions options, IResourceGraph resourceGraph, IQueryLayerComposer queryLayerComposer, IPaginationContext paginationContext, - IEnumerable constraintProviders, IQueryableBuilder queryableBuilder, ILoggerFactory loggerFactory) - : InMemoryResourceService(options, resourceGraph, queryLayerComposer, paginationContext, constraintProviders, queryableBuilder, + IEnumerable constraintProviders, IQueryableBuilder queryableBuilder, IReadOnlyModel entityModel, ILoggerFactory loggerFactory) + : InMemoryResourceService(options, resourceGraph, queryLayerComposer, paginationContext, constraintProviders, queryableBuilder, entityModel, loggerFactory) { protected override IEnumerable GetDataSource(ResourceType resourceType) diff --git a/src/JsonApiDotNetCore/Queries/QueryableBuilding/QueryClauseBuilderContext.cs b/src/JsonApiDotNetCore/Queries/QueryableBuilding/QueryClauseBuilderContext.cs index 42dcf80428..05cccf7943 100644 --- a/src/JsonApiDotNetCore/Queries/QueryableBuilding/QueryClauseBuilderContext.cs +++ b/src/JsonApiDotNetCore/Queries/QueryableBuilding/QueryClauseBuilderContext.cs @@ -29,7 +29,7 @@ public sealed class QueryClauseBuilderContext /// /// The Entity Framework Core entity model. /// - public IModel EntityModel { get; } + public IReadOnlyModel EntityModel { get; } /// /// Used to produce unique names for lambda parameters. @@ -51,7 +51,7 @@ public sealed class QueryClauseBuilderContext /// public object? State { get; } - public QueryClauseBuilderContext(Expression source, ResourceType resourceType, Type extensionType, IModel entityModel, + public QueryClauseBuilderContext(Expression source, ResourceType resourceType, Type extensionType, IReadOnlyModel entityModel, LambdaScopeFactory lambdaScopeFactory, LambdaScope lambdaScope, IQueryableBuilder queryableBuilder, object? state) { ArgumentGuard.NotNull(source); diff --git a/src/JsonApiDotNetCore/Queries/QueryableBuilding/QueryableBuilderContext.cs b/src/JsonApiDotNetCore/Queries/QueryableBuilding/QueryableBuilderContext.cs index 4659cca875..358990fdc7 100644 --- a/src/JsonApiDotNetCore/Queries/QueryableBuilding/QueryableBuilderContext.cs +++ b/src/JsonApiDotNetCore/Queries/QueryableBuilding/QueryableBuilderContext.cs @@ -29,7 +29,7 @@ public sealed class QueryableBuilderContext /// /// The Entity Framework Core entity model. /// - public IModel EntityModel { get; } + public IReadOnlyModel EntityModel { get; } /// /// Used to produce unique names for lambda parameters. @@ -41,7 +41,7 @@ public sealed class QueryableBuilderContext /// public object? State { get; } - public QueryableBuilderContext(Expression source, Type elementType, Type extensionType, IModel entityModel, LambdaScopeFactory lambdaScopeFactory, + public QueryableBuilderContext(Expression source, Type elementType, Type extensionType, IReadOnlyModel entityModel, LambdaScopeFactory lambdaScopeFactory, object? state) { ArgumentGuard.NotNull(source); @@ -58,15 +58,15 @@ public QueryableBuilderContext(Expression source, Type elementType, Type extensi State = state; } - public static QueryableBuilderContext CreateRoot(IQueryable source, Type extensionType, IModel model, object? state) + public static QueryableBuilderContext CreateRoot(IQueryable source, Type extensionType, IReadOnlyModel entityModel, object? state) { ArgumentGuard.NotNull(source); ArgumentGuard.NotNull(extensionType); - ArgumentGuard.NotNull(model); + ArgumentGuard.NotNull(entityModel); var lambdaScopeFactory = new LambdaScopeFactory(); - return new QueryableBuilderContext(source.Expression, source.ElementType, extensionType, model, lambdaScopeFactory, state); + return new QueryableBuilderContext(source.Expression, source.ElementType, extensionType, entityModel, lambdaScopeFactory, state); } public QueryClauseBuilderContext CreateClauseContext(IQueryableBuilder queryableBuilder, Expression source, ResourceType resourceType, diff --git a/src/JsonApiDotNetCore/Queries/QueryableBuilding/SelectClauseBuilder.cs b/src/JsonApiDotNetCore/Queries/QueryableBuilding/SelectClauseBuilder.cs index d1113946ad..c67b785d89 100644 --- a/src/JsonApiDotNetCore/Queries/QueryableBuilding/SelectClauseBuilder.cs +++ b/src/JsonApiDotNetCore/Queries/QueryableBuilding/SelectClauseBuilder.cs @@ -40,8 +40,8 @@ public virtual Expression ApplySelect(FieldSelection selection, QueryClauseBuild private Expression CreateLambdaBodyInitializer(FieldSelection selection, ResourceType resourceType, LambdaScope lambdaScope, bool lambdaAccessorRequiresTestForNull, QueryClauseBuilderContext context) { - IEntityType entityType = context.EntityModel.FindEntityType(resourceType.ClrType)!; - IEntityType[] concreteEntityTypes = entityType.GetConcreteDerivedTypesInclusive().ToArray(); + IReadOnlyEntityType entityType = context.EntityModel.FindEntityType(resourceType.ClrType)!; + IReadOnlyEntityType[] concreteEntityTypes = entityType.GetConcreteDerivedTypesInclusive().ToArray(); Expression bodyInitializer = concreteEntityTypes.Length > 1 ? CreateLambdaBodyInitializerForTypeHierarchy(selection, resourceType, concreteEntityTypes, lambdaScope, context) @@ -56,12 +56,12 @@ private Expression CreateLambdaBodyInitializer(FieldSelection selection, Resourc } private Expression CreateLambdaBodyInitializerForTypeHierarchy(FieldSelection selection, ResourceType baseResourceType, - IEnumerable concreteEntityTypes, LambdaScope lambdaScope, QueryClauseBuilderContext context) + IEnumerable concreteEntityTypes, LambdaScope lambdaScope, QueryClauseBuilderContext context) { IReadOnlySet resourceTypes = selection.GetResourceTypes(); Expression rootCondition = lambdaScope.Accessor; - foreach (IEntityType entityType in concreteEntityTypes) + foreach (IReadOnlyEntityType entityType in concreteEntityTypes) { ResourceType? resourceType = resourceTypes.SingleOrDefault(type => type.ClrType == entityType.ClrType); @@ -115,7 +115,7 @@ private Expression CreateLambdaBodyInitializerForSingleType(FieldSelection selec } private static ICollection ToPropertySelectors(FieldSelectors fieldSelectors, ResourceType resourceType, Type elementType, - IModel entityModel) + IReadOnlyModel entityModel) { var propertySelectors = new Dictionary(); @@ -134,17 +134,18 @@ private static ICollection ToPropertySelectors(FieldSelectors return propertySelectors.Values; } - private static void IncludeAllScalarProperties(Type elementType, Dictionary propertySelectors, IModel entityModel) + private static void IncludeAllScalarProperties(Type elementType, Dictionary propertySelectors, IReadOnlyModel entityModel) { - IEntityType entityType = entityModel.GetEntityTypes().Single(type => type.ClrType == elementType); + IReadOnlyEntityType entityType = entityModel.GetEntityTypes().Single(type => type.ClrType == elementType); - foreach (IProperty property in entityType.GetProperties().Where(property => !property.IsShadowProperty())) + foreach (IReadOnlyProperty property in entityType.GetProperties().Where(property => !property.IsShadowProperty())) { var propertySelector = new PropertySelector(property.PropertyInfo!); IncludeWritableProperty(propertySelector, propertySelectors); } - foreach (INavigation navigation in entityType.GetNavigations().Where(navigation => navigation.ForeignKey.IsOwnership && !navigation.IsShadowProperty())) + foreach (IReadOnlyNavigation navigation in entityType.GetNavigations() + .Where(navigation => navigation.ForeignKey.IsOwnership && !navigation.IsShadowProperty())) { var propertySelector = new PropertySelector(navigation.PropertyInfo!); IncludeWritableProperty(propertySelector, propertySelectors); pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy