Dapperを使用して汎用リポジトリを構築しようとしています。ただし、CRUD操作を実装するのにいくつかの問題があります。
リポジトリからのコードは次のとおりです。
public class GenericRepository<TEntity> : IGenericRepository<TEntity> where TEntity : class
{
internal IDbConnection Connection
{
get
{
return new SqlConnection(ConfigurationManager.ConnectionStrings["SoundyDB"].ConnectionString);
}
}
public GenericRepository(string tableName)
{
_tableName = tableName;
}
public void Delete(TEntity entity)
{
using (IDbConnection cn = Connection)
{
cn.Open();
cn.Execute("DELETE FROM " + _tableName + " WHERE Id=@ID", new { ID = entity.Id });
}
}
}
ご覧のとおり、私のdelete-methodは、型クラスのパラメーターであるパラメーターとしてTEntityを取ります。
UserRepositoryからDeleteメソッドを次のように呼び出します。
public class UserRepository : GenericRepository<User>, IUserRepository
{
private readonly IConnectionFactory _connectionFactory;
public UserRepository(IConnectionFactory connectionFactory) : base("User")
{
_connectionFactory = connectionFactory;
}
public async Task<User> Delete(User model)
{
var result = await Delete(model);
return result;
}
}
問題は、汎用リポジトリの削除操作にentity.Id
を書き込めないことです。エラーが発生します。では、どうすればこのようなCRUD操作を簡単に実装できますか?
エラーメッセージは次のとおりです。
TEntityに「Id」の定義が含まれておらず、「TEntity」タイプの引数を受け入れる拡張メソッド「Id」が見つかりませんでした
そのようにインターフェースを定義します。
public interface ITypeWithId {
int Id {get;}
}
そして、User
型がそのインターフェースを実装していることを確認してください。
次に、それをジェネリック制約としてクラスに適用します。
public class GenericRepository<TEntity> : IGenericRepository<TEntity> where TEntity : class, ITypeWithId
リポジトリに格納されているタイプがあり、Idプロパティがない場合は、削除タイプの制約をクラスではなくメソッドに固有にします。これにより、文字列や複合(マルチ)キーなど、他の何かをキー入力する可能性のあるタイプでも、同じリポジトリタイプを使用できます。
public void Delete<T>(T entity) where T : class, ITypeWithId
{
using (IDbConnection cn = Connection)
{
cn.Open();
cn.Execute("DELETE FROM " + _tableName + " WHERE Id=@ID", new { ID = entity.Id });
}
}
これをしないでください!汎用リポジトリは、価値よりも混乱を招きます。これは脆弱なコード(_tableNameの文字列リテラル、idパラメーターの無効なキャストエラー)であり、ギャップのあるセキュリティホール(_tableNameを介したSQLインジェクション)をもたらします。 Dapperを選択した場合、それはSQLを制御したいためであるため、Dapperに送信するSQLを生成することは意味がありません。
以下のようなインターフェースを定義する必要があります
public interface IIdentityEntity
{
public int Id { get; set;}
}
クラスを使用するすべてのエンティティは、IIdentityEntityを実装する必要があります。
最初の行は次のように変更する必要があります
public class GenericRepository<TEntity> : IGenericRepository<TEntity> where TEntity : class,IIdentityEntity
問題は、TEntityをクラスとしてのみ記述し、クラスの記述にIDが含まれていないため、Genericタイプがその中にIdフィールドを保持するインターフェイスを実装したことをコンパイラに通知する必要があることです。