是否可以使用带有内部连接的 ef core 5 运行 原始 sql 并将数据具体化为 class?

is it possible to run a raw sql using ef core 5 with inner join and materialize the data to a class?

public class UserDto
{
    public int Id {get; set;}
    public string Name {get; set;}
    public string Email {get; set;}
    public string Username {get; set;}
}

//代码第一个实体class

public class User
{
    public int Id {get; set;}
    public string Username {get; set;}
}

//代码第一个实体class

public class Profile
{
    public string Name {get; set;}
    public string Email {get; set;}
}

// 我想做这样的事情

List<UserDto> userDto = context.Database
.FromSqlRaw<List<UserDto>>("SELECT u.Id, u.Username, p.Name, p.Email FROM dbo.User u
    INNER JOIN dbo.Profile p on p.UserId = u.id 
    ").ToList();

我通常会使用视图,以便能够通过迁移处理所有 RawSql 查询。

为此,您可以在创建主要实体和迁移后执行以下操作。

为结果模型新建Class:

public class JoinedResult
{
    public int UserId { get; set; }
    public string Username { get; set; }
    public string Name { get; set; }
    public string Email { get; set; }
}

通过 运行 add-migration 命令创建空迁移,例如:

add-migration JoinedResultsView

然后像这样更新迁移:

public partial class JoinedResultsView : Migration
{
    protected override void Up(MigrationBuilder migrationBuilder)
    {
        migrationBuilder.Sql(@"CREATE VIEW JoinedResults
        AS
            SELECT        u.UserId, u.Username, p.Name, p.Email
            FROM            dbo.Users AS u INNER JOIN
        dbo.Profiles AS p ON p.UserId = u.UserId");
    }

    protected override void Down(MigrationBuilder migrationBuilder)
    {
        migrationBuilder.Sql("DROP VIEW JoinedResults");
    }
}

最后一步是使用配置将视图映射到实体 class:

public class JoinedResultConfiguration :IEntityTypeConfiguration<JoinedResult>
{
    public void Configure(EntityTypeBuilder<JoinedResult> builder)
    {
        builder.ToView("JoinedResults");
        builder.HasNoKey();
        builder.Property(p => p.Name).HasColumnName("Name");
        builder.Property(p => p.Email).HasColumnName("Email");
        builder.Property(p => p.UserId).HasColumnName("UserId");
        builder.Property(p => p.Username).HasColumnName("Username");
    }
}

然后将配置添加到 DbContext:

public class MyDbContext : DbContext
{
    public DbSet<Profile> Profiles { get; set; }
    public DbSet<User> Users { get; set; }
    public DbSet<JoinedResult> JoinedResults { get; set; }


    protected override void OnConfiguring(DbContextOptionsBuilder options) => options.UseSqlServer($"data source=.;initial catalog=TheDb;Integrated Security=True");
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.ApplyConfiguration(new JoinedResultConfiguration());
    }
}

就是这样。

不仅你可以在你的代码中得到这样的查询结果:

var queryResult = cntx.JoinedResults.ToList();

但您还可以在此基础上添加 linq 查询:

var x = cntx.JoinedResults.OrderBy(x => x.Name).FirstOrDefault(x => x.Name.Contains("MA"));

这也是一种非常有用的方法,可以提取出大查询和慢查询的公共部分并将它们重写为 Sql 视图,然后在它们之上添加 linq 查询以用于 non-common部分。

玩得开心;)