简体   繁体   English

如何在 Entity Framework Core 3.1 中对自定义对象执行原始 SQL 查询,而无需迁移想要创建表?

[英]How do I execute a raw SQL query to a custom object in Entity Framework Core 3.1, without migrations wanting to create a table?

I'm querying a Store table to show the user the 10 closest Store s.我正在查询Store表以向用户显示 10 个最近的Store I'd like to display the Name and Distance of the Store , but prefer to keep distance in a custom entity.我想显示StoreNameDistance ,但更喜欢在自定义实体中保持距离。

Store fields: Id , Name , Latitude , Longitude , etc Store字段: IdNameLatitudeLongitude
StoreDto fields: Id, Name , Distance` StoreDto字段: Id, Name , Distance`

This SO answer gets us on the right track, particularly with the comments.这个 SO 答案让我们走上了正确的轨道,尤其是评论。 However, DbQuery is now deprecated.但是,现在不推荐使用 DbQuery。

The docs on Keyless Entity Types say we can use a Keyless Entity Type to serve as the return type for raw SQL queries. Keyless Entity Types上的文档说我们可以使用 Keyless Entity Type 作为原始 SQL 查询的返回类型。

My DbContext already has:我的 DbContext 已经有:

public DbSet<Store> Stores { get; set; }

Adding添加

public DbSet<StoreDto> StoreDtos { get; set; }

And

modelBuilder.Entity<QuestSiteDto>()
    .HasNoKey()
    .ToView(null); // Hack to prevent table generation

Allows my store search code to work.允许我的商店搜索代码工作。 But the next time I run a migration, EF Core wants to create a StoreDto table, unless I add that ugly ToView(null) hack.但是下次我运行迁移时,EF Core 想要创建一个 StoreDto 表,除非我添加那个丑陋的ToView(null) hack。

For reference, here is my query:作为参考,这是我的查询:

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();

What is the proper way to do this?这样做的正确方法是什么? If you believe you know the recommended way, can you please cite your source?如果你相信你知道推荐的方式,你能引用你的来源吗? As of the time of this posting, the Keyless Entity Types doc page focuses more on Views and Tables rather than raw queries (unless I missed something).截至本文发布时,无键实体类型文档页面更多地关注视图和表而不是原始查询(除非我遗漏了一些东西)。

You can also query types not registered in your DbContext.您还可以查询未在 DbContext 中注册的类型。 The idea is to introduce introduce a separate single-entity DbContext type for each ad-hoc query type.这个想法是为每个 ad-hoc 查询类型引入一个单独的单实体 DbContext 类型。 Each would be initialized and cached seperately.每个将被单独初始化和缓存。

So just add an extension method like this:所以只需添加一个像这样的扩展方法:

   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);
            }
        }

    }

Or for EF Core 5:或者对于 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);
        }
    }

}

Use would look like:使用看起来像:

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");
}

This originally appeared here, but github issue comment threads aren't very discoverable: https://github.com/dotnet/efcore/issues/1862#issuecomment-451671168这最初出现在这里,但 github 问题评论线程不是很容易发现: https : //github.com/dotnet/efcore/issues/1862#issuecomment-451671168

To create the equivalent of DbQuery in ef core 3.x you add HasNoKey() and ToView() to your Entity in your modelcreating.要在 ef core 3.x 中创建等效的 DbQuery,您可以在模型创建中将 HasNoKey() 和 ToView() 添加到您的实体。 This will prevent Migrations from creating a table.这将阻止迁移创建表。

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);
    });
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 Entity Framework Core,执行原始 SQL 并参数化表名 - Entity Framework Core, execute raw SQL and parameterize table name 没有 DbSet 的原始 SQL 查询 - Entity Framework Core - Raw SQL Query without DbSet - Entity Framework Core 如何使用Entity Framework Core在多个表上执行原始联合SQL查询 - How to execute a raw union SQL query on several tables with Entity Framework Core 如何使用实体框架执行原始 SQL 查询而无需使用模型? - How to execute raw SQL query using Entity Framework without having to use a model? 如何在Microsoft实体模型框架中执行Sql Raw查询 - how to execute Sql Raw Query in Microsoft Entity Model Framework 如何将自定义 function 转换为 Entity Framework Core 3.1 的 sql 表达式 - How can I convert a custom function to a sql expression for Entity Framework Core 3.1 如何在 Entity Framework Core 中执行 SQL 查询而不修改上下文 class - How to execute SQL query in Entity Framework Core without modifying context class 实体框架核心-执行原始SQL-SQL / SQLite的相同代码 - Entity Framework Core - Execute raw sql - Same code for SQL/SQLite Entity Framework Core 6 - 你到底如何执行 SQL 查询并返回 object? - Entity Framework Core 6 - How on earth to you execute an SQL query and return an object? Entity Framework Core 如何在生产环境中执行迁移 - Entity Framework Core how to execute migrations in production environment
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM