c# - 如何在 Entity Framework Core 3.1 中对自定义对象执行原始 SQL 查询,而不需要创建表的迁移?
问题描述
我正在查询一个Store
表以向用户显示 10 个最接近Store
的 s。我想显示Name
和Distance
,Store
但更喜欢在自定义实体中保持距离。
Store
字段:、、、、Id
等Name
字段:名称距离`Latitude
Longitude
StoreDto
Id,
,
这个 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
允许我的商店搜索代码工作。但是下次我运行迁移时,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();
这样做的正确方法是什么?如果你相信你知道推荐的方式,你能引用你的来源吗?截至本文发布之时,Keyless Entity Types 文档页面更多地关注视图和表,而不是原始查询(除非我遗漏了什么)。
解决方案
您还可以查询未在 DbContext 中注册的类型。这个想法是为每个 ad-hoc 查询类型引入一个单独的单实体 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
推荐阅读
- batch-file - 可以从文本文件中读取特定行/字母的批处理代码
- c++ - 结构的共享指针以在所有文件中使用处理程序的实例
- java - 在 Java 中导入新框架?
- date - Redshift 存储过程未正确传递日期
- python - 如何删除 nlargest 元素?
- java - 注册错误:java.lang.string 类型的值 br 无法转换为 jsonobject
- flask - Gunicorn --preload 选项导致工人挂起?
- sql - 在 SQL 中设置一个变量,并使用该变量来计算需要多少个框才能适合给定尺寸的项目列表
- javascript - 如何使一个div的margin-left等于另一个div的margin-left
- jquery - 如何从自动完成建议中接收关键字,而不在 jQuery UI 的末尾添加逗号?