你想看的是keyless entity types。
这是 EF Core 3.0 中的 new feature。
一个usage是从原始sql查询中检索数据。
使用 PostgreSQL。
在 PostgreSQL 中,我们创建了一个函数来生成密码:
CREATE OR REPLACE FUNCTION generate_password() RETURNS text AS $$
BEGIN
RETURN (SELECT substring(md5(random()::text) from 0 for 12));
END
$$ LANGUAGE plpgsql;
我们创建我们的实体:
public class PasswordGenerator
{
public string Password { get; set; }
}
在我们应用程序的 DbContext 中,我们 configure 我们的实体:
public DbSet<PasswordGenerator> PasswordGenerator { get; set; }
public MyDbContext(DbContextOptions options)
: base(options)
{}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<PasswordGenerator>().HasNoKey();
}
我们使用FromSqlRaw 方法调用返回密码的函数:
public async Task<string> GetGeneratedPassword()
{
var pg = await _context.PasswordGenerator
.FromSqlRaw(@"SELECT * from generate_password() AS ""Password""")
.FirstAsync();
return pg.Password;
}
我们使用别名“密码”将我们的查询正确映射到我们的实体。
安装的两个包是:Microsoft.EntityFrameworkCore和Npgsql.EntityFrameworkCore.PostgreSQL。
使用 SQL Server。
我们创建一个存储过程来生成密码:
CREATE OR ALTER PROCEDURE generate_password
AS
SET NOCOUNT ON
SELECT SUBSTRING (CONVERT(varchar(255), NEWID()), 0, 12) AS "Password"
RETURN
GO
使用别名“密码”将我们的查询正确映射到我们的实体。
下面是我们如何使用FromSqlRaw 方法:
public async Task<string> GetGeneratedPassword()
{
var pg = (await _context.PasswordGenerator
.FromSqlRaw("EXEC generate_password")
.ToListAsync())
.First();
return pg.Password;
}
LINQ 查询期望我们的原始查询是 composable,这就是我们在 FromSqlRaw 方法之后立即调用 ToListAsync() 的原因。
SQL Server 不允许组合存储过程调用,因此任何
尝试将其他查询运算符应用于此类调用将导致
在无效的 SQL 中。正确使用AsEnumerable 或AsAsyncEnumerable 方法
在FromSqlRaw 或FromSqlInterpolated 方法之后确保EF
Core 不会尝试对存储过程进行组合。
安装的两个包是:Microsoft.EntityFrameworkCore和Microsoft.EntityFrameworkCore.SqlServer
更新 - 使用GetDbConnection 方法
感谢@David Browne - Microsoft 提供的answer,我们可以调用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;
}