Using dapper Unit of Work in 3-tier architecture
You're correct in point #1 that passing the connection string to each service to get your UoW instance is bad form.  I'm not sure if that code was designed for a DI first world as is the case Asp.Net Core.

My recommendation (for #2) is keep it simple.  Remove the Unit of Work shell, but keep the repository contained inside.  Repository methods are usually transactional, and according to your question, you're using Dapper, so maintaining transactional consistency can be done per method call (not based on state as with the UoW).

SQL transactions are part of the natural semantics of Dapper:

public static IEnumerable&lt;T&gt; Query&lt;T&gt;(this IDbConnection cnn, string sql, object param = null, SqlTransaction transaction = null, bool buffered = true)


So your repository methods have the rough form of:

(assuming an open connection)

using(var tran = connection.BeginTransaction()) {
    try {
        // multiple operations involving connection and tran here

        tran.Commit();
    } catch {
        tran.Rollback();
        throw;
    }
}


The Dispose() is implicit to the using and you've neutralized your issue.

See: using Statement

If you absolutely need UoW style, you may consider moving to something like Entity Framework (or a heavier weight ORM in general), as the DBContext is essentially a much better featured and maintainable unit of work design, if not overkill for your project.