Tuesday, 5 July 2022

UnitOfWork pattern in Entity Framework

This article will present code regarding the 'Unit of Work' pattern in Entity Framework, EF. I will also show updated code for a generic repository pattern of the same code. Note that the code in this article is very general and can be run against your DbContext regardless of how it looks. It is solved general, using generics and some usage of reflection. Note that this code goes against DbContext and is written in a solution that uses Entity Framework 6.4.4. However, the code here should work also in newer EF versions, that is - EF Core versions - such as EF Core 6, as the code is very general and EF Core also got the similar structure at least concerning the code shown here.. First off, the code for the interface IUnitOfWork looks like this:
 
 IUnitOfWork.cs

using SomeAcme.Common.Interfaces;

namespace SomeAcme.Data.EntityFramework.Managers
{
    public interface IUnitOfWork
    {
        UnitOfWork AddRepository<T>() where T : class;
         UnitOfWork AddCustomRepository<T>() where T : class;
        int Complete();
        void Dispose();
        UnitOfWork RemoveRepository<T>() where T : class;
        IRepository<T> Repository<T>() where T : class;
    }
}
 
The method Complete is important, as it will commit the transaction and perform changes to the database which the UnitOfWork implementation will work against. The code for UnitWork implementation looks like this:
 
 UnitOfWork.cs

using SomeAcme.Common.Interfaces;
using System;
using System.Collections.Generic;
using System.Linq;

namespace SomeAcme.Data.EntityFramework.Managers
{

    public class UnitOfWork : IUnitOfWork, IDisposable
    {

        private readonly System.Data.Entity.DbContext _dbContext;

        public UnitOfWork(System.Data.Entity.DbContext dbContext)
        {
            _dbContext = dbContext;
            _repositories = new Dictionary<Type, object>();
        }

        public UnitOfWork AddRepository<T>() where T : class
        {
            if (!_repositories.ContainsKey(typeof(T)))
            {
                var repoObj = Activator.CreateInstance(typeof(Repository<T>), _dbContext);
                Repository<T> repo = repoObj as Repository<T>;
                if (repo == null)
                {
                    throw new ArgumentNullException($"Could not instantiate repository of type {typeof(T).Name}");
                }
                _repositories[typeof(T)] = repo;
            }
            return this;
        }

        public UnitOfWork AddCustomRepository<T>() where T : class
        {
            if (!_repositories.ContainsKey(typeof(T)))
            {
                bool checkImpementationPassesGenericInterfaceCheck = typeof(T).GetInterfaces().Any(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IRepository<>));
                if (!checkImpementationPassesGenericInterfaceCheck)
                {
                    throw new ArgumentException($"The type {typeof(T).Name} must implement IRepository<T> to be added as a custom repository");
                }
                var repoObj = Activator.CreateInstance(typeof(T), _dbContext);

                if (repoObj == null)
                {
                    throw new ArgumentNullException($"Could not instantiate repository of type {typeof(T).Name}");
                }
                _repositories[typeof(T)] = repoObj;
            }
            return this;
        }

        public UnitOfWork RemoveRepository<T>() where T : class
        {
            if (_repositories.ContainsKey(typeof(T)))
            {
                _repositories.Remove(typeof(T));
            }
            return this;
        }

        public IRepository<T> Repository<T>() where T : class
        {
            //find suitable repo - possibly a custom repo ..
            IRepository<T> repoFound = null; 

            foreach (var item in _repositories)
            {
                if (item.Key == typeof(T))
                {
                    repoFound = _repositories[typeof(T)] as IRepository<T>;
                    break; 
                }
                bool checkImpementationPassesGenericInterfaceCheck = item.Key.GetInterfaces().Any(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IRepository<>));
                if (checkImpementationPassesGenericInterfaceCheck)
                {
                    //the repo implements IRepository<T> - this is the one to use
                    repoFound = item.Value as IRepository<T>;
                    break;
                }
            }
            if (repoFound == null)
            {
                throw new ArgumentNullException($"Could not retrieve repositiory defined inside the UnitOfWork for entity of type: {typeof(T).Name}. Is it registered into the UnitOfWork. Use method 'AddRepository'");
            }
            return repoFound; 
        }

        private readonly IDictionary<Type, object> _repositories;

        public int Complete()
        {
            if (_dbContext == null)
            {
                throw new ArgumentNullException($"The db context object of the UnitOfWork class is null, cannot complete the UnitOfWork as the db context is not initialized! No changes was performed in DB !"); 
            }
            int numStateEntriesWritten = _dbContext.SaveChanges();
            return numStateEntriesWritten;
        }

        public void Dispose()
        {
            _dbContext?.Dispose(); //dispose the passed in _shared_ db context instead of 
            //disposing the db context inside each repository to dispose only once.. 
            GC.SuppressFinalize(this);
        }

    }
}

 
Note - this implementation focuses on being able to define repositories to the UnitOfWork before running the unit work in case you want to be able to specify which tables / entities got repositories in the db context. Many other implementations will specify the repositories to expose in the UnitOfWork via property getters for example. This will make it easier to implement such a UnitOfWork implementation without this ability to expanding which repositories the UnitOfWork is supporting. Also note that in case you want to add a custom repository that implements IRepository<T> you need to cast into that custom repository when you call the 'Repository' method. A closed abstraction instead, where you do not add repositories like in this code may be more desirable. But perhaps there are scenarios where you want to add repositories that can take part in the UnitOfWork dynamically as shown here. The downside is that you need to initialize the UnitOfWork in addition to performing the database 'steps' before calling the 'Complete' method. What is the most practical UnitOfWork implementation in many cases most probably will be a closed abstraction where you specify which repositories the UnitOfWork supports and avoid having to add repositories like shown here. I did this as an academic exercise to see if such an implementation was possible. The unit tests passes and it looks okay to implement. Note though that you should probably have some default initialization here, i.e. in the constructor of UnitOfWork specify some default repositories and consider if you want to allow adding or removing repositories in the UnitOfWork class. Also, removing repositories could be considered an anti-pattern, so you could disallow this - only allowing adding custom repositories implementating the IRepository<T> in addition to listing up some implemented repositories. Important - always just pass in ONE db context. Each repository must use the same db context instance so the change tracking works as expected. Also note - the UnitOfWork and repositories implement IDisposable. When UnitOfWork goes out of scope, the db context is disposed. In case you want to add the UnitOfWork as a service in a DI container, remember to set up a scoped instance so it will be disposed. In case you use Singleton - this will cause the db connection to hang around.. the Dispose method is exposed as a public method anyways and can be called to dispose on-demand.. Finally, here are some tests that passes testing out the UnitOfWork together with the generic repository pattern !

 
        [Test] 
        public void UnitOfWorkPerformsExpected()
        {
            var dbContext = GetContext();
            var unitOfWork = new UnitOfWork(dbContext);
     
            unitOfWork.AddRepository<OperationExternalEquipment>()
                .AddRepository<OperationDiagnoseCode>();

            var operationExternalEquipment = new OperationExternalEquipment
            {
                OperationId = 10296,
                EquipmentText = "Stent graft type ABC-123",
                OrderedDate = DateTime.Today
            };

            var operationDiagnoseCode = new OperationDiagnoseCode
            {
                OperationId = 10296,
                DiagnoseCodeId = "A09.9",
                IsCodePreFabricated = true                
            };
            
            //act 
            unitOfWork.Repository<OperationExternalEquipment>().Add(operationExternalEquipment);
            unitOfWork.Repository<OperationDiagnoseCode>().Add(operationDiagnoseCode);
            int savedResult = unitOfWork.Complete();

            savedResult.Should().Be(2); 

            //assert 
            var savedOperationEquipmentsForOperation = unitOfWork.Repository<OperationExternalEquipment>().Find(x => x.OperationId == 10296).ToList(); 
            savedOperationEquipmentsForOperation.Any(x => x.EquipmentText == "Stent graft type ABC-123").Should().BeTrue();

            var savedOperationDiagnoseCodesForOperation = unitOfWork.Repository<OperationDiagnoseCode>().Find(x => x.OperationId == 10296).ToList();
            savedOperationDiagnoseCodesForOperation.Any(x => x?.DiagnoseCodeId == "A09.9").Should().BeTrue();

            //cleanup
            unitOfWork.Repository<OperationExternalEquipment>().Remove(operationExternalEquipment);
            unitOfWork.Repository<OperationDiagnoseCode>().Remove(operationDiagnoseCode);
            savedResult = unitOfWork.Complete();

            savedResult.Should().Be(2);

        }

        [Test]
        public void UnitOfWorkCustomRepoPerformsExpected()
        {
            var dbContext = GetContext();
            var unitOfWork = new UnitOfWork(dbContext);
            unitOfWork.AddCustomRepository<OperationExternalEquipmentCustomRepo>();
            var operationExternalEquipment = new OperationExternalEquipment
            {
                OperationId = 10296,
                EquipmentText = "Stent graft type DEF-456",
                OrderedDate = DateTime.Today
            };         

            //act 
            unitOfWork.Repository<OperationExternalEquipment>().Add(operationExternalEquipment);
       
            int savedResult = unitOfWork.Complete();

            savedResult.Should().Be(1);

            //assert 
            var savedOperationEquipmentsForOperation = unitOfWork.Repository<OperationExternalEquipment>().Find(x => x.OperationId == 10296).ToList(); 
            savedOperationEquipmentsForOperation.Any(x => x.EquipmentText == "Stent graft type DEF-456").Should().BeTrue();

            //check if we can use a custom repo method !
            var equipmentRepo = unitOfWork.Repository<OperationExternalEquipment>() as OperationExternalEquipmentCustomRepo;
            var equipmentTexts = string.Join(",", equipmentRepo.GetEquipmentTexts(10296));
            bool foundText = equipmentTexts.Contains("Stent graft type DEF-456"); 
            foundText.Should().BeTrue();

            //cleanup
            unitOfWork.Repository<OperationExternalEquipment>().Remove(operationExternalEquipment);
            savedResult = unitOfWork.Complete();

            savedResult.Should().Be(1);

        }
 
 
The adjusted implementation of Repository now looks like this - I have renamed many of the methods to be more standard compared to other implementations of the repository pattern demonstrated online in different videos on Youtube for example. The updated interface for repository now looks like this:
 
  IRepository.cs
  
  using System;
using System.Collections.Generic;
using System.Linq.Expressions;

namespace SomeAcme.Common.Interfaces
{

    /// <summary>
    /// Generic implementation of repository pattern for (should maybe have been implemented a decade ago to save some development time .. :-) to save some code in DAL-layer Data.EntityFramework ) 
    /// </summary>
    /// <typeparam name="T">Entity T (POCO for table)</typeparam>
    public interface IRepository<T> where T : class
    {

        /// <summary>
        /// Performs an insert of an entity
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="entity"></param>
        /// <param name="keyValues">If set, these key values are used to locate entity in db after the insertion has been performed if specifed by other param for saveImmediate</param>
        /// <param name="saveImmediate">Save immediately in db after adding the entity</param>
        T Add(T entity, bool saveImmediate = false);

        /// <summary>
        /// Performs an insert of multiple entities
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="entity"></param>
        /// <param name="saveImmediate">Save immediately after adding item in db</param>
        IEnumerable<T> AddRange(IEnumerable<T> entity, bool saveImmediate = false);

        /// <summary>
        /// Saves changes. Commits the data to the database.
        /// </summary>
        /// <param name="dbContext">Db context</param>
        void SaveChanges(object dbContext);

        /// <summary>
        /// Delete an entity specified by <paramref name="keyValues"/> to look up entity
        /// </summary>
        /// <param name="keyValues"></param>
        /// <returns></returns>
        T Remove(bool saveImmediate, params object[] keyValues);

        /// <summary>
        /// Deletes an entity specified by <paramref name="entity"/> from the database
        /// </summary>
        /// <param name="entity"></param>
        void Remove(T entity, bool saveImmediate = false);

        /// <summary>
        /// Removes entities specified by <paramref name="entities"/> from the database
        /// </summary>
        /// <param name="entities"></param>
        void RemoveRange(IEnumerable<T> entities, bool saveImmediate = false);

        /// <summary>
        /// Update <paramref name="entity"/>
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="entity"></param>
        /// <param name="saveImmediate">Save immediate if set to true</param>
        /// <returns></returns>
        T Update(T entity, bool saveImmediate, params object[] keyValues);

        /// <summary>
        /// Equivalent to a 'GetById' method, but tailored for generic use. 
        /// Retrieves <paramref name="idSelector"/> specified by <paramref name="idValue"/>
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="keyValues">Key values to use to find entity</param>
        T Get(bool asNoTracking = true, params object[] keyValues);

        /// <summary>
        /// Retrieves entities of type <typeparamref name="T"/> via predicate <paramref name="condition"/>.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="condition"></param>
        /// <returns></returns>
        /// <param name="asNoTracking">If true, does not track items (less chance of db locks due to turning off change tracking) </param>
        IEnumerable<T> Find(Expression<Func<T, bool>> condition, bool asNoTracking = true);

        /// <summary>
        /// Retrieves an entity of type <typeparamref name="T"/> via predicate <paramref name="condition"/>.
        /// If not found, null is returned.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="condition"></param>
        /// <returns></returns>
        T GetByCondition(Expression<Func<T, bool>> condition);

        /// <summary>
        /// Retrieve all the entities specified by <typeparamref name="T"/>.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        /// <param name="asNoTracking">If true, does not track items (less chance of db locks due to turning off change tracking) </param>
        IEnumerable<T> GetAll(bool asNoTracking = true); 

    }
}

 

And the implementation looks like this:
 
 
 using SomeAcme.Common.Interfaces;
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Linq.Expressions;

namespace SomeAcme.Data.EntityFramework.Managers
{
    public class Repository<T> : IRepository<T>, IDisposable where T : class
    {
        private readonly System.Data.Entity.DbContext _dbContext;

        public Repository(System.Data.Entity.DbContext dbContext)
        {
            _dbContext = dbContext;
        }

        public T Add(T entity, bool saveImmediate = false)
        {
            return ExecuteQuery((T obj, System.Data.Entity.DbContext dbContext) =>
            {
                var entityDb = dbContext.Set<T>().Add(entity);                
                if (saveImmediate)
                    SaveChanges(dbContext);
                return entityDb;

            }, entity);
        }

        public T Add(T entity, bool saveImmediate = false, params object[] keys)
        {
            return ExecuteQuery((T obj, System.Data.Entity.DbContext dbContext) =>
            {
                dbContext.Entry(obj).State = EntityState.Added;
                if (saveImmediate)
                    SaveChanges(dbContext);
                var entityInDb = dbContext.Set<T>().Find(keys);
                return entityInDb;
            }, entity);
        }

        public IEnumerable<T> AddRange(IEnumerable<T> entities, bool saveImmediate)
        {
            var entitites = _dbContext.Set<T>().AddRange(entities);
            if (saveImmediate)
                SaveChanges(_dbContext);
            return entitites;
        }

        public T Remove(bool saveImmediate = false, params object[] keyValues)
        {
            var entity = _dbContext.Set<T>().Find(keyValues);
            if (entity == null)
                return null;
            var entry = _dbContext.Entry(entity);
            if (entry == null)
                return null;
            entry.State = EntityState.Deleted;
            if (saveImmediate)
                SaveChanges(_dbContext);
            return entity;
        }

        public void Remove(T entity, bool saveImmediate = false)
        {
            _dbContext.Set<T>().Remove(entity);
            if (saveImmediate)
                SaveChanges(_dbContext);
        }

        public void RemoveRange(IEnumerable<T> entities, bool saveImmediate = false)
        {
            _dbContext.Set<T>().RemoveRange(entities);
            if (saveImmediate)
                SaveChanges(_dbContext);
        }

        /// <summary>
        /// Note - requiring here that we have defined primary key(s) on the target tables ! 
        /// </summary>
        /// <param name="keyValues"></param>
        /// <returns></returns>
        public T Get(params object[] keyValues)
        {
            var entity = _dbContext.Set<T>().Find(keyValues);
            _dbContext.Entry(entity).State = EntityState.Detached;
            return entity;
        }

        public IEnumerable<T> GetAll(bool asNoTracking = true)
        {
            return asNoTracking ? _dbContext.Set<T>().AsNoTracking() : _dbContext.Set<T>();
        }

        public IEnumerable<T> Find(Expression<Func<T, bool>> condition, bool asNoTracking = true)
        {
            IQueryable<T> query = asNoTracking ? _dbContext.Set<T>().AsNoTracking() : _dbContext.Set<T>();
            var entities = query.Where(condition);
            return entities;
        }

        public T GetByCondition(Expression<Func<T, bool>> condition)
        {
            IQueryable<T> query = _dbContext.Set<T>().AsNoTracking();
            var entities = query.Where(condition);
            return entities.FirstOrDefault();
        }

        public bool ExistsByCondition(Expression<Func<T, bool>> condition)
        {
            IQueryable<T> query = _dbContext.Set<T>().AsNoTracking();
            return query.Any(condition);

        }

        public T Get(bool asNoTracking, params object[] keyValues)
        {
            var entity = asNoTracking ? _dbContext.Set<T>().AsNoTracking().FirstOrDefault() : _dbContext.Set<T>().Find(keyValues);
            return entity;
        }

        public void SaveChanges(object context)
        {
            var dbContext = context as System.Data.Entity.DbContext;
            if (dbContext == null)
            {
                throw new ArgumentException($"dbContext object inside save method : Must be of type System.Data.Entity.DbContext", nameof(context));
            }
            dbContext.SaveChanges(); 
        }

        public T Update(T entity, bool saveImmediate = false, params object[] keyValues)
        {
            return ExecuteQuery((T obj, System.Data.Entity.DbContext dbContext) =>
            {
                var entityInDb = dbContext.Set<T>().Find(keyValues);
                if (entityInDb == null)
                    return null; 
                dbContext.Entry(entityInDb).CurrentValues.SetValues(obj);                
                if (saveImmediate)
                {
                    SaveChanges(dbContext);
                }
                return obj;
            }, entity);            
        }

        private T ExecuteQuery(Func<T, System.Data.Entity.DbContext, T> query, T entity)
        {
            T result = query(entity, _dbContext);
            return result;
        }

        public void Dispose()
        {
            Dispose(true); 
        }

        private void Dispose(bool isDisposing)
        {
            if (isDisposing)
            {
                _dbContext?.Dispose();
                GC.SuppressFinalize(this);
            }
        }

    }
}

 
 
Totally, the code of the unit of work and repository pattern is about 300 lines of code combined. It should match a lot of Data Access Layer (DAL) implementations of Entity Framework, possible we could reduce a lot of code here in many projects by following these patterns which are accepted data access patterns defined by the 'Gang of four' way back many decades ago. If I would adjust this code next I would do these modifications :
  • UnitOfWork should only allow adding custom repos in addition to some predefines repos. This should give an overall simplification of UnitOfWork
  • Specific transaction handling should be added, like setting the transaction isolation scope. Also speifically doing rollback in case anything crashes in UnitOfWork
  • Possible add some more shared utility methods inside Repository class if such should be added

Saturday, 25 June 2022

Generic Singleton that allows initing a type T once

I have looked into a simple design pattern today and decided to have a go with a generic implementation of this. The class is sealed with a private constructor. It will for a generic pattern of type T instantiate a 'Singleton', i.e. same objects will be returned via the static Instance property for a given type T. It is also possible to 'Init' to a custom object in case you want to set the Singleton object to a custom inited object of type T. If you skip initing call - this will just use the parameterless
constructor. But you can only do a call to Init once (i.e. call the Init method) , so do this initing only in the application startup code for example. Of course, we could instead create a more distilled version, disallowing 'Initing' singleton objects not allowing sending in an adjusted instance of type T as the inited singleton - and also Singleton is considered a pattern which should be implemented per class and not in a generic manner like this. Also, it violates the Single responsibility principle
as the object of type T has not control of how in instantiates itself. But Singleton pattern is an accepted creational pattern.
 

 public sealed class Singleton<T> where T : class, new()
    {
        private static Lazy<T> InstanceProxy
        {
            get
            {
                if (_instanceObj?.IsValueCreated != true)
                {
                    _instanceObj = new Lazy<T>(() => new T());
                }
                return _instanceObj;
            }
        }

        private static Lazy<T>? _instanceObj;


        public static T Instance { get { return InstanceProxy.Value; } } 

        public static void Init(Lazy<T> instance)
        {
            if (_instanceObj?.IsValueCreated == true)
            {
                throw new ArgumentException($"A Singleton for the type <T> is already set"); 
            }
            _instanceObj = instance ?? throw new ArgumentNullException(nameof(instance)); 
        }

        private Singleton()
        {   
        }
    }
 
You can use it like this : Some model class :
 

public class Aeroplane
{
    public string? Model { get; set; }
    public string? Manufacturer { get; set; }
    public int YearBuilt { get; set; }
    public int PassengerCount { get; set; }
}
 
Usage sample :


var aeroplane = new Aeroplane
{
    Manufacturer = "Boeing",
    Model = "747",
    PassengerCount = 350,
    YearBuilt = 2005
};

var aeroPlane3 = Singleton<Aeroplane>.Instance;
var aeroPlane4 = Singleton<Aeroplane>.Instance;

Console.WriteLine($"Aeroplane3 and aeroplane4 is same object? {Object.ReferenceEquals(aeroPlane3, aeroPlane4)}");
 
Outputs 'true'. Trying to re-init type T Singleton to another object fails :
 
var aeroplane2 = new Aeroplane
{
    Manufacturer = "Sopwith Aviation Company",
    Model = "Sophwith Camel",
    PassengerCount = 1,
    YearBuilt = 1917
};

Singleton<Aeroplane>.Init(new Lazy<Aeroplane>(aeroplane2));
 
You can of course just access the Singleton with initing it - as mentioned it will call the default public constructor of type T and set an this 'default' instance of T as the singleton for type T. Possible you could have a way of setting a custom constructor here instead of passing an object as a sort of improved 'factory pattern'. We could for example in the Init method specify which constructor method
to call in the initing and pass in parameters for example.
 
var aeroplaneDefaultInstantiated = Singleton<Aeroplane>.Instance; 
 
Note : Default instantiation - calls the parameterless public constructor of type T. So you must do the initialization inside the public parameterless constructor if you skip the Init method. We can also allow sending in a custom constructor to the Singleton class by offering another init method. Here, we can send in parameters of given types and therefore identify the constructor to call. This of course demands that such a constructor exists. It offers another way of setting a singleton object. We now can either set the singleton object for type T via :
  • An instance that calls the default parameterless constructor and sets this object as the singleton for type T
  • A customized instance in case you want to have more fine tuned initalized object to be set as the singleton for type T
  • An instance that calls a specified constructor of the type T and sets the created instance as the singleton for type T
Sample I tested this out with (dump method calls below are done inside Linqpad 7 - so paste this code into there) :
 
void Main()
{
	Singleton<Aeroplane>.Init(new object[] { "Nieuport IV", 1911 });
	//Singleton<Aeroplane>.Init(new object[] { "Nieuport V", 1914 });

	var aeroplaneTwo = Singleton<Aeroplane>.Instance;
	var aeroplaneThree = Singleton<Aeroplane>.Instance;
	Object.ReferenceEquals(aeroplaneTwo, aeroplaneThree).Dump();
	aeroplaneTwo.Dump();
	aeroplaneThree.Dump();
}

public class Aeroplane
{
	public string? Model { get; set; }
	public string? Manufacturer { get; set; }
	public int YearBuilt { get; set; }
	public int PassengerCount { get; set; }
	
	public Aeroplane()
	{
			
	}
	
	public Aeroplane(string model, int yearBuilt)
	{
		Model = model; 
		YearBuilt = yearBuilt; 
		if (YearBuilt < 1913) {
			PassengerCount = 1; 
		}
	}
}


public sealed class Singleton<T> where T : class, new()
{
	private static Lazy<T> InstanceProxy
	{
		get
		{
			if (_instanceObj?.IsValueCreated != true)
			{
				_instanceObj = new Lazy<T>(() => new T());
			}
			return _instanceObj;
		}
	}

	private static Lazy<T>? _instanceObj;

	public static void Init(object[] constructorParams)
	{
		if (_instanceObj?.IsValueCreated == true)
		{
			throw new ArgumentException($"A Singleton for the type <{typeof(T).Name}> is already set");
		}
		var constructor = typeof(T).GetConstructor(constructorParams.Select(p => p.GetType()).ToArray());
		if (constructor == null)
		{
			string typenamesParams = string.Join(",", constructorParams.Select(p => p.GetType()));
			throw new ArgumentException($"Could not find a constructor of type {typeof(T).Name} with the parameter types {typenamesParams}"); 
		}
		var instanceObj = constructor.Invoke(constructorParams); 
		_instanceObj = new Lazy<T>(instanceObj as T); 

	}


	public static T Instance { get { return InstanceProxy.Value; } }

	public static void Init(Lazy<T> instance)
	{
		if (_instanceObj?.IsValueCreated == true)
		{
			throw new ArgumentException($"A Singleton for the type <T> is already set");
		}
		_instanceObj = instance ?? throw new ArgumentNullException(nameof(instance));
	}

	private Singleton()
	{
	}
}

 
Once more, we disallow calling the Init method many times, here we call a specific constructor to init as the Singleton object.

Monday, 6 June 2022

Repository pattern in Entity Framework Core

I have been looking into repository pattern for EF Core today. This is what I got :
 
 
using SomeAcme.Interfaces;
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Linq.Expressions;

namespace SomeAcme.DAL.Pattern
{
    public class Repository<T> : IRepository<T> where T : class
    {

        private System.Data.Entity.DbContext GetContext()
        {
            return new SomeAcmeDbContext as System.Data.Entity.DbContext;
        }

        private T ExecuteQuery(Func<T, System.Data.Entity.DbContext, T> query, T entity)
        {
            using (var context = GetContext())
            {
                T result = query(entity, context);
                return result;
            }
        }

        public T Add(T entity, bool saveImmediate = false)
        {
            return ExecuteQuery((T obj, System.Data.Entity.DbContext dbContext) =>
            {
                var entityDb = dbContext.Set<T>().Add(entity);                
                if (saveImmediate)
                    SaveChanges(dbContext);
                return entityDb;

            }, entity);
        }

        public T Add(T entity, bool saveImmediate = false, params object[] keys)
        {
            return ExecuteQuery((T obj, System.Data.Entity.DbContext dbContext) =>
            {
                dbContext.Entry(obj).State = EntityState.Added;
                if (saveImmediate)
                    SaveChanges(dbContext);
                var entityInDb = dbContext.Set<T>().Find(keys);
                return entityInDb;
            }, entity);
        }

        public IEnumerable<T> AddRange(IEnumerable<T> entities, bool saveImmediate)
        {
            using (var dbContext = GetContext())
            {
                var entitites = dbContext.Set<T>().AddRange(entities);
                if (saveImmediate)
                    SaveChanges(dbContext);
                return entitites;
            }
        }

        public T Delete(bool saveImmediate = false, params object[] keyValues)
        {
            using (var dbContext = GetContext())
            {
                var entity = dbContext.Set<T>().Find(keyValues);
                if (entity == null)
                    return null;
                var entry = dbContext.Entry(entity);
                if (entry == null)
                    return null; 
                entry.State = EntityState.Deleted;
                if (saveImmediate)
                    SaveChanges(dbContext); 
                return entity; 
            }        
        }

        /// <summary>
        /// Note - requiring here that we have defined primary key(s) on the target tables ! 
        /// </summary>
        /// <param name="keyValues"></param>
        /// <returns></returns>
        public T Get(params object[] keyValues)
        {
            using (var dbContext = GetContext())
            {
                var entity = dbContext.Set<T>().Find(keyValues);
                dbContext.Entry(entity).State = EntityState.Detached; 
                return entity; 
            }      
        }

        public IList<T> GetAll(bool asNoTracking = true)
        {
            using (var dbContext = GetContext())
            {
                return asNoTracking ? dbContext.Set<T>().AsNoTracking().ToList() : dbContext.Set<T>().ToList();
            }           
        }

        public IList<T> GetAllByCondition(Expression<Func<T, bool>> condition, bool asNoTracking = true)
        {
            using (var dbContext = GetContext())
            {
                IQueryable<T> query = asNoTracking ? dbContext.Set<T>().AsNoTracking() : dbContext.Set<T>();
                var entities = query.Where(condition);
                return entities.ToList();
            }          
        }

        public T GetFirstByCondition(Expression<Func<T, bool>> condition)
        {
            return GetAllByCondition(condition).FirstOrDefault(); 
        }

        public T GetByKeyValues(bool asNoTracking, params object[] keyValues)
        {
            using (var dbContext = GetContext())
            {
                var entity = asNoTracking ? dbContext.Set<T>().AsNoTracking().FirstOrDefault() : dbContext.Set<T>().Find(keyValues);
                return entity;
            }
        }

        public void SaveChanges(object context)
        {
            var dbContext = context as System.Data.Entity.DbContext;
            if (dbContext == null)
            {
                throw new ArgumentException("Must be of type System.Data.Entity.DbContext", nameof(context));
            }
            dbContext.SaveChanges(); 
        }

        public T Update(T entity, bool saveImmediate = false, params object[] keyValues)
        {
            return ExecuteQuery((T obj, System.Data.Entity.DbContext dbContext) =>
            {
                var entityInDb = dbContext.Set<T>().Find(keyValues);
                if (entityInDb == null)
                    return null; 
                dbContext.Entry(entityInDb).CurrentValues.SetValues(obj);                
                  if (saveImmediate)
                {
                    SaveChanges(dbContext);
                }
                return obj;
            }, entity);            
        }

    }
}

 
 
 
And here are some unit tests against a database of mine (containing some integration tests)
 
using AutoMapper;
using FluentAssertions;
using SomeAcme.Common;
using SomeAcme.Common.DataContract;
using SomeAcme.Data.EntityFramework.Managers;
using SomeAcme.Data.EntityFramework.Models;
using NUnit.Framework;
using System.Collections.Generic;
using System.Linq;

namespace SomeAcme.Service.Implementation.Test.Pattern
{

    [TestFixture]
    public class RepositoryTest
    {
        private IMapper _mapper; 

        [SetUp]
        public void TestInitialize()
        {
            IntegrationTestBootstrapper.Run();
            System.Threading.Thread.CurrentPrincipal = new ConcreteClaimsPrincipal(SomeAcme.Administrator, "107455", "Testutvikler, Ivrig");

            var configuration = new MapperConfiguration(cfg =>
            {
                cfg.CreateMap>PasSystem, PasSystemDataContract>();
            });

#if DEBUG
            // only during development, validate your mappings; remove it before release
            // configuration.AssertConfigurationIsValid();
#endif
            _mapper = configuration.CreateMapper(); 

        }

        [Test]
        public void GetAllReturnsExpected()
        {
            var pasSystemRepository = new Repository>PasSystem>();
            var allPasSystem = pasSystemRepository.GetAll()?.Select(x => _mapper.Map>PasSystemDataContract>(x)).ToList();
            allPasSystem?.Count().Should().BePositive();
        }

        [Test]
        public void GetReturnsExpected()
        {
            var jobTitleRepo = new Repository>Title>();
            var jobTitle = jobTitleRepo.Get(6);
            Assert.IsNotNull(jobTitle);
            jobTitle.Text.Should().Be("Anestesisykepleier"); 
        }

        [Test] 
        public void AddRangeDoesNotThrow()
        {
            var postponementCauseRepo = new Repository>PostponementCause>();
            var postponementCauses = new List>PostponementCause>()
            {
                new PostponementCause{ FreshOrganizationalUnitId = 107455, IsActive = true, Text = "Personel not available"},
                new PostponementCause{ FreshOrganizationalUnitId = 107455, IsActive = true, Text = "Personel already busy"},
            };
            postponementCauseRepo.AddRange(postponementCauses, true);
        }

        [Test]
        public void GetByConditionReturnsNonEmpty()
        {
            var timeMatrixRepository = new Repository>TimeMatrix>();
            var timematrices = timeMatrixRepository.GetAllByCondition(t => t.PreOperation > 80).ToList();
            timematrices.Count().Should().BePositive(); 
        }

        [Test]
        public void AddTimeMatrixToDbViaRepositorySucceeds()
        {
            var timeMatrixRepository = new Repository>TimeMatrix>();

            var timeMatrix = new TimeMatrix
            {
                Code = "T100",
                IsActive = false,
                PostOperation = 11,
                PreOperation = 11,
            };
            var timeMatrixSavedToDb = timeMatrixRepository.Add(timeMatrix, true);

            timeMatrixSavedToDb.TimeMatrixId.Should().BePositive(); 
        }

        [Test]
        public void AddAndUpdateTimeMatrixToDbViaRepositorySucceeds()
        {
            var timeMatrixRepository = new Repository>TimeMatrix>();

            var timeMatrix = new TimeMatrix
            {
                Code = "T100",
                IsActive = false,
                PostOperation = 11,
                PreOperation = 11,
            };
            var timeMatrixSavedToDb = timeMatrixRepository.Add(timeMatrix, true);
            timeMatrixSavedToDb.TimeMatrixId.Should().BePositive();
            timeMatrix.Code = "T200";
            timeMatrix.PreOperation = 11; 
            timeMatrixSavedToDb = timeMatrixRepository.Update(timeMatrix, true, timeMatrixSavedToDb.TimeMatrixId);
            timeMatrixSavedToDb.Code.Should().Be("T200"); 
            timeMatrix.PreOperation.Should().Be(11);    
        }

        [Test]
        public void AddAndUpdateAndDeleteTimeMatrixToDbViaRepositorySucceeds()
        {
            var timeMatrixRepository = new Repository>TimeMatrix>();

            var timeMatrix = new TimeMatrix
            {
                Code = "T100",
                IsActive = false,
                PostOperation = 11,
                PreOperation = 11,
            };
            var timeMatrixSavedToDb = timeMatrixRepository.Add(timeMatrix, true);
            timeMatrixSavedToDb.TimeMatrixId.Should().BePositive();
            timeMatrix.Code = "T300";
            timeMatrix.PreOperation = 11;
            timeMatrixSavedToDb = timeMatrixRepository.Update(timeMatrix, true, timeMatrixSavedToDb.TimeMatrixId);
            timeMatrixSavedToDb.Code.Should().Be("T300");
            timeMatrixSavedToDb.PreOperation.Should().Be(11);
            timeMatrixSavedToDb = timeMatrixRepository.Delete(true, timeMatrixSavedToDb.TimeMatrixId);
            timeMatrixSavedToDb.Should().NotBeNull(); 
        }

        [Test]
        public void AddPostOfficeToDbViaRepositorySucceeds()
        {
            var postOfficeRepository = new Repository>PostOffice>();

            var postOffice = new PostOffice
            {
                PostalPlace = "Steinkjer",
                PostalCode = "7724"
            };
            var postOfficeSavedToDb = postOfficeRepository.Add(postOffice, true);
            postOfficeSavedToDb.Should().NotBeNull();
        }


    }
}

 
Also note the usage of Auto mapper here to automatically map between POCO entity objects to DTO (Data transfer objects, usually data contracts for example). Building a useful repository pattern in EF Core and combining it with Automapper (available on nuget) will probably reduce your Data Access Layer logic a bit .. In many cases maybe an understatement.. Note - this code have passed unit tests, but not been used in production (yet). Methods that demands keyValues to find entities do require your table to have primary keys on the table. This still is 'demo code' and WIP (Work in progress). Works !