PetaPoco + unit of work + repository pattern
Asked Answered
H

1

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;
}
Huss answered 1/2, 2016 at 17:8 Comment(1)
Could you elaborate what the "IDatabase" interface supposed to be? Also, were you happy with this approach?Obe
L
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();
    }
}
Lulu answered 1/2, 2016 at 22:0 Comment(14)
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!Huss
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.Lulu
what is the purpose of ThreadStatic if Current is not static?Huss
Generally your UnitOfWork is not buildingHuss
Also BaseRepository does not implement the interfaceHuss
Updated code for you. I'm writing this by hand to give you an ideaLulu
Thanks - impressive that you write this by hand. I could not even live without resharper (-:Huss
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 ...Huss
Ah yes sorry, it should be abstract. (Updated). The other is c#6 syntax, which updated to be version 5.Lulu
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?Huss
any chance for some feedback pleeease? Intend to get your code working today.Huss
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.Huss
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?Anaanabaena
The PetaPocoUnitOfWork handles the DB transaction. Database & Transaction are PetaPoco types. @Anaanabaena feel free to open a GH issue if you need additional informationLulu

© 2022 - 2024 — McMap. All rights reserved.