質問: **バックグラウンド** 私は郵便番号の特定の半径内にイベントを見つけるアプリケーションを書いています。あなたは郵便番号を入力し、xの半径にあるすべてのコンサートが現れるチケットマスターのように考えることができます。 私は郵便番号と各郵便番号の緯度と経度を持つデータベーステーブルを持っています。私はまた、それぞれの 'Event'にZipCodeフィールドがある 'EventListings'テーブルを持っています。 **問題** 現在、サービス層のLinq-to-EntitiesクエリでHaversine式を使用して、どのイベントが半径内にあるか調べています。今、私はそれをwhere句のフィルターとして使用しています。私はselect句にも入れたいので、ウェブサイトでは "これは4.6マイル離れている"と表示することができます。 私は別のC#メソッドにこのコードを移動することはできません。なぜなら、Linq-to-EntitiesはSQLに変換できないという不平を言います。そのため、select文でも式全体を複製する必要があります。これは非常に醜いです。私はそれを修正しようとしました。 **私が試したこと** エンティティを編集し、 "DistanceFromOrigin"という特殊なスカラープロパティを追加しました。次に、すべてのエンティティデータと、新しいフィールド "DistanceFromOrigin"のハードコード値(テスト目的)を戻したストアドプロシージャを作成しました。 私はエンティティフレームワークにEventListingsエンティティのselectステートメントでsprocを使用するように指示できないことに気がつきました... Philは空間を提案しました。 **質問** Spatialを使用して、郵便番号の半径内のイベントを検索するにはどうすればよいですか?
回答: だから、フィルの提案を使って、私は空間を使ってこれをたくさん書き直しました。これは素晴らしく、.NET4.5、EF5 +、SQLサーバー(2008年以降)が必要です。私はEF6 + Sql Server 2012を使用しています。 **セットアップ** =========== 最初のステップは、データベース`EventListings`テーブルに`Geography`カラムを追加することでした(右クリック - >デザイン)。私は私の場所を指定した: !ここに画像の説明を入力 次に、データベースでEDMを使用しているので、作成した新しいフィールドを使用するようにモデルを更新する必要がありました。 Geographyをdoubleに変換できないというエラーが発生しました。そのため、エンティティのLocationプロパティを選択し、そのプロパティに移動してそのタイプをdoubleから`Geography`変更しました。 !ここに画像の説明を入力 **半径内のクエリと場所によるイベントの追加** ========================= それでは、あなたのエンティティコレクションを次のように照会するだけです。 ``` var events = EventRepository.EventListings .Where(x => x.Location.Distance(originCoordinates) * 0.00062 <= radiusParam); ``` Distance extensionメソッドは、現在のオブジェクトから、渡した「他の」オブジェクトまでの距離を取得します。この他のオブジェクトは、 `DbGeography`タイプ`DbGeography` 。ちょうど静的メソッドを呼び出すと、これらの子犬のいずれかを作成し、ちょうどポイントとしてあなたの経度と緯度を投げる: ``` DBGeography originCoordinates = DBGeography.fromText("Point(" + originLongitude + " " + originLatitude + ")"); ``` これは私がoriginCoordinatesをどのように作成したかではありません。私はすべての郵便番号とその緯度と経度のリストを持った別のデータベースをダウンロードしました。私はそれにも`Geography`タイプの列を追加しました。私はこの答えの最後にどのように表示されますか。次にZipcodeコンテキストを照会して、ZipCodeテーブルのLocationフィールドからDbGeographyオブジェクトを取得しました。 ユーザーがちょうど郵便番号よりも特定の起源を望む場合、私はGoogle Maps API(GeoCodeより具体的に)を呼び出し、ウェブサービスを介してユーザー固有の住所の緯度と経度を取得してからDBGeographyオブジェクトを作成します応答の緯度と経度の値。 私はイベントを作成するときにGoogle APIを使用します。エンティティをEFに追加する前に、次のような場所変数を設定するだけです。 ``` someEventEntity.Location = DBGeography.fromText("Point(" + longitudeFromGoogle+ " " + latitudeFromGoogle + ")"); ``` **その他のヒントとテクニックとトラブルシューティング** ============================== **私はどのようにDistance Extension Methodを取得しましたか?** 拡張メソッド`Distance`を取得するには、プロジェクトへの参照を追加する必要があります`System.Data.Entity`を実行した後、using: `using System.Data.Entity.Spatial;`追加する必要があり`using System.Data.Entity.Spatial;`あなたのクラスに。 `Distance` extensionメソッドは**メートルの単位の単位で**距離を返します(あなたはそれを変更することができますが、これはデフォルトです)。ここで、私の半径パラメータはマイル単位であったので、変換する数学をしました。 **注** : `System.Data.Spatial`は`DBGeography`クラスがあります。これは**間違ったもので** 、私にとってはうまくいかないでしょう。私がインターネット上で見つけた多くの例は、これを使っています。 **緯度/経度を地理情報の列に変換する方法** あなたが私のようで、すべての`Latitude`と`Longitude`列を含む郵便番号データベースをダウンロードして、それが地理学の列を持っていないことに気づいたなら、これは助けになるかもしれません: 1)場所の列をテーブルに追加します。型を地理に設定します。 2)次のSQLを実行します ``` UPDATE [ZipCodes] SET Location = geography::STPointFromText('POINT(' + CAST([Longitude] AS VARCHAR(20)) + ' ' + CAST([Latitude] AS VARCHAR(20)) + ')', 4326) ``` **地理アイテムの詳細を表示する方法** したがって、DbGeographyアイテムを挿入した後でSql Server Management StudioのEventListingsテーブルをクエリすると、 `Location`列に0x1234513462346のような16進値が保持されていることがわかります。これは、正しい値が挿入されたことを確認したいときにはあまり役に立ちません。 このフィールドの緯度と経度を実際に表示するには、次のようにクエリを実行する必要があります。 ``` SELECT Location.Lat Latitude, Location.Long Longitude FROM [EventListings] ```