Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unable to use Microsoft.Azure.Cosmos.Spatial.Point in Entity Getting System.InvalidOperationException #20479

Closed
aliiqbalIntelligenes opened this issue Mar 31, 2020 · 14 comments

Comments

@aliiqbalIntelligenes
Copy link

aliiqbalIntelligenes commented Mar 31, 2020

I'm using asp.net core 3.1 with entity framework core cosmos db provider. In my model I've a property for LocationCoordinates of type pointer.

I'm using point class in my model class ApplicationUser. Microsoft.Azure.Cosmos.Spatial.Point for saving coordinates latitude & longitude.

CosmosDb Package I'm using https://www.nuget.org/packages/Microsoft.EntityFrameworkCore.Cosmos/

Please help.

Entity Class:

using Microsoft.AspNetCore.Identity;
using Microsoft.Azure.Cosmos.Spatial;

namespace TestApp_Backend_API.Entities
{
    public class ApplicationUser : IdentityUser
    {
        public string FirstName { get; set; }

        public string LastName { get; set; }

        public string ZipCode { get; set; }

        public Point LocationCoordinates { get; set; }
    }
}

DbContext Class:

using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using TestApp_Backend_API.Entities;

namespace TestApp_Backend_API.DbContexts
{
    public class TestAppContext : IdentityDbContext<ApplicationUser>
    {
        public TestAppContext(DbContextOptions options)
            : base(options)
        {
        }

        protected override void OnModelCreating(ModelBuilder builder)
        {
            base.OnModelCreating(builder);

            builder.HasDefaultContainer("Users");
            builder.Entity<IdentityUser>().ToContainer("Users");
            builder.Entity<ApplicationUser>().ToContainer("Users");
            builder.Entity<IdentityUserRole<string>>().ToContainer("UserRoles");
            builder.Entity<IdentityUserLogin<string>>().ToContainer("UserLogins");
            builder.Entity<IdentityUserClaim<string>>().ToContainer("UserClaims");
            builder.Entity<IdentityRole>().ToContainer("Roles");
            builder.Entity<IdentityUserToken<string>>().ToContainer("UserTokens");
        }
    }
}

Startup Class:

public void ConfigureServices(IServiceCollection services)
        {
            services.AddDbContext<RunnerContext>(options =>
            {
                options.UseCosmos(
                    "https://localhost:8081/",
                    "mykey",
                    "TestDemoDb");
            });

            services.AddControllers();

            var builder = services.AddIdentityCore<User>(options => { });
            var identityBuilder = new IdentityBuilder(builder.UserType, builder.Services);
            identityBuilder.AddRoles<IdentityRole>();
            identityBuilder.AddEntityFrameworkStores<RunnerContext>();
            identityBuilder.AddSignInManager<SignInManager<User>>();

            services.AddAuthentication();
        }

Project File:

<Project Sdk="Microsoft.NET.Sdk.Web">

  <PropertyGroup>
    <TargetFramework>netcoreapp3.1</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="3.1.3" />
    <PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="3.1.3" />
    <PackageReference Include="Microsoft.AspNetCore.Identity.UI" Version="3.1.3" />
    <PackageReference Include="Microsoft.Azure.Cosmos" Version="3.7.0" />
    <PackageReference Include="Microsoft.EntityFrameworkCore.Cosmos" Version="3.1.3" />
  </ItemGroup>

</Project>

Error:

System.InvalidOperationException: No suitable constructor found for entity type 'BoundingBox'. The following constructors had parameters that could not be bound to properties of the entity type: cannot bind 'min', 'max' in 'BoundingBox(Position min, Position max)'.
   at Microsoft.EntityFrameworkCore.Metadata.Conventions.ConstructorBindingConvention.ProcessModelFinalized(IConventionModelBuilder modelBuilder, IConventionContext`1 context)
   at Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal.ConventionDispatcher.ImmediateConventionScope.OnModelFinalized(IConventionModelBuilder modelBuilder)
   at Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal.ConventionDispatcher.OnModelFinalized(IConventionModelBuilder modelBuilder)
   at Microsoft.EntityFrameworkCore.Metadata.Internal.Model.FinalizeModel()
   at Microsoft.EntityFrameworkCore.ModelBuilder.FinalizeModel()
   at Microsoft.EntityFrameworkCore.Infrastructure.ModelSource.CreateModel(DbContext context, IConventionSetBuilder conventionSetBuilder)
   at Microsoft.EntityFrameworkCore.Infrastructure.ModelSource.GetModel(DbContext context, IConventionSetBuilder conventionSetBuilder)
   at Microsoft.EntityFrameworkCore.Internal.DbContextServices.CreateModel()
   at Microsoft.EntityFrameworkCore.Internal.DbContextServices.get_Model()
   at Microsoft.EntityFrameworkCore.Infrastructure.EntityFrameworkServicesBuilder.<>c.<TryAddCoreServices>b__7_3(IServiceProvider p)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitFactory(FactoryCallSite factoryCallSite, RuntimeResolverContext context)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSiteMain(ServiceCallSite callSite, TArgument argument)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitCache(ServiceCallSite callSite, RuntimeResolverContext context, ServiceProviderEngineScope serviceProviderEngine, RuntimeResolverLock lockType)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitScopeCache(ServiceCallSite singletonCallSite, RuntimeResolverContext context)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSite(ServiceCallSite callSite, TArgument argument)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitConstructor(ConstructorCallSite constructorCallSite, RuntimeResolverContext context)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSiteMain(ServiceCallSite callSite, TArgument argument)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitCache(ServiceCallSite callSite, RuntimeResolverContext context, ServiceProviderEngineScope serviceProviderEngine, RuntimeResolverLock lockType)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitScopeCache(ServiceCallSite singletonCallSite, RuntimeResolverContext context)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSite(ServiceCallSite callSite, TArgument argument)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.Resolve(ServiceCallSite callSite, ServiceProviderEngineScope scope)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.DynamicServiceProviderEngine.<>c__DisplayClass1_0.<RealizeService>b__0(ServiceProviderEngineScope scope)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngine.GetService(Type serviceType, ServiceProviderEngineScope serviceProviderEngineScope)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngineScope.GetService(Type serviceType)
   at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(IServiceProvider provider, Type serviceType)
   at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService[T](IServiceProvider provider)
   at Microsoft.EntityFrameworkCore.DbContext.get_DbContextDependencies()
   at Microsoft.EntityFrameworkCore.DbContext.get_InternalServiceProvider()
   at Microsoft.EntityFrameworkCore.DbContext.get_DbContextDependencies()
   at Microsoft.EntityFrameworkCore.DbContext.Set[TEntity]()
   at Microsoft.AspNetCore.Identity.EntityFrameworkCore.UserStore`9.get_UsersSet()
   at Microsoft.AspNetCore.Identity.EntityFrameworkCore.UserStore`9.get_Users()
   at Microsoft.AspNetCore.Identity.EntityFrameworkCore.UserStore`9.FindByEmailAsync(String normalizedEmail, CancellationToken cancellationToken)
   at Microsoft.AspNetCore.Identity.UserManager`1.FindByEmailAsync(String email)
   at TestApp_Backend_API.Services.AuthRepository.IsEmailExistsAsync(String email) in D:\Ali\Projects\TestApp-Backend\TestApp-BackendServices\TestApp-Backend-API\Services\AuthRepository.cs:line 303
   at TestApp_Backend_API.Controllers.AuthController.SendEmailVerificationToken(SendEmailVerificationTokenDto dto) in D:\Ali\Projects\TestApp-Backend\TestApp-BackendServices\TestApp-Backend-API\Controllers\AuthController.cs:line 317
   at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.TaskOfIActionResultExecutor.Execute(IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeActionMethodAsync>g__Logged|12_1(ControllerActionInvoker invoker)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeNextActionFilterAsync>g__Awaited|10_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Rethrow(ActionExecutedContextSealed context)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeInnerFilterAsync()
--- End of stack trace from previous location where exception was thrown ---
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeFilterPipelineAsync>g__Awaited|19_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Logged|17_1(ResourceInvoker invoker)
   at Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)
   at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)
   at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
   at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)
@smitpatel
Copy link
Contributor

Duplicate of #17317

@smitpatel smitpatel marked this as a duplicate of #17317 Mar 31, 2020
@aliiqbalIntelligenes
Copy link
Author

Can someone please give me a solution?

@aliiqbalIntelligenes
Copy link
Author

aliiqbalIntelligenes commented Mar 31, 2020

@xtellurian Can you please give me the workaround for this problem with solution.

@xtellurian
Copy link

hey @aliiqbalIntelligenes I sure can.

I did something like this:

public void Configure(EntityTypeBuilder<MyClassWithGeoJson> builder)
{
      builder.Property(e => e.GeoLocation).HasJsonConversion();
}

and the geolocation class

public class GeoLocation
{
    public GeoLocation(double lon, double lat)
    {
        Type = "Point";
        if (lat > 90 || lat < -90) { throw new ArgumentException("A latitude coordinate must be a value between -90.0 and +90.0 degrees."); }
        if (lon > 180 || lon < -180) { throw new ArgumentException("A longitude coordinate must be a value between -180.0 and +180.0 degrees."); }
        Coordinates = new double[2] { lon, lat };
    }

    [JsonProperty("type")]
    public string Type { get; set; }
    [JsonProperty("coordinates")]
    public double[] Coordinates { get; set; }

    public double? Lat() => Coordinates?[1];
    public double? Lon() => Coordinates?[0];
}

where the extension method HasJsonConversion is as follows:

public static PropertyBuilder<T> HasJsonConversion<T>(this PropertyBuilder<T> propertyBuilder)
{
    ValueConverter<T, string> converter = new ValueConverter<T, string>(
        v => JsonConvert.SerializeObject(v),
        v => JsonConvert.DeserializeObject<T>(v));

    ValueComparer<T> comparer = new ValueComparer<T>(
        (l, r) => JsonConvert.SerializeObject(l) == JsonConvert.SerializeObject(r),
        v => v == null ? 0 : JsonConvert.SerializeObject(v).GetHashCode(),
        v => JsonConvert.DeserializeObject<T>(JsonConvert.SerializeObject(v)));

    propertyBuilder.HasConversion(converter);
    propertyBuilder.Metadata.SetValueConverter(converter);
    propertyBuilder.Metadata.SetValueComparer(comparer);

    return propertyBuilder;
}

@aliiqbalIntelligenes
Copy link
Author

@xtellurian Thank you so much for the help. Then i can query like this ?

SELECT f.id
    FROM Families f
    WHERE ST_DISTANCE(f.location, {'type': 'Point', 'coordinates':[31.9, -4.8]}) < 30000

@xtellurian
Copy link

@aliiqbalIntelligenes I'm not sure

@aliiqbalIntelligenes
Copy link
Author

@xtellurian Actually i want to query for users who are within specific kilometers.

@aliiqbalIntelligenes
Copy link
Author

@xtellurian When data is in this format in cosmos db:

"DefaultLocationCoordinates": {
        "type": "Point",
        "coordinates": [
            31.9,
            -4.8
        ]
    },

Then this query runs fine

select * from c where ST_DISTANCE(c.DefaultLocationCoordinates, {'type': 'Point', 'coordinates':[31.9, -4.8]}) < 90000

But when saving in this format:

"DefaultLocationCoordinates": "{\"type\":\"Point\",\"coordinates\":[31.9,-4.8]}"

Query not returning any thing. will you help me on this?

@aliiqbalIntelligenes
Copy link
Author

@xtellurian ?

@xtellurian
Copy link

I'm sorry @aliiqbalIntelligenes but I don't know

@ajcvickers
Copy link
Contributor

@aliiqbalIntelligenes Full support for spatial types in Cosmos is not yet implemented. That's what issue #17317 is tracking. While @aliiqbalIntelligenes's workaround will allow you to store and retrieve values from the database, support for for actually running this type of query will require #17317 to be implemented.

@aliiqbalIntelligenes
Copy link
Author

@ajcvickers Thanks for clarification. When can we expect full support for spatial types in Cosmos? Any time period ?

@ajcvickers
Copy link
Contributor

@aliiqbalIntelligenes #17317 is in the Backlog milestone. This means that it is not planned for the next release (EF Core 5.0). We will re-assess the backlog following the this release and consider it at that time. However, keep in mind that there are many other high priority features with which it will be competing for resources.

@aliiqbalIntelligenes
Copy link
Author

@ajcvickers Ok Thanks.

@ajcvickers ajcvickers reopened this Oct 16, 2022
@ajcvickers ajcvickers closed this as not planned Won't fix, can't repro, duplicate, stale Oct 16, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants