6

The code below seems to work using this:

var unitOfWorkProvider = new PetaPocoUnitOfWorkProvider();
var repository = new FakeRepository();
var fake = new Fake
{     
    // etc.
};

using (var uow = unitOfWorkProvider.GetUnitOfWork("BlaConnectionString"))
{
    repository.Insert(uow, fake);
    uow.Commit();
}

which may eventually move into a service layer. I would appreciate any feedback to improve this code.

public interface IUnitOfWork : IDisposable
{
    void Commit();
    Database Database { get; }
}

public interface IUnitOfWorkProvider
{
    IUnitOfWork GetUnitOfWork(string connectionString);
}

public class PetaPocoUnitOfWorkProvider : IUnitOfWorkProvider
{
    public IUnitOfWork GetUnitOfWork(string connectionString)
    {
        return new PetaPocoUnitOfWork(connectionString);
    }
}

public interface IRepository<T>
{
    void Insert(IUnitOfWork unitOfWork, T entity);
    void Update(IUnitOfWork unitOfWork, T entity);
    void Delete(IUnitOfWork unitOfWork, T entity);
    T Fetch(IUnitOfWork unitOfWork, long uid);
}

public class PetaPocoUnitOfWork : IUnitOfWork
{
    private readonly Transaction _petaTransaction;
    private readonly Database _database;

    public PetaPocoUnitOfWork(string connectionString)
    {
        _database = new Database(connectionString);
        _petaTransaction = new Transaction(_database);
    }

    public void Dispose()
    {
        _petaTransaction.Dispose();
    }

    public Database Database
    {
        get { return _database; }
    }

    public void Commit()
    {
        _petaTransaction.Complete();
    }
}

public class FakeRepository : IRepository<Fake>
{
    public void Insert(IUnitOfWork unitOfWork, Fake entity)
    {
        unitOfWork.Database.Save(entity);
    }

    public void Update(IUnitOfWork unitOfWork, Fake entity)
    {
        unitOfWork.Database.Update(entity);
    }

    public void Delete(IUnitOfWork unitOfWork, Fake entity)
    {
        unitOfWork.Database.Delete(entity);
    }

    public FakeJobFact Fetch(IUnitOfWork unitOfWork, long uid)
    {
        return unitOfWork.Database.Fetch<Fake>("SELECT * FROM Fakes WHERE [FakeId] = @0", uid).FirstOrDefault();
    }
}

PS:

I have adapted the code according to @Plebsori current answer:

public abstract class BaseRepository<T>
{
    protected IDatabase Database
    {
        get
        {
        return UnitOfWork.Current;
        }
    }

    public void Insert(T entity)
    {
        Database.Save(entity);
    }

    public void Update(T entity)
    {
        Database.Update(entity);
    }

    public void Delete(T entity)
    {
        Database.Delete(entity);
    }
}

public interface IRepository<T>
{
    void Insert(T entity);
    void Update(T entity);
    void Delete(T entity);
    T Fetch(long uid);
}

public interface IUnitOfWork : IDisposable
{
    void Commit();
    Database Database { get; }
}

public interface IUnitOfWorkProvider
{
    IUnitOfWork GetUnitOfWork(string connectionString);
}

public class PetaPocoUnitOfWork : IUnitOfWork
{
    private readonly Transaction _petaTransaction;
    private readonly Database _database;

    public PetaPocoUnitOfWork(string connectionString)
    {
        _database = new Database(connectionString);
        _petaTransaction = new Transaction(_database);
    }

    public void Dispose()
    {
        UnitOfWork.Current = null;
        _petaTransaction.Dispose();
    }

    public Database Database
    {
        get { return _database; }
    }

    public void Commit()
    {
        _petaTransaction.Complete();
    }
}

public class PetaPocoUnitOfWorkProvider : IUnitOfWorkProvider
{
    public IUnitOfWork GetUnitOfWork(string connectionString)
    {
        if (UnitOfWork.Current != null)
        {
        throw new InvalidOperationException("Existing unit of work.");
        }

        var petaPocoUnitOfWork = new PetaPocoUnitOfWork(connectionString);
        UnitOfWork.Current = petaPocoUnitOfWork.Database;
        return petaPocoUnitOfWork;
    }
}

public static class UnitOfWork
{
    [ThreadStatic] public static IDatabase Current;
}
cs0815
  • 16,751
  • 45
  • 136
  • 299
  • Could you elaborate what the "IDatabase" interface supposed to be? Also, were you happy with this approach? – Mathieu Oct 16 '17 at 14:12

1 Answers1

6

You may or may not like, but here's how I removed the passing of unit of work and the unit of work from the interface.

var unitOfWorkProvider = new PetaPocoUnitOfWorkProvider();
var repository = new FakeRepository();
var fake = new Fake
{     
    // etc.
};

using (var uow = unitOfWorkProvider.GetUnitOfWork("BlaConnectionString"))
{
    repository.Insert(fake);
    uow.Commit();
}

Code

public interface IUnitOfWorkProvider
{
    IUnitOfWork GetUnitOfWork(string connectionString);
}

public static class UnitOfWork
{
    [ThreadStatic]
    public static IUnitOfWork Current { get; set; }
}

public class PetaPocoUnitOfWorkProvider : IUnitOfWorkProvider
{
    public IUnitOfWork GetUnitOfWork(string connectionString)
    {
        if (UnitOfWork.Current != null) 
        {
           throw new InvalidOperationException("Existing unit of work.");
        }
        UnitOfWork.Current = new PetaPocoUnitOfWork(connectionString);
        return UnitOfWork.Current;
    }
}

public interface IRepository<T>
{
    void Insert(T entity);
    void Update(T entity);
    void Delete(T entity);
    T Fetch(long uid);
}

public class PetaPocoUnitOfWork : IUnitOfWork
{
    private readonly Transaction _petaTransaction;
    private readonly Database _database;

    public PetaPocoUnitOfWork(string connectionString)
    {
        _database = new Database(connectionString);
        _petaTransaction = new Transaction(_database);
    }

    public void Dispose()
    {
        UnitOfWork.Current = null;
        _petaTransaction.Dispose();
    }

    public Database Database
    {
        get { return _database; }
    }

    public void Commit()
    {
        _petaTransaction.Complete();
    }
}

public abstract class BaseRepository<T> : IRepository<T>
{
    protected IDatabase Db
    {
        get
        {
            return UnitOfWork.Current;
        }
    }
}

public class FakeRepository : BaseRepository<T>
{
    public void Insert(Fake entity)
    {
        Db.Save(entity);
    }

    public void Update(Fake entity)
    {
        Db.Update(entity);
    }

    public void Delete(Fake entity)
    {
        Db.Delete(entity);
    }

    public FakeJobFact Fetch(long uid)
    {
        return Db.Fetch<Fake>("SELECT * FROM Fakes WHERE [FakeId] = @0", uid).FirstOrDefault();
    }
}
Plebsori
  • 1,075
  • 9
  • 23
  • I like this thanks. Yeah passing the uow all the time does not feel right. Just curious - does the UnitOfWork.Current ensure that the repo only uses the uow which is part of the using scope? Thanks! – cs0815 Feb 02 '16 at 09:02
  • 1
    Updated the code example to throw if another unit of work exists. It will stop nested unit of works. However, as the static is thread static, this design will mean multiple threads will each have their own scope. – Plebsori Feb 02 '16 at 11:51
  • what is the purpose of ThreadStatic if Current is not static? – cs0815 Feb 03 '16 at 17:45
  • Generally your UnitOfWork is not building – cs0815 Feb 03 '16 at 17:50
  • Also BaseRepository does not implement the interface – cs0815 Feb 03 '16 at 17:53
  • 1
    Updated code for you. I'm writing this by hand to give you an idea – Plebsori Feb 03 '16 at 22:14
  • Thanks - impressive that you write this by hand. I could not even live without resharper (-: – cs0815 Feb 04 '16 at 08:25
  • I am sorry but your BaseRepository should it by abstract and implement the interface generically (e.g. use T)? Also not sure what protected IDatabase Db => UnitOfWork.Current means? Sorry ... – cs0815 Feb 04 '16 at 08:32
  • 1
    Ah yes sorry, it should be abstract. (Updated). The other is c#6 syntax, which updated to be version 5. – Plebsori Feb 04 '16 at 12:09
  • I keep trying to get this to work but am currently stuck with PetaPocoUnitOfWorkProvider - it throws errors. It would be really great, if you could try to build this at your end and post the correct code? – cs0815 Feb 06 '16 at 13:55
  • any chance for some feedback pleeease? Intend to get your code working today. – cs0815 Feb 08 '16 at 13:46
  • I have tried to adapt you answer to get it to work. My attempt can be found in the PS section of my original question. If you think this is correct and once you adapt your answer I will accept it. – cs0815 Feb 08 '16 at 15:13
  • I can't see anything which starts the transaction? So all database operations complete even if an exception occurs halfway through? There is no call to _database.BeginTransaction() anywhere? – NickG Apr 15 '20 at 13:57
  • The `PetaPocoUnitOfWork` handles the DB transaction. `Database` & `Transaction` are PetaPoco types. @NickG feel free to open a GH issue if you need additional information – Plebsori Apr 17 '20 at 09:57