ASP.NETコアとEF COREを使用したGeneric Repository Patternのチュートリアルに従いました here たとえば
public class Repository<T> : IRepository<T> where T : class
{
protected readonly DbContext _dbContext;
protected readonly DbSet<T> _dbSet;
public Repository(DbContext context)
{
_dbContext = context ?? throw new
ArgumentException(nameof(context));
_dbSet = _dbContext.Set<T>();
}
public void Add(T entity)
{
_dbSet.Add(entity);
}
}
これはEF Coreを使用しているため、Add
メソッドを介してデータを挿入するために事前定義されたメソッドを使用できますが、dapperになるとsqlクエリが必要です.Dapperに適したこの汎用インターフェイスを作成するにはどうすればよいですか?
@PathumLakshanによる例はコメントからリクエストしています。提供されている例は非同期で記述されていますが、ソースは同期的に実装できます。とにかく、これはDapperでインフラストラクチャを管理する方法の単なる例です。クラスDb
は、データを取得してSQLクエリを実行するための一般的なメソッドを提供します。たとえば、基本的なクエリにオーバーロードGet<T>(string, object)
を使用するか、Get<T>(Func<SqlConnection, SqlTransaction, int, Task<T>>
を使用してlet QueryMultiple
と言います。クラスRepository<Entity>
は、エンティティEntity
の基本リポジトリをどのように表示できるかを示しています。
Dbクラス:
public class Db : IDb
{
private readonly Func<SqlConnection> _dbConnectionFactory;
public Db(Func<SqlConnection> dbConnectionFactory)
{
_dbConnectionFactory = dbConnectionFactory ?? throw new ArgumentNullException(nameof(dbConnectionFactory));
}
public async Task<T> CommandAsync<T>(Func<SqlConnection, SqlTransaction, int, Task<T>> command)
{
using (var connection = _dbConnectionFactory.Invoke())
{
await connection.OpenAsync();
using (var transaction = connection.BeginTransaction())
{
try
{
var result = await command(connection, transaction, Constants.CommandTimeout);
transaction.Commit();
return result;
}
catch (Exception ex)
{
transaction.Rollback();
Logger.Instance.Error(ex);
throw;
}
}
}
}
public async Task<T> GetAsync<T>(Func<SqlConnection, SqlTransaction, int, Task<T>> command)
{
return await CommandAsync(command);
}
public async Task<IList<T>> SelectAsync<T>(Func<SqlConnection, SqlTransaction, int, Task<IList<T>>> command)
{
return await CommandAsync(command);
}
public async Task ExecuteAsync(string sql, object parameters)
{
await CommandAsync(async (conn, trn, timeout) =>
{
await conn.ExecuteAsync(sql, parameters, trn, timeout);
return 1;
});
public async Task<T> GetAsync<T>(string sql, object parameters)
{
return await CommandAsync(async (conn, trn, timeout) =>
{
T result = await conn.QuerySingleAsync<T>(sql, parameters, trn, timeout);
return result;
});
}
public async Task<IList<T>> SelectAsync<T>(string sql, object parameters)
{
return await CommandAsync<IList<T>>(async (conn, trn, timeout) =>
{
var result = (await conn.QueryAsync<T>(sql, parameters, trn, timeout)).ToList();
return result;
});
}
}
リポジトリクラス:
public class Repository<Entity> : IRepository<Entity>
{
protected readonly IDb _db;
public Repository(IDb db)
{
_db = db ?? throw new
ArgumentException(nameof(db));
}
public async Task Add(Entity entity)
{
await _db.ExecuteAsync("INSERT INTO ... VALUES...", entity);
}
public async Task Update(Entity entity)
{
await _db.ExecuteAsync("UPDATE ... SET ...", entity);
}
public async Task Remove(Entity entity)
{
await _db.ExecuteAsync("DELETE FROM ... WHERE ...", entity);
}
public async Task<Entity> FindByID(int id)
{
return await _db.GetAsync<Entity>("SELECT ... FROM ... WHERE Id = @id", new { id });
}
public async Task<IEnumerable<Entity>> FindAll()
{
return await _db.SelectAsync<Entity>("SELECT ... FROM ... ", new { });
}
}
Db
は、リポジトリで必要となるExecuteScalar
など、他の汎用メソッドで拡張できます。それが役に立てば幸い。
Dapperジェネリックリポジトリを持つプロジェクトがありましたが、プロジェクトの進化後、dapperの真のパワーを使用するジェネリックリポジトリditchedができました。
一般的なCRUD操作なしで直接Dapperを使用することをお勧めします。
私たちが持っていたことを実証するために、生産準備が整っていないサンプルコードを提供し、独自のジェネリックリポジトリを実装するアイデアを提供します。
public abstract class ConnectionBase : IDbConnection
{
protected ConnectionBase(IDbConnection connection)
{
Connection = connection;
}
protected IDbConnection Connection { get; private set; }
// Verbose but necessary implementation of IDbConnection:
#region "IDbConnection implementation"
public string ConnectionString
{
get
{
return Connection.ConnectionString;
}
set
{
Connection.ConnectionString = value;
}
}
public int ConnectionTimeout
{
get
{
return Connection.ConnectionTimeout;
}
}
public string Database
{
get
{
return Connection.Database;
}
}
public ConnectionState State
{
get
{
return Connection.State;
}
}
public IDbTransaction BeginTransaction()
{
return Connection.BeginTransaction();
}
public void Close()
{
Connection.Close();
}
public IDbCommand CreateCommand()
{
return Connection.CreateCommand();
}
public void Dispose()
{
Connection.Dispose();
}
public void Open()
{
Connection.Open();
}
#endregion
}
汎用リポジトリ
public abstract class GenericRepository<T> : IRepository<T> where T : class //EntityBase, IAggregateRoot
{
private readonly string _tableName;
internal IDbConnection Connection
{
get
{
return new SqlConnection(ConfigurationManager.ConnectionStrings["SmsQuizConnection"].ConnectionString);
}
}
public GenericRepository(string tableName)
{
_tableName = tableName;
}
internal virtual dynamic Mapping(T item)
{
return item;
}
public virtual void Add(T item)
{
using (IDbConnection cn = Connection)
{
var parameters = (object)Mapping(item);
cn.Open();
item.ID = cn.Insert<Guid>(_tableName, parameters);
}
}
public virtual void Update(T item)
{
using (IDbConnection cn = Connection)
{
var parameters = (object)Mapping(item);
cn.Open();
cn.Update(_tableName, parameters);
}
}
public virtual void Remove(T item)
{
using (IDbConnection cn = Connection)
{
cn.Open();
cn.Execute("DELETE FROM " + _tableName + " WHERE ID=@ID", new { ID = item.ID });
}
}
public virtual T FindByID(Guid id)
{
T item = default(T);
using (IDbConnection cn = Connection)
{
cn.Open();
item = cn.Query<T>("SELECT * FROM " + _tableName + " WHERE ID=@ID", new { ID = id }).SingleOrDefault();
}
return item;
}
public virtual IEnumerable<T> FindAll()
{
IEnumerable<T> items = null;
using (IDbConnection cn = Connection)
{
cn.Open();
items = cn.Query<T>("SELECT * FROM " + _tableName);
}
return items;
}
}