实体框架核心:此平台不支持类型Udt.(空间数据 - 地理)

Eli*_*Eli 3 c# entity-framework asp.net-web-api entity-framework-core

我正在尝试实体框架核心,偶然发现了一个我从未见过的错误,无法弄清楚如何修复它.我正在使用.net Core Web API 2.0和EntityFramework Core 2.00-preview2-final

这是一个触发错误的简单示例.

(概念:从数据库中获取用户的简单端点)

错误:System.PlatformNotSupportedException:此平台不支持类型Udt.

有什么建议?

问题是我在我的数据库中使用地理,但我在模型中将它用作字符串,因为实体框架核心还不支持空间数据......

如何在不摆脱地理位置的情况下保持这种蛋糕的美味,这是一个重要特征吗?

编辑:请参阅我当前解决方案的答案

Eli*_*Eli 8

好的,我是如何解决它的:

目的是将地理位置保留在Entity Framework Core中(不使用DbGeography)

1)我创建了一个名为Location的结构:

public struct Location
{
    public double Longitude { get; set; }
    public double Latitude { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

2)将其添加到您的EF实体模型

public class User
{
    public Location Location { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

3)在模型构建器中隐藏它

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<User>().Ignore(x => x.Location);
}
Run Code Online (Sandbox Code Playgroud)

4)生成迁移(添加迁移迁移名称)

5)转到您的迁移文件1231randomnumbers1231_migrationname.cs并添加以下内容(这样我们创建另一个名为Location的地理类型列),然后更新您的数据库(update-database):

migrationBuilder.Sql(@"ALTER TABLE [dbo].[User] ADD [Location] geography NULL");
Run Code Online (Sandbox Code Playgroud)

6)(可选)我创建了一个静态类来更新数据库,如果在mulple表中有一个Location列,则会很方便.

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<Location> 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;
    }
}
Run Code Online (Sandbox Code Playgroud)

这仅适用于EF Core 2.0

Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("en-US");
Run Code Online (Sandbox Code Playgroud)

对于EF Core 1.0,您必须找到用'.'替换','的替代方法.一个很好的旧时尚.Replace()方法可以完成这项工作.

location.Longitude.ToString().Replace(',', '.')
Run Code Online (Sandbox Code Playgroud)

7)CRUD示例:

7.1:阅读

public async Task<User> GetByIdAsync(int id)
{
    User user =  await ctx.User.AsNoTracking().SingleOrDefaultAsync(x => x.Id == id);

    user.Location = await GeneralDB.GetLocation(ctx, "user", id);
    return user;
}
Run Code Online (Sandbox Code Playgroud)

7.2:创建

public async Task<User> CreateAsync(User entity)
{

    ctx.User.Add(entity);
    await ctx.SaveChangesAsync();
    await GeneralDB.UpdateLocation(ctx, "user", entity.Location, entity.Id);
    return entity;  
}
Run Code Online (Sandbox Code Playgroud)

7.3:更新

public async Task<User> UpdateAsync(User entity)
{
    ctx.User.Attach(entity);
    ctx.Entry<User>(entity).State = EntityState.Modified;
    await ctx.SaveChangesAsync();

    await GeneralDB.UpdateLocation(ctx, "user", entity.Location, entity.Id);

    return entity;
}
Run Code Online (Sandbox Code Playgroud)