I have a class that inserts and loads any model from a SQL Server:
public class SqlDataAccess : ISqlDataAccess
{
    private readonly IConfiguration _configuration;
    public SqlDataAccess(IConfiguration configuration)
    {
        _configuration = configuration;
    }
    public async Task<IEnumerable<T>> LoadData<T, U>(string storedProcedure,
                                                     U parameters,
                                                     string connectionId = "Default")
    {
        using IDbConnection connection = new SqlConnection(_configuration.GetConnectionString(connectionId));
        return await connection.QueryAsync<T>(storedProcedure,
                                              parameters,
                                              commandType: CommandType.StoredProcedure);
    }
    public async Task SaveData<T>(string storedProcedure,
                                  T parameters,
                                  string connectionId = "Default")
    {
        using IDbConnection connection = new SqlConnection(_configuration.GetConnectionString(connectionId));
        await connection.ExecuteAsync(storedProcedure,
                                      parameters,
                                      commandType: CommandType.StoredProcedure);
    }
}
They are all functions calling stored procedures, for example:
CREATE PROCEDURE [dbo].[spAuthors_Insert]
    @FirstName NVARCHAR(46),
    @LastName NVARCHAR(46)
AS
BEGIN
    INSERT INTO [dbo].[Authors] (FirstName, LastName)
    VALUES (@FirstName, @LastName);
END
Which uses the following table:
CREATE TABLE [dbo].[Authors]
(
    [Id] INT NOT NULL PRIMARY KEY IDENTITY,
    [FirstName] NVARCHAR(46),
    [LastName] NVARCHAR(46)
)
To insert an author, I use the following function:
public Task InsertAuthor(AuthorModel author) => _db.SaveData("dbo.spAuthors_Insert", new { author.FirstName, author.LastName });
Now I need to change so that when I insert an author it returns the register with the id, I've tried to change for QueryFirstAsync but it doesn't return the model back. What I can change to make this generic function return the saved register?
Also on a side note, is it possible on the LoadData function pass multiple models so I can split them when I use a stored procedure with joins like:
CREATE PROCEDURE [dbo].[spArts_GetByUserId]
    @Id NVARCHAR(256)
AS
BEGIN
    SELECT *
    FROM [dbo].[Arts]
    INNER JOIN [dbo].[Registrations] ON [dbo].[Arts].id = [dbo].[Registrations].ArtId
    INNER JOIN [dbo].[Arts_details] ON [dbo].[Arts].Arts_details_id = [dbo].[Arts_details].Id
    INNER JOIN [dbo].[Authors] ON [dbo].[Arts_details].AuthorId = [dbo].[Authors].Id
    WHERE UserId = @Id;
END
An example of a simple get:
public async Task<ArtsModel?> GetArt(int id)
{
    var result_art = await _db.LoadData<ArtsModel, dynamic>("dbo.spArts_Get", new { Id = id });
    var art = result_art.FirstOrDefault();
    return art;
}