我正在查询 Store
表以向用户显示 10 个最近的Store
。我想显示 Store
的名称
和距离
,但更喜欢在自定义实体中保持距离。
Store
字段:Id
、Name
、Latitude
、Longitude
等
StoreDto
字段:Id、
名称、
距离`
This SO answer让我们走上正轨,尤其是评论。不过,DbQuery 现已弃用。
Keyless Entity Types 上的文档假设我们可以使用无键实体类型作为原始 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
允许我的商店搜索代码工作。但下次我运行迁移时,EF Core 想要创建一个 StoreDto 表,除非我添加那个丑陋的 ToView(null)
hack。
作为引用,这是我的查询:
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();
执行此操作的正确方法是什么?如果您相信您知道推荐的方式,您可以引用您的来源吗?截至本文发布时,无键实体类型文档页面更多地关注 View 和表,而不是原始查询(除非我错过了一些内容)。
最佳答案
您还可以查询未在 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);
}
}
}
或者对于 EF Core 5:
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.Set<T>().FromSqlRaw(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.Entity<T>().HasNoKey();
//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
关于c# - 如何在 Entity Framework Core 3.1 中对自定义对象执行原始 SQL 查询,而不需要创建表的迁移?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59620619/