Store
テーブルをクエリして、ユーザーに最も近い10個のStore
sを表示しています。 Name
のDistance
とStore
を表示したいのですが、カスタムエンティティでは距離を保ちたいと考えています。
Store
フィールド:Id
、Name
、Latitude
、Longitude
などStoreDto
フィールド:_Id,
_ Name _,
_ Distance`
これはSO回答 で、特にコメントを使用して適切な方向に進みます。ただし、DbQueryは非推奨になりました。
Keyless Entity Types のドキュメントでは、Keyless Entity Typeを使用して生のSQLクエリの戻り値の型として使用できると述べています。
私のDbContextはすでに持っています:
_public DbSet<Store> Stores { get; set; }
_
追加
_public DbSet<StoreDto> StoreDtos { get; set; }
_
そして
_modelBuilder.Entity<QuestSiteDto>()
.HasNoKey()
.ToView(null); // Hack to prevent table generation
_
店舗検索コードを機能させます。しかし、次に移行を実行するとき、醜いToView(null)
ハックを追加しない限り、EF CoreはStoreDtoテーブルを作成する必要があります。
参考までに、これが私のクエリです:
_var sql =
@"select
geography::Point({0}, {1}, 4326).STDistance(geography::Point(Latitude, Longitude, 4326)) / 1609.34 as Distance,
Id,
[Name]
from
Store"
var results = await StoreDtos
.FromSqlRaw(sql, latitudeUnsafe, longitudeUnsafe)
.OrderBy(x => x.Distance)
.Take(10)
.ToListAsync();
_
これを行う適切な方法は何ですか?推奨される方法を知っていると思われる場合は、出典を引用していただけますか?この投稿の時点では、キーレスエンティティタイプのドキュメントページでは、生のクエリではなく、ビューとテーブルに重点が置かれています(私が何かを見落とした場合を除きます)。
DbContextに登録されていないタイプをクエリすることもできます。このアイデアは、アドホッククエリタイプごとに個別の単一エンティティのDbContextタイプを導入することです。それぞれが個別に初期化およびキャッシュされます。
したがって、次のような拡張メソッドを追加するだけです。
public static class SqlQueryExtensions
{
public static IList<T> SqlQuery<T>(this DbContext db, Func<T> targetType, string sql, params object[] parameters) where T : class
{
return SqlQuery<T>(db, sql, parameters);
}
public static IList<T> SqlQuery<T>(this DbContext db, string sql, params object[] parameters) where T : class
{
using (var db2 = new ContextForQueryType<T>(db.Database.GetDbConnection()))
{
return db2.Query<T>().FromSql(sql, parameters).ToList();
}
}
class ContextForQueryType<T> : DbContext where T : class
{
DbConnection con;
public ContextForQueryType(DbConnection con)
{
this.con = con;
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
//switch on the connection type name to enable support multiple providers
//var name = con.GetType().Name;
optionsBuilder.UseSqlServer(con);
base.OnConfiguring(optionsBuilder);
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
var t = modelBuilder.Query<T>();
//to support anonymous types, configure entity properties for read-only properties
foreach (var prop in typeof(T).GetProperties(BindingFlags.Instance | BindingFlags.Public ))
{
if (!prop.CustomAttributes.Any(a => a.AttributeType == typeof(NotMappedAttribute)))
{
t.Property(prop.Name);
}
}
base.OnModelCreating(modelBuilder);
}
}
}
使用は次のようになります。
using (var db = new Db())
{
var results = db.SqlQuery<ArbitraryType>("select 1 id, 'joe' name");
//or with an anonymous type like this
var results2 = db.SqlQuery(() => new { id =1, name=""},"select 1 id, 'joe' name");
}
これは最初はここに表示されましたが、githubの問題のコメントスレッドはあまり発見できません: https://github.com/dotnet/efcore/issues/1862#issuecomment-451671168
Efコア3.xでDbQueryに相当するものを作成するには、モデル作成でエンティティにHasNoKey()およびToView()を追加します。これにより、移行によってテーブルが作成されなくなります。
public DbSet<Store> Stores { get; set; }
public DbSet<StoreDto> StoreDtos { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<StoreDtos>(sd =>
{
sd.HasNoKey().ToView(null);
});
}