Entity Framework Core: Type Udt is not supported on this platform. (Spatial Data - Geography)

前端 未结 3 570
醉酒成梦
醉酒成梦 2020-12-06 08:25

I\'m experimenting with entity framework core and stumbled upon an error I\'ve never seen before and can\'t figure out how to fix it. I\'m using .net Core Web API 2.0 with E

3条回答
  •  长情又很酷
    2020-12-06 08:58

    Ok here is how I solved it:

    The purpose is to keep geography in Entity Framework Core (without using DbGeography)

    1) I created a struct called Location:

    public struct Location
    {
        public double Longitude { get; set; }
        public double Latitude { get; set; }
    }
    

    2) Add it to your EF Entity Model

    public class User
    {
        public Location Location { get; set; }
    }
    

    3) Hide it in your modelbuilder

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity().Ignore(x => x.Location);
    }
    

    4) Generate a Migration (Add-Migration migrationname)

    5) Go to your migration file 1231randomnumbers1231_migrationname.cs and add the following (this way we create another column of type geography named Location) and then update your database (update-database):

    migrationBuilder.Sql(@"ALTER TABLE [dbo].[User] ADD [Location] geography NULL");
    

    6) (optional) I created a static class to update the db, handy if you have a Location column in mulple tables.

    public static class GeneralDB
    {
    
        public static async Task UpdateLocation(DbContext ctx, string table, Location location, int id)
        {
            Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("en-US");
    
            string query = String.Format(@"UPDATE [dbo].[{0}] SET Location = geography::STPointFromText('POINT(' + CAST({1} AS VARCHAR(20)) + ' ' + CAST({2} AS VARCHAR(20)) + ')', 4326) WHERE(ID = {3})"
            , table.ToLower(), location.Longitude, location.Latitude, id);
            await ctx.Database.ExecuteSqlCommandAsync(query);
        }
        public static async Task GetLocation(DbContext ctx, string table, int id)
        {
            Location location = new Location();
    
            using (var command = ctx.Database.GetDbConnection().CreateCommand())
            {
                string query = String.Format("SELECT Location.Lat AS Latitude, Location.Long AS Longitude FROM [dbo].[{0}] WHERE Id = {1}"
                    , table, id);
                command.CommandText = query;
                ctx.Database.OpenConnection();
                using (var result = command.ExecuteReader())
                {
                    if (result.HasRows)
                    {
                        while (await result.ReadAsync())
                        {
                            location.Latitude = result.GetDouble(0);
                            location.Longitude = result.GetDouble(1);
                        }
                    }
    
                }
            }
    
            return location;
        }
    }
    

    This only works in EF Core 2.0

    Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("en-US");
    

    For EF Core 1.0 you would have to find an alternative way to replace a ',' with '.'. A good old fashion .Replace() method could do the job.

    location.Longitude.ToString().Replace(',', '.')
    

    7) CRUD Examples:

    7.1: Read

    public async Task GetByIdAsync(int id)
    {
        User user =  await ctx.User.AsNoTracking().SingleOrDefaultAsync(x => x.Id == id);
    
        user.Location = await GeneralDB.GetLocation(ctx, "user", id);
        return user;
    }
    

    7.2: Create

    public async Task CreateAsync(User entity)
    {
    
        ctx.User.Add(entity);
        await ctx.SaveChangesAsync();
        await GeneralDB.UpdateLocation(ctx, "user", entity.Location, entity.Id);
        return entity;  
    }
    

    7.3: Update

    public async Task UpdateAsync(User entity)
    {
        ctx.User.Attach(entity);
        ctx.Entry(entity).State = EntityState.Modified;
        await ctx.SaveChangesAsync();
    
        await GeneralDB.UpdateLocation(ctx, "user", entity.Location, entity.Id);
    
        return entity;
    }
    

提交回复
热议问题