Skip to content

Smoother IoC using Dapper Dapper.FastCRUD with Repository and UnitOfWork patterns

License

Notifications You must be signed in to change notification settings

generik0/Smooth.IoC.Dapper.Repository.UnitOfWork

Repository files navigation

Project Icon Smooth.IoC.Dapper.Repository.UnitOfWork

generik0 VSTS Build Status NuGet

This package it created to "FIX" the contradictory concepts behind the Repository and UnitOfWork patterns together with using inversition of control / dependancy injection.
Also i wanted to make the creation of sessions (IDbConnection) and UnitOFWork's (a Transaction) by the injected factory automatically connection / begin transaction on creation, and disconnect/commit on disposal. Hence making database work become nice and smooth like....
I also want to be able to do unit (integration) testing with e.g. a Sqlite but the production database engine could be e.g. a MsSQl. This is possible now...

So far there are examples of Autofact, Castle.Windsor, Ninject, Simpleinjector, StructureMap, and Unity.

WARNING: Until Smooth.IoC.Dapper.Repository.UnitOfWork reaches version 1.0, I reserve the right to make minor backwards-incompatible changes to the API.

Table of Contents generated with DocToc

What are the features of the library?

The library gives you the building blocks to:

  • Create Sessions from the IDbFactory in your methods, IDbFactory should be injected into your class's. As Session extends IDbConnection and will Open on the factory spawning the session and dispose the connection on disposal of the connection.
  • Your Sessions can create UnitOfWork's. As Session extends IDbConnection and will Open on the factory spawning the session and dispose the connection on disposal of the connection.
  • If your logic just needs "just a" UnitOfWork with a session that has the same scope, the factory can create it for you.
  • The IRepository and abstract concrete class should be used on your individual repository classes to provide simple and basic calls. But ofcasue you can add all the queries you want into your Repositories and use the dapper and dapper.FastCRUD (or any other extensions) functionality provided to you.
  • The repository abstract classes use Dapper.FastCRUD to give you a fluent ORM experience with the most common calls.
  • This library does not lock you to using dapper and FastCRUD, you can use any library you like that extends IDbConnection and IDbTransation, and still use the IDbFactory, ISession and IUnitOrWork.
  • Implemented for .net 4.5.2, .net 4.6.1+, .net 1.6 standard+.

You will have to register the IDbFactory and IUnitOfWork manually, along with your other registrations. But below in this readme and in the test examples i have added examples for: Autofac, Castle.Windsor, StructureMap, Ninject, Unity. I did not want to add al sorts of IoC containers to the package, so please look at the examples. And If you have better knowledge of an IoC framework, please let me know if it can be done better, and smoother...

About Dapper and Dapper.FastCRUD

I use Dapper and Dapper.FastCRUD for my sql work. It is made by the very cool StackExchange mob.
Dapper is a micro ORM data does only what you ask of it through queries. Dapper
There is an extension to Dapper called Dapper.FastCRUD. This adds fluentness to dapper also for complex joins etc. Dapper.FastCRUD.

Dapper is not Entity, Linq2Sql nor NHibernate. It is a micro ORM and really really fast. Dapper.FastCRUD does not add the FULL fluent ORM experience. So you will need to think about what you do.
But you are also in control of the code and optimization of calls. For me, i believe that the "big" ORMs do to much, and Dapper together with Dapper.FastCRUD is the perfect amount of just doing enough...

The drawback with Dapper.FastCRUD is it may fail if you don't give it the wrong SqlDialect. So i have extended FastDappers IDbConnection extensions so the projects own ISession and IUnitOfWork are extended. Then the FastCRUD IDbConnection extension method extended by ISession or IUnitOfWork extensions in the package. This insures that the dialogue is set correct, if needed. This means that your Entity can only be used for one database type per ioc container / executing assembily. So you can also use a different databae for your tests than your production code.

  • This will only effect FastCRUD calls using ISession or IUnitOrWork instances. Not IDbConnection instances.
  • If you want your entity to span across more than one database, you can use the RepositoryBase to extend from bypassing the Repository abstraction.
  • I have created a SqlDialectInstance (Singleton) expert that can help you set the dialogue. Please use it if you have issues with your session and sql dialect.
  • I have created a SqlHelper, that insures FastCRUD's SqlDialect is set if you decide you need a FastCRUD "Sql" helper method. Please use my SqlHelper otherwise your SqlDialect might be frozen. It is available for Repositories as "Sql" a proctected property.
  • TA dialogue helper is also available in your repository through "SetDialogueOnce" method. You will need to use it for joins.. (Hint: if your dialogue gets stuck in the wrong state you can "reset" the FastCRUD mapping using OrmConfiguration.RegisterEntity();)

You can do a lot fluently with FastCRUD. Check out there wiki:

Or as i have already menioned use dapper or any other extension utilizing IDbConnection and IDbTransaction...

What does this libray do?

All of the repository and UoW pattern examples i could find online did not include the usage of a factory for registration and injection. The session would typically be added to the constructor meaning when the session was disposed by one method, another method in the class could not use it any more. The examples with IoC used some very complex registration and multithreading code. But there really isn't a need for this!
Basically something didn't seam to fix with the typical UoW and Repository patterns together with IoC.
I also found that injecting a simple factory that could create simple IDbConnections and IDbTransactions was not good enough. Because more intelegence/help was needed. Hence the IDbFactory, ISession, IUnitOfWork, IRepository interfaces and logic was born...
At the same time it is very important that it be possible to use one connection for production code and another for unit testing (e.g. MsSql for production and Sqlite for testing).

  • Please note, that SqlLite only accepts IsolationLevel.Serializable for the uow. You will need to override the default IsolationLevel.RepeatableRead. This design allows for this. As your custom session interface is used as the generic for the repository, not the session class allowing for different connection strings. You can even use the same database migrations if you have done code first. I have used SimpleMigrations as it allows both console running for the production code / installer and inproc for unit testing.

You are welcome to look at the unit tests for examples or look below in this readme.

What this the package include?

So what i have done/created is this:

  1. IDbFactory is a simple interface that you register with your IoC. It can create/spwan ISession's and IUntOfWork's. But primary used in code to spawn Sessions or UnitofWork's with an attached session.
  2. ISession<TDatabase> (and Session<TDatabase> abstraction): Extends IDbConnection. You use it to extend your Database connection / Session type. Yours session classes and interfaces require a connection string. So If you have multiple database connections, you need 1 ISession and Session extended Interface and class per database. When the session is created by the factory it connects to the database, when it disposes it discontects and disposes. For Castle Windsor it also untracks the object. You can use the session for any IDbConnection or dapper (or extension) framework you like, as ISession extends IDbConnection ;-). The ISession also has a Dapper.FastCRUD extension so the SqlDialect is automatically set for the enitity depending on the connection.
  3. IUnitOfWork (and UnitOfWork): Extends IDbTransaction. You don't need to extend anything with this. When you have created a session in you code, you can create a uow from the session. Then the session is created by the factory it begins a transaction (isolation i a parameter), when it disposes it commits (roleback on exception) and disposes. For Castle Windsor it also untracks the object. ;-). The IUnitOfWork also has a Dapper.FastCRUD extension so the SqlDialect is automatically set for the enitity depending on the connection.
  4. IRepository<TSession, TEntity, TPk> (Repository<TSession, TEntity, TPk> abstraction): Is a default repository that you extend with your own repository for each of the entities you want a repository for. There as some builtin methods e.g. GetAll, Get, and SaveOrUpdate. You can add the methods you need for your entity using any IDbConnection framework. have used dapper-dot-net and dapper.FastCRUD for the quering.
  5. IEntity<TPk>: An interface for your Entities so you always have a Id key defined. You can make your entities as you please. this is only to help you.
  6. IRepositoryBase (and RepositoryBase<TEntity> abstraction): This is a vanilla base repository, you can use it if you do not want to use Dapper or Dapper.FastCRUD. It includes an protected method to set the dialect which you will need to do, if you want to use FastCRUD.

Code Examples: Sessions, Repositories and UnitOfWork

Below is examples of using the package with Sessions, UnitOfWork and repositories.

Session and ISession

Below is an example of a session / dbconnection class. Creating a your custom session and interface type, Extend the session base with your DbConnection type. Remember to use default convensions for your interface, you need to pass the connection string into the Session base class. NB the generic is a ADO IDbConnection, For Dapper.FastCRUD is supporst MsSql, MySql, SQLite, PostgreSql with the SqlDialect

You can inject a setting/config interface for this injected into your session class and then pass the connection setting:

public class TestSession : Session<SQLiteConnection>, ITestSession {
    public TestSession(IDbFactory session, IMyDatabaseSettings settings)
            : base(session, settings.ConnectionString)
    {
    }
}

You can manually add your connection string to the base class, but i don't recommend this:

public class TestSession : Session<SQLiteConnection>, ITestSession {
    public TestSession(IDbFactory session)
        : base(session, "Data Source=:memory:;Version=3;New=True;")
    {
    }
}

Repository and IRepository

Below is an example of a repository class that extends Repository. Creating a Repository interface, Add the IRepository to your Repository interface and give it the Entity and Pk generics. The IRepository interface and abstract class has a large number of the most primary calls to the database. E.g.

  • Get,
  • GetAll and
  • SaveOrUpdate. Both in sync and Async

All the special calls you need in your repository can you can just add There are no restrictions ;-)

public interface IBraveRepository : IRepository<Brave, int>
{
}

public class BraveRepository : Repository<Brave, int>, IBraveRepository
{
    public BraveRepository(IDbFactory factory) : base(factory)
    {
        public BraveRepository(IDbFactory factory) : base(factory)
        {
        }

        //An example of a custom "join" call, if the abstract repository calls are not enough...
        public Brave GetWithInnerJoin(int key, ISession connection)
        {
            var entity = CreateInstanceHelper.Resolve<Brave>();
            entity.Id = key;
            return connection.Get(entity, statement =>
            {
                statement.Include<New>(join => join.InnerJoin())
                .Include<World>(join => join.InnerJoin());
            });
        }
    }
}

Using Session and UnitOFWork in a class/method

Below is an examples of a the factory spawning a session and the session (using its injected factory) to spawn a UoW.
Here we create a session to get data, and create a uow to save data.
The connection and begin transaction will happen on create and close and commit will happen @disposal

public class MyClass : IMyClass
{
	private readonly IDbFactory _factory;
    private readonly IBraveRepository _repository;

    public MyClass(IDbFactory factory, IBraveRepository braveRepository)
    {
		_factory = factory;
        _repository = braveRepository;
    }

	public void DoSomething()
	{
        // Spawn a session and spawn 1-to-many UnitOFWork with the connection
		using (var session = _factory.Create<ITestSession>())
        {
			var myItem = _repository.GetKey(1, session);
            using (var uow = session.UnitOfWork())
			{
				_repository.SaveOrUpdate(myItem, uow);
			}
            var myItem = _repository.GetKey(myItem.Id, session);
        }

        // Spawn a session with a UnitOFWork. Their lifescope is the same..
        using (var uow = _factory.Create<IUnitOFWork, ITestSession>())
        {
			var myItem = _repository.GetKey(1, uow);
            _repository.SaveOrUpdate(myItem, uow);
        }
	}
}

Below is the simple version where we just want to get some data and the repository will created and close the connection it self.
This is not generally recommended

public class MyClass : IMyClass
{
	private readonly IDbFactory _factory;
    private readonly IBraveRepository _repository;

    public MyClass(IDbFactory factory, IBraveRepository braveRepository)
    {
		_factory = factory;
        _repository = braveRepository;
    }

    // The base repository will Spawn a session of the generic type and dispose it after the call.
	public void DoSomething()
	{
        var myItem = _repository.GetKey<ITestSession>(1, session);
	}
}

That simple. That smooth.#

Code Examples: IoC registration

You need to register your own repository and session classes yourself. But using default conventions this should happen automatically in you bootsrapper, right? Please look in the test project under "IoC_Example_Installers" for any changes or updates.

Autofac registration

Autofac does have a factory using delegates but this does not fit the same pattern as all the other IoC. So one has to wrap the factory in a concrete implementation. Luckely the concrete implementation can be internal (or even private if you like). Registration examples:

public class AutofacRegistrar
{
    public void Register(ContainerBuilder builder)
    {
        builder.Register(c=> new AutofacDbFactory(c.Resolve<IComponentContext>())).As<IDbFactory>().SingleInstance();
        builder.RegisterType<Dapper.Repository.UnitOfWork.Data.UnitOfWork>().As<IUnitOfWork>();
    }

    sealed class AutofacDbFactory : IDbFactory
    {
        private readonly IComponentContext _container;
        public AutofacDbFactory(IComponentContext container)
        {
            _container = container;
        }
        public T Create<T>() where T : class, ISession
        {
            return _container.Resolve<T>();
        }
        public TUnitOfWork Create<TUnitOfWork, TSession>(IsolationLevel isolationLevel = IsolationLevel.Serializable) where TUnitOfWork : class, IUnitOfWork where TSession : class, ISession
        {
            return _container.Resolve<TUnitOfWork>(new NamedParameter("factory", _container.Resolve <IDbFactory>()),
                new NamedParameter("session", Create<TSession>()), new NamedParameter("isolationLevel", isolationLevel)
                , new NamedParameter("sessionOnlyForThisUnitOfWork", true));
        }
        public T Create<T>(IDbFactory factory, ISession session, IsolationLevel isolationLevel = IsolationLevel.Serializable) where T : class, IUnitOfWork
        {
            return _container.Resolve<T>(new NamedParameter("factory", factory),
                new NamedParameter("session", session), new NamedParameter("isolationLevel", isolationLevel));
        }
        public void Release(IDisposable instance)
        {
            instance.Dispose();
        }
    }
}

Castle Windsor Installer

You need to register the factory and UnitofWork for castle to work. Castle has its own factory implemenation that creates and releases the instances for you. However for the UnitOfWork that also creates a session, i.e. _dbFactory.Create<IUnitOFWork, ISession>() a custom componentSelector was needed. If you do not wish to use the "_dbFactory.Create<IUnitOFWork, ISession>()" call, you can leave ther "DbFactoryComponentSelector" and registration out...

public class CastleWindsorInstaller : IWindsorInstaller
{
    public void Install(IWindsorContainer container, IConfigurationStore store)
    {
        if (FacilityHelper.DoesKernelNotAlreadyContainFacility<TypedFactoryFacility>(container))
        {
            container.Kernel.AddFacility<TypedFactoryFacility>();
        }
        container.Register(Component.For<IUnitOfWork>()
            .ImplementedBy<Dapper.Repository.UnitOfWork.Data.UnitOfWork>().IsFallback().LifestyleTransient());
        container.Register(Component.For<DbFactoryComponentSelector, ITypedFactoryComponentSelector>());
        container.Register(Component.For<IDbFactory>().AsFactory(c => c.SelectedWith<DbFactoryComponentSelector>()));
    }

    sealed class DbFactoryComponentSelector : DefaultTypedFactoryComponentSelector
    {
        private readonly IKernelInternal _kernel;

        public DbFactoryComponentSelector(IKernelInternal kernel)
        {
            _kernel = kernel;
        }
        protected override IDictionary GetArguments(MethodInfo method, object[] arguments)
        {
            var generics = method.GetGenericArguments();
            if (generics.Length != 2 || generics.First() != typeof(IUnitOfWork) || !method.Name.Equals("create", StringComparison.InvariantCultureIgnoreCase))
            {
                return base.GetArguments(method, arguments);
            }
            var uow = new Arguments
            {
                {"session", _kernel.Resolve(generics.Last())},
                {"isolationLevel", arguments[0]},
                {"sessionOnlyForThisUnitOfWork", true}
            };
            return uow;
        }
    }
}

Ninject registration

Ninject like Castle has a good factory. Unfortunately the factory does not have a Release. So i decided to in the example to combine the use of the ninject's factory and a concrete factory. You need to install-package Ninject.Extensions.Factory for this to work

When you register the binding, for this example to work, the Bind must be after your other registrations so that "Rebind" on the DbFactory works. It is only so the DbFactory is singleton. It doesn't need to be, but i am hoping it is faster for the IoC to resolve.

public class NinjectBinder
{
    public void Bind(IKernel kernel)
    {
        kernel.Bind<INinjectDbFactory>().ToFactory(() => new TypeMatchingArgumentInheritanceInstanceProvider());
        kernel.Rebind<IDbFactory>().To<DbFactory>().InSingletonScope();
        kernel.Bind<IUnitOfWork>().To<Dapper.Repository.UnitOfWork.Data.UnitOfWork>()
            .WithConstructorArgument(typeof(IDbFactory))
            .WithConstructorArgument(typeof(ISession))
            .WithConstructorArgument(typeof(IsolationLevel));
    }
    
    [NoIoCFluentRegistration]
    sealed class DbFactory : IDbFactory
    {
        private readonly IResolutionRoot _resolutionRoot;
        private readonly INinjectDbFactory _factory;
        public DbFactory(IResolutionRoot resolutionRoot)
        {
            _resolutionRoot = resolutionRoot;
            _factory = resolutionRoot.Get<INinjectDbFactory>();
        }
        public T Create<T>() where T : class, ISession
        {
            return _factory.Create<T>();
        }
        public TUnitOfWork Create<TUnitOfWork, TSession>(IsolationLevel isolationLevel = IsolationLevel.Serializable) where TUnitOfWork : class, IUnitOfWork where TSession : class, ISession
        {
            return _factory.CreateUnitOwWork<TUnitOfWork>(this, Create<TSession>(), isolationLevel, true);
        }

        public T Create<T>(IDbFactory factory, ISession session, IsolationLevel isolationLevel = IsolationLevel.Serializable) where T : class, IUnitOfWork
        {
            return _factory.CreateUnitOwWork<T>(factory, session, isolationLevel);
        }
        public void Release(IDisposable instance)
        {
            _resolutionRoot.Release(instance);
        }
    }

    public interface INinjectDbFactory
    {
        T Create<T>() where T : ISession;
        T CreateUnitOwWork<T>(IDbFactory factory, ISession connection, IsolationLevel isolationLevel = IsolationLevel.Serializable, bool sessionOnlyForThisUnitOfWork = false) where T : IUnitOfWork;
    }
}

Simple Injector registration

I am not very happy about the Simpleinjector example. But it works. However the concrete factory implemenation include the word "new". Simple Injector does not like passing runtime arguments for the constructor and as the UnitOrWork requier the session instance it is a problem. Otherwise we get a new instance. I decided to make a concrete factory, and for the UoW it "new's" the UnitOfWork. I looked at the delegate factory pattern in simple injector, this may be a better solution for the future But the online help on the subject was incorrect and i could not replicate.

public class SimpleInjectorRegistrar
{
    public void Register(Container container)
    {
        container.RegisterSingleton<IDbFactory>(new SimpleInjectorDbFactory(container));
    }
    public static void RegisterDisposableTransient(Container container , Type service, Type implementation )
    {
        var reg = Lifestyle.Transient.CreateRegistration(implementation, container);
        reg.SuppressDiagnosticWarning(DiagnosticType.DisposableTransientComponent, "suppressed.");
        container.AddRegistration(service, reg);
    }

    sealed class SimpleInjectorDbFactory : IDbFactory
    {
        private readonly Container _container;
        public SimpleInjectorDbFactory(Container container)
        {
            _container = container;
        }
        public T Create<T>() where T : class, ISession
        {
            return _container.GetInstance<T>();
        }
        public TUnitOfWork Create<TUnitOfWork, TSession>(IsolationLevel isolationLevel = IsolationLevel.Serializable) where TUnitOfWork : class, IUnitOfWork where TSession : class, ISession
        {
            return new Dapper.Repository.UnitOfWork.Data.UnitOfWork(_container.GetInstance<IDbFactory>(), Create<TSession>(),
                isolationLevel, true) as TUnitOfWork;
        }
        public T Create<T>(IDbFactory factory, ISession session, IsolationLevel isolationLevel = IsolationLevel.Serializable) where T : class, IUnitOfWork
        {
            return new Dapper.Repository.UnitOfWork.Data.UnitOfWork(factory, session, isolationLevel) as T;
        }
        public void Release(IDisposable instance)
        {
            instance?.Dispose();
        }
    }
}

Structure Map registration

You need to create a concrete factory and register it, passing the container as an argument to the factory

public class StructureMapRegistration
{
    public void Register(IContainer container)
    {
        container.Configure(c=>c.For<IDbFactory>()
            .UseIfNone<StructureMapDbFactory>().Ctor<IContainer>()
            .Is(container).Singleton());
    }

    sealed class StructureMapDbFactory : IDbFactory
    {
        private IContainer _container;
        public StructureMapDbFactory(IContainer container)
        {
            _container = container;
        }
        public T Create<T>() where T : class, ISession
        {
            return _container.GetInstance<T>();
        }
        public TUnitOfWork Create<TUnitOfWork, TSession>(IsolationLevel isolationLevel = IsolationLevel.Serializable) where TUnitOfWork : class, IUnitOfWork where TSession : class, ISession
        {
            return _container.With(_container.GetInstance<IDbFactory>()).With(Create<TSession>() as ISession)
                .With(isolationLevel).With(true).GetInstance<TUnitOfWork>();
        }
        public T Create<T>(IDbFactory factory, ISession session , IsolationLevel isolationLevel = IsolationLevel.Serializable) where T : class, IUnitOfWork
        {
            return _container.With(factory).With(session).With(isolationLevel).GetInstance<T>();
        }
        public void Release(IDisposable instance)
        {
            _container.Release(instance);
        }
    }
}

Unity registration

Unity does not appear to have a very good factory. So one has to wrap the factory in a concrete implementation. Luckely the concrete implementation can be internal (or even private if you like). Unfortunately Unity could not figure out when i tried to override only 2 paramateres, that it should use a diffent constructor. So the UnitOfWork Constructor with 3 parameters is always called.

public class UnityRegistrar
{
    public void Register(IUnityContainer container)
    {
        container.RegisterType<IDbFactory, UnityDbFactory>(new ContainerControlledLifetimeManager(),
            new InjectionConstructor(container));
        container.RegisterType<IUnitOfWork, Dapper.Repository.UnitOfWork.Data.UnitOfWork>();
    }

    sealed class UnityDbFactory : IDbFactory
    {
        private readonly IUnityContainer _container;
        public UnityDbFactory(IUnityContainer container)
        {
            _container = container;
        }
        public T Create<T>() where T : class, ISession
        {
            return _container.Resolve<T>();
        }
        public TUnitOfWork Create<TUnitOfWork, TSession>(IsolationLevel isolationLevel = IsolationLevel.Serializable) where TUnitOfWork : class, IUnitOfWork where TSession : class, ISession
        {
            return _container.Resolve<TUnitOfWork>(new ParameterOverride("factory", _container.Resolve<IDbFactory>()),
                new ParameterOverride("session", Create<TSession>()), new ParameterOverride("isolationLevel", isolationLevel),
                new ParameterOverride("sessionOnlyForThisUnitOfWork", true));
        }
        public T Create<T>(IDbFactory factory, ISession session,  IsolationLevel isolationLevel = IsolationLevel.Serializable) where T : class, IUnitOfWork
        {
            return _container.Resolve<T>(new ParameterOverride("factory", factory),
                new ParameterOverride("session", session), new ParameterOverride("isolationLevel", isolationLevel),
                new ParameterOverride("sessionOnlyForThisUnitOfWork", false));
        }
        public void Release(IDisposable instance)
        {
            _container.Teardown(instance);
        }
    }
}

Version History

  • 0.0.x
    • Created Session, UnitOfWork, IDBFactory, Repository Getters and SaveOrUpdate
    • Castle Windsor integration
  • 0.1.x
    • Added examples and test for Autofac, Ninject, StructureMap, SimpleInjector and Unity.
    • Bug fixes.
    • Extended IUnitOFWork and Session for FastCRUD
  • 0.2.x
    • Bug fix with transactions and UoW extensions (0.2.69)
    • Add UnitOfWork Creation from Factory (0.2.73)
    • Change so DbConnection does not have to be passed to the repo. Instead an adhoc session made by repo has the ISession as generic (0.2.73)
    • Seperate Async and Sync calls (0.2.73)
    • Remove need for IEntity to get and set key value (0.2.73)
  • 0.3.x (Done)
    • Add more tests for repository calls (0.3.4)
    • Change so it isn't allows async methods being called in repo (also for sync) (0.3.4)
    • Add more FastCRUD standard calls (Delete and Count) with tests in the repository (0.3.4)
    • Change all async tests to use AssetDoesNotThrowAsync (0.3.4)
    • Fixed issue where factory.Create<IUnitOFWork, ISession>() did not set the sql dialect.(0.3.0)
    • Minimise the use of reflections in session and uow extensions (0.3.4)
    • Add IComparable constraint to TPk (0.3.5)
    • Bug fixes and improved the collections not to be static (0.3.8)
    • For Asp.Net constructor injection of session insured that a uow in session or command reopened the connection if it is closed. (0.3.9)
    • Made UnitofWork default to IsolationLevel.RepeatableRead instead of IsolationLevel.Serializable (0.3.15)
    • Made all repository methods be virtual so the can be overriden. (0.3.15)
    • Add dictionaries to minimise reflections. (0.3.18)
  • 0.4.x (Done)
    • Make plan IEntity queries use pure dapper but maybe use FastCRUD SQL builder? (0.4.0)
      • Split nuspec up so Dapper and FastDapper are not resolved with Session, UnitOfWork, etc. (0.4.0)
    • Removed IDbTransaction from Uow as it only gave problems (0.4.0)
  • 0.5.x (Started)
    • Update to Vs 2017 (0.5.1)
    • Add net40 support for UnitOfWork Package (done)
    • Make the Smooth repo use the uow nuget (done)
    • Add where and parameter paramateres into uow and session extensions. And expand Repository. (In Progress)
      • Add FastCRUD bulk methods with tests to repo.
    • Add more Xml Summaries for all used interfaces.
  • 0.6.x (Future)
    • Look into making Session inherit DbConnection

About

Smoother IoC using Dapper Dapper.FastCRUD with Repository and UnitOfWork patterns

Resources

License

Stars

Watchers

Forks

Packages

No packages published