.Net Core, Dapper and PostgreSQL


  1. first of all we have to create interface

Dapper Npgsql

public interface IRepository<T> where T : class
{
	Task AddAsync(T item);
	Task RemoveAsync(T item);
	Task UpdateAsync(T item);
	Task<T> FindAsync(long id);
	Task<IEnumerable<T>> FindAsync(Expression<Func<T, object>> filter, object value);
	Task<IEnumerable<T>> FindAllAsync();
}
  1. create few entities
public class Operation 
{
		
	public long Id { get;  set; }
	
	public DateTime CreatedAt { get;  set; }

	public JObject Attachment { get; set; }

	public int OperationTypeId { get;  set; }

	public OperationType OperationType { get;  set; }
}

public class OperationType
{
	
	public int Id { get; set; }	

	public string Description { get; set; }
	
}
	
  1. implement base repository

DapperExtensions

internal class BaseRepository<T>: IRepository<T>  where T : class
{
	protected string _connectionString;        
						
	public BaseRepository(string connectionString)
	{
		_connectionString = connectionString;            
	}

	public async Task AddAsync(T item)
	{
		using (IDbConnection cn = await _getConnectionAsync())
		{                                
			await cn.InsertAsync<T>(item);

			cn.Close();
		}
	}

	public async Task<IEnumerable<T>> FindAsync(Expression<Func<T, object>> filter, object value)
	{
		using (var db = await _getConnectionAsync())
		{
			var predicate = Predicates.Field(filter, Operator.Eq, value);
			var result = await db.GetListAsync<T>(predicate);
			db.Close();
			return result;
		}
	}

	public async Task<IEnumerable<T>> FindAllAsync()
	{
		using (var db = await _getConnectionAsync())
		{                
			var result = await db.GetListAsync<T>();
			db.Close();
			return result;
		}
	}

	public async Task<T> FindAsync(long id)
	{
		using (var db = await _getConnectionAsync())
		{
			var result = await db.GetAsync<T>(id);
			db.Close();
			return result;
		}
	}

	public async Task RemoveAsync(T item)
	{
		using (var db = await _getConnectionAsync())
		{
			var result = await db.DeleteAsync<T>(item);
			db.Close();                
		}
	}

	public async Task UpdateAsync(T item)
	{
		using (var db = await _getConnectionAsync())
		{
			var result = await db.UpdateAsync<T>(item);
			db.Close();
		}
	}
	

	/// <summary>
	/// creates connection to database
	/// </summary>
	/// <returns>opened connection</returns>
	private async Task<IDbConnection> _getConnectionAsync()
	{
		var conn = new NpgsqlConnection(_connectionString);
		await conn.OpenAsync();
		return conn;
	}
}


  1. create dbcontext which contains all repositories
public class DbContext
{
	
	public DbContext(string connectionString)
	{
		//postgres support
		DapperExtensions.DapperAsyncExtensions.SqlDialect = new PostgreSqlDialect();
		//jobject to db jsonb handler
		SqlMapper.AddTypeHandler(typeof(JObject), JObjectHandler.Instance);

		OperationTypes = new BaseRepository<OperationType>(connectionString);
		Operations = new BaseRepository<Operation>(connectionString);
		TokenStatuses = new BaseRepository<TokenStatus>(connectionString);
		Tokens = new BaseRepository<Token>(connectionString);
	}

	public IRepository<OperationType> OperationTypes { get; private set; }
	public IRepository<Operation> Operations { get; private set; }        
}
  1. add mappers
public class OperationMapper: ClassMapper<Operation>
{
	public OperationMapper()
	{
		Table("operations");

		Map(x => x.Id).Key(KeyType.Identity);

		Map(x => x.CreatedAt).Column("registration_ts");
		Map(x => x.OperationTypeId).Column("operation_type");

		Map(x => x.OperationType).Ignore();

		//map all other columns
		AutoMap();
	}
}

 public class OperationTypeMapper: ClassMapper<OperationType>
{
	public OperationTypeMapper()
	{
		Table("operation_types");

		Map(x => x.Id).Key(KeyType.Identity);

		//map all other columns
		AutoMap();
	}
}
  1. add jobject handler
    class JObjectHandler : TypeHandler<JObject>
    {
     private JObjectHandler() { }
     public static JObjectHandler Instance { get; } = new JObjectHandler();
     public override JObject Parse(object value)
     {
         var json = (string)value;
         return json == null ? null : JObject.Parse(json);
     }
     public override void SetValue(IDbDataParameter parameter, JObject value)
     {
         parameter.Value = value?.ToString(Newtonsoft.Json.Formatting.None);
         ((NpgsqlParameter)parameter).NpgsqlDbType = NpgsqlDbType.Jsonb;
     }
    }
    
  2. Let’s test. Database should set Id for our entity after AddAsync method run
[TestMethod]
public async Task Add()
{
	var item = new Entities.Operation() { CreatedAt = DateTime.Now, Attachment = JObject.Parse("{ test: 'test'}"), OperationTypeId = 1 };
	await _context.Operations.AddAsync(item);

	Assert.AreNotEqual(0, item.id);	
}
Buy Me A Coffee

Related Posts

Avoid reflections of mappers and let Mapster generate mappers for you

Mapster generating tool for onion application

Predict Bitcoin price with ML.net

Live time series coin price predictor with machine learning

Throw exceptions from backend to frontend with blazor

One of advantages of using same code language on both frontend and backend

How to avoid violating of SOLID principles while extending service behaviours

Step by step extending service behaviour with decorator pattern respecting SOLID

Blazor render optimization

Best practices

.Net 6 brings application state to blazor webassembly server prerender

It kills strange blinking on screen

Must have libraries for blazor

List of best nuget packages

Blazor virtualize component

Lazy loading of big lists. Rendering optimization.

Blazor grpc - comunication optimization

Smaller and faster requests to your backend from blazor wasm

Free database for your blazor app

Don't pay for the cloud