简体   繁体   English

如何在 EF Core 3.14 中运行存储过程

[英]How to run a stored procedure in EF Core 3.14

I am working on the security and user management of a new platform built entirely in .NET Core.我正在研究完全在 .NET Core 中构建的新平台的安全性和用户管理。

In particular I am trying to generate a random password for new users.特别是我正在尝试为新用户生成一个随机密码。 I have loaded a large list of English words into a table and created a stored procedure to select random words from the table and compose a password in the correct-horse-battery-staple format.我已将大量英语单词列表加载到表中,并为表中的 select 随机单词创建了一个存储过程,并以正确的马电池主食格式编写密码。

The stored procedure ( Passwords.GenerateRandomPassword ) takes no parameters and returns a single line varchar column named password .存储过程 ( Passwords.GenerateRandomPassword ) 不接受任何参数并返回名为password的单行varchar列。

Everything works up to this point.到目前为止一切正常。 I can run the query directly against the server and it works fine.我可以直接对服务器运行查询,它工作正常。

I have a method on my userRepository like so:我的 userRepository 上有一个方法,如下所示:

public async Task<string> GenerateRandomPassword()
{

}

but I cannot figure out how to get EF Core 3.14 to call this stored procedure and return a value.但我无法弄清楚如何让 EF Core 3.14 调用这个存储过程并返回一个值。

Documentation may not be up to date, or maybe I'm missing an assembly reference.文档可能不是最新的,或者我可能缺少程序集参考。

The context object and the context.database object do not seem to contain any methods that look like they will allow me to execute a stored procedure and retrieve a value.上下文 object 和 context.database object 似乎不包含任何看起来允许我执行存储过程并检索值的方法。

Documentation seems to suggest that there should be a FromSQL method or similar.文档似乎表明应该有FromSQL方法或类似方法。

Any help will be greatly appreciated.任何帮助将不胜感激。

The general solution is to call db.Database.GetDbConnection(), which gives you the ADO.NET connection object that you can use directly.一般的解决方案是调用db.Database.GetDbConnection(),它给你ADO.NET连接object,你可以直接使用。

eg例如

var con = (SqlConnection)db.Database.GetDbConnection();
con.Open();
var cmd = con.CreateCommand();
cmd.CommandText = "exec ...";

There's also the db.Database.ExecuteSqlxxx methods, which work for simple cases.还有用于简单情况的db.Database.ExecuteSqlxxx方法。

What you want to look at is keyless entity types .您要查看的是无键实体类型

This is a new feature in EF Core 3.0.这是 EF Core 3.0 中的一项新功能

One usage is to retrieve data from raw sql queries.一种用法是从原始 sql 查询中检索数据。

Using PostgreSQL .使用PostgreSQL

In PostgreSQL we create a function to generate a password:在 PostgreSQL 我们创建一个 function 来生成密码:

CREATE OR REPLACE FUNCTION generate_password() RETURNS text AS $$
  BEGIN
    RETURN (SELECT substring(md5(random()::text) from 0 for 12));
  END
$$ LANGUAGE plpgsql;

We create our entity:我们创建我们的实体:

public class PasswordGenerator
{
    public string Password { get; set; }
}

In our application's DbContext we configure our entity:在我们应用程序的 DbContext 中,我们配置我们的实体:

public DbSet<PasswordGenerator> PasswordGenerator { get; set; }

public MyDbContext(DbContextOptions options) 
    : base(options)
{}

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<PasswordGenerator>().HasNoKey();
}

We use the FromSqlRaw method to call our function that returns our password:我们使用FromSqlRaw方法调用返回密码的 function:

public async Task<string> GetGeneratedPassword()
{
    var pg = await _context.PasswordGenerator
        .FromSqlRaw(@"SELECT * from generate_password() AS ""Password""")
        .FirstAsync();

    return pg.Password;
}

We use the alias "Password" to correctly map our query to our entity.我们使用别名“密码”来正确 map 我们对实体的查询。

The two packages installed are: Microsoft.EntityFrameworkCore and Npgsql.EntityFrameworkCore.PostgreSQL .安装的两个包是: Microsoft.EntityFrameworkCoreNpgsql.EntityFrameworkCore.PostgreSQL

Using SQL Server .使用SQL 服务器

We create a stored procedure to generate a password:我们创建一个存储过程来生成密码:

CREATE OR ALTER PROCEDURE generate_password
AS
    SET NOCOUNT ON
    SELECT SUBSTRING (CONVERT(varchar(255), NEWID()), 0, 12) AS "Password"
    RETURN  
GO

Use the alias "Password" to correctly map our query to our entity.使用别名“密码”正确 map 我们对实体的查询。

Here's how we use the FromSqlRaw method:下面是我们如何使用FromSqlRaw方法:

public async Task<string> GetGeneratedPassword()
{
    var pg = (await _context.PasswordGenerator
        .FromSqlRaw("EXEC generate_password")
        .ToListAsync())
        .First();

    return pg.Password;
}

LINQ queries expect our raw queries to be composable , which is why we call ToListAsync() right after the FromSqlRaw method. LINQ 查询期望我们的原始查询是可组合的,这就是我们在FromSqlRaw方法之后立即调用ToListAsync()的原因。

SQL Server doesn't allow composing over stored procedure calls, so any attempt to apply additional query operators to such a call will result in invalid SQL. SQL 服务器不允许组合存储过程调用,因此任何尝试将其他查询运算符应用于此类调用都会导致无效的 SQL。 Use AsEnumerable or AsAsyncEnumerable method right after FromSqlRaw or FromSqlInterpolated methods to make sure that EF Core doesn't try to compose over a stored procedure.FromSqlRawFromSqlInterpolated方法之后使用AsEnumerableAsAsyncEnumerable方法,以确保 EF Core 不会尝试通过存储过程进行组合。

The two packages installed are: Microsoft.EntityFrameworkCore and Microsoft.EntityFrameworkCore.SqlServer安装的两个包是: Microsoft.EntityFrameworkCoreMicrosoft.EntityFrameworkCore.SqlServer

UPDATE - Using the GetDbConnection method更新- 使用GetDbConnection方法

Thanks to the answer provided by @David Browne - Microsoft, we can call the GetDbConnection extension method to access the database directly.感谢@David Browne - Microsoft 提供的答案,我们可以调用GetDbConnection扩展方法直接访问数据库。

public async Task<string> GetGeneratedPassword()
{
    var password = "";
    var connection = _context.Database.GetDbConnection();
    try
    {
        await connection.OpenAsync();
        using (var command = connection.CreateCommand())
        {
            // SQL Server
            command.CommandText = "EXEC generate_password";

            // PostgreSQL
            // command.CommandText = @"SELECT * from generate_password()";
            using (var reader = await command.ExecuteReaderAsync())
            {
                if (await reader.ReadAsync())
                {
                    password = reader.GetString(0);
                }
            }
        }
    }
    finally
    {
        await connection.CloseAsync();
    }

    return password;
}

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

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM