私はエンティティフレームワークのコアを試していて、これまでに見たことがないエラーに遭遇し、それを修正する方法を理解できません。私はEntityFramework Core 2.00-preview2-finalを持つ.net Core Web API 2.0を使用しています
次に、エラーを引き起こす簡単な例を示します。
(概念:データベースからユーザーを取得する単純なエンドポイント)
エラー:System.PlatformNotSupportedException:このプラットフォームでは、Udt型はサポートされていません。
助言がありますか?
問題は、データベースで地理情報を使用していることですが、エンティティフレームワークのコアがまだ空間データをサポートしていないため、モデルの文字列として使用しています。
地理を取り除かずにこのケーキを美味しく保つ方法は、それが重要な機能なのですか?
編集:現在の解決策については私の答えを見てください
私はそれを解決した方法です。
目的は、(DbGeographyを使用せずに)Entity Frameworkコアの地理を維持することです。
1)Locationという構造体を作成しました。
public struct Location
{
public double Longitude { get; set; }
public double Latitude { get; set; }
}
2)EFエンティティモデルに追加する
public class User
{
public Location Location { get; set; }
}
3)あなたのモデルビルダでそれを隠す
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<User>().Ignore(x => x.Location);
}
4)マイグレーションを生成する(マイグレーションの追加マイグレーション名)
5)マイグレーションファイル1231randomnumbers1231_migrationname.csに移動し、次のように追加して(Locationという名前の地理タイプの別の列を作成します)、データベースを更新します(update-database)。
migrationBuilder.Sql(@"ALTER TABLE [dbo].[User] ADD [Location] geography NULL");
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;
}
}
これはEF Core 2.0でのみ動作します
Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("en-US");
EF Core 1.0では、 '、'を '。'で置き換える代わりの方法を見つける必要があります。良い古いファッション.Replace()メソッドは仕事をすることができます。
location.Longitude.ToString().Replace(',', '.')
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;
}
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;
}
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;
}
更新:EF Core 2.2以降、空間データのサポートがあります。
http:// portainer /#/ stacks / kolibry-acc_8ssg5qantkr2dgrbxsamsikf6
エリ、tnxはあなたの解決策です。私にとっては、それはほとんど完璧な解決策でした。私は2つの問題を抱えていました:
問題
ソリューション
create trigger VisitLocation_trigger on Visit
after UPDATE, INSERT, DELETE
as
if exists(SELECT * from inserted)
If exists(Select * from deleted)
BEGIN
-- UPDATE
UPDATE visit_location SET location = GEOGRAPHY::Point(Latitude, Longitude, 4326) FROM visit_location JOIN inserted ON visit_location.visitid = inserted.id
END
else
BEGIN
-- INSERT
INSERT INTO visit_location SELECT Id, GEOGRAPHY::Point(Latitude, Longitude, 4326) FROM inserted
END
else
BEGIN
-- DELETE
declare @visitId int;
SELECT @visitId = Id from deleted i;
DELETE visit_location WHERE visit_location.visitid = @visitId
end
_context.Visit.FromSql(
"SELECT TOP 50 v.* " +
"FROM visit v " +
"INNER JOIN visit_location vl ON v.id = vl.visitid " +
"WHERE v.date > {0} " +
"AND GEOGRAPHY::Point({1},{2}, 4326).STDistance(Location) < {3} " +
"ORDER BY GEOGRAPHY::Point({1},{2}, 4326).STDistance(Location)",
startDate, latitude, longitude, radius).ToList();
CRUD
読む
public async Task<Visit> GetByIdAsync(int id)
{
return await _context.Visit.AsNoTracking().SingleOrDefaultAsync(x => x.Id == id);
}
public IList<Visit> GetLastVisitsForHouseIdsByCoordinates(DateTime startDate, double longitude, double latitude, long radius)
{
return
_context.Visit.FromSql("SELECT TOP 50 v.* " +
"FROM visit v " +
"INNER JOIN visit_location vl ON v.id = vl.visitid " +
"WHERE v.IsLastVisit = 1 " +
"AND v.date > {0} " +
"AND GEOGRAPHY::Point({1},{2}, 4326).STDistance(Location) < {3} " +
"ORDER BY GEOGRAPHY::Point({1},{2}, 4326).STDistance(Location)",
startDate, latitude, longitude, radius).ToList();
}
作成する
public async Task<Visit> CreateAsync(Visit visit)
{
_context.Visit.Add(visit);
await _context.SaveChangesAsync();
return visit;
}
更新
public async Task<Visit> UpdateAsync(Visit visit)
{
_context.Visit.Attach(visit);
_context.Entry(visit).State = EntityState.Modified;
await _context.SaveChangesAsync();
return visit;
}
削除する
public async Task DeleteAsync(Visit visit)
{
_dbContext.Remove(entityToUpdate);
_context.Entry(visit).State = EntityState.Deleted;
await _context.SaveChangesAsync();
return visit;
}
データベースモデル
public class Visit
{
public int Id { get; set; }
[Required]
public VisitStatus Status { get; set; }
[Required]
public double? Latitude { get; set; }
[Required]
public double? Longitude { get; set; }
public Location Location { get; set; }
[Required]
public DateTime Date { get; set; }
public string Street { get; set; }
public int? StreetNumber { get; set; }
public string StreetNumberLetter { get; set; }
public string StreetNumberLetterAddition { get; set; }
public string City { get; set; }
}
public struct Location
{
public double Longitude { get; set; }
public double Latitude { get; set; }
}