リポジトリ パターンの実装経験
-
01-07-2019 - |
質問
新しい asp.net Web プロジェクトを開始する準備をしており、LINQ-to-SQL に移行する予定です。で見つけた情報を使用して、データ層のセットアップを少し作業しました。 マイク・ハドロウ これは、インターフェイスとジェネリックスを使用して、データベース内の各テーブルのリポジトリを作成します。最初はこれは面白いアプローチだと思いました。ただし、基本 Repository クラスを作成し、それを継承して、アクセスする必要があるテーブルの TableNameRepository クラスを作成する方が合理的ではないかと考えています。
どのアプローチを使用すれば、クリーンなテスト可能な方法でテーブルに固有の機能を追加できますか?参考までに私のリポジトリ実装を示します。
public class Repository<T> : IRepository<T> where T : class, new()
{
protected IDataConnection _dcnf;
public Repository()
{
_dcnf = new DataConnectionFactory() as IDataConnection;
}
// Constructor injection for dependency on DataContext
// to actually connect to a database
public Repository(IDataConnection dc)
{
_dcnf = dc;
}
/// <summary>
/// Return all instances of type T.
/// </summary>
/// <returns>IEnumerable<T></returns>
public virtual IEnumerable<T> GetAll()
{
return GetTable;
}
public virtual T GetById(int id)
{
var itemParam = Expression.Parameter(typeof(T), "item");
var whereExp = Expression.Lambda<Func<T, bool>>
(
Expression.Equal(
Expression.Property(itemParam, PrimaryKeyName),
Expression.Constant(id)
), new ParameterExpression[] { itemParam }
);
return _dcnf.Context.GetTable<T>().Where(whereExp).Single();
}
/// <summary>
/// Return all instances of type T that match the expression exp.
/// </summary>
/// <param name="exp"></param>
/// <returns>IEnumerable<T></returns>
public virtual IEnumerable<T> FindByExp(Func<T, bool> exp)
{
return GetTable.Where<T>(exp);
}
/// <summary>See IRepository.</summary>
/// <param name="exp"></param><returns></returns>
public virtual T Single(Func<T, bool> exp)
{
return GetTable.Single(exp);
}
/// <summary>See IRepository.</summary>
/// <param name="entity"></param>
public virtual void MarkForDeletion(T entity)
{
_dcnf.Context.GetTable<T>().DeleteOnSubmit(entity);
}
/// <summary>
/// Create a new instance of type T.
/// </summary>
/// <returns>T</returns>
public virtual T Create()
{
//T entity = Activator.CreateInstance<T>();
T entity = new T();
GetTable.InsertOnSubmit(entity);
return entity;
}
/// <summary>See IRepository.</summary>
public virtual void SaveAll()
{
_dcnf.SaveAll();
}
#region Properties
private string PrimaryKeyName
{
get { return TableMetadata.RowType.IdentityMembers[0].Name; }
}
private System.Data.Linq.Table<T> GetTable
{
get { return _dcnf.Context.GetTable<T>(); }
}
private System.Data.Linq.Mapping.MetaTable TableMetadata
{
get { return _dcnf.Context.Mapping.GetTable(typeof(T)); }
}
private System.Data.Linq.Mapping.MetaType ClassMetadata
{
get { return _dcnf.Context.Mapping.GetMetaType(typeof(T)); }
}
#endregion
}
解決
依存関係注入 (キャッスル?) を使用してリポジトリを作成する (つまり、別のキャッシュなどでラップできる) かのように、具象型を使用するかどうかは問題ではない、と提案したくなります。その場合、コードベースは次のようになります。どちらの方法でやったとしても、それは賢明なことではありません。
次に、DI にリポジトリを依頼するだけです。例えば。城の場合:
public class Home {
public static IRepository<T> For<T> {
get {
return Container.Resolve<IRepository<T>>();
}
}
}
個人的には、必要が見つかるまで、タイプを底に詰めるつもりはありません。
あなたの質問の残りの半分は、テストとキャッシュの目的で IRepository のメモリ内実装を簡単に提供できるかどうかだと思います。これについては、linq-to-objects が遅くなる可能性があるので注意してください。次のようなものが見つかるかもしれません。 http://www.codeplex.com/i4o 役に立つ。
他のヒント
テーブルごとにリポジトリを作成しないでください。
代わりに、ドメイン モデルに存在するすべての「エンティティ ルート」(または集約ルート) に対してリポジトリを作成する必要があります。パターンの詳細と実際の例をここで確認できます。