ほとんどすべての CRUD 機能をカプセル化するリポジトリ抽象クラスがあります。
public abstract class DataRepository<T> : IRepository<T>
where T : class
{
public DataContext Context { get; private set; }
public TransactionScope Transaction { get; private set; }
/// <summary>
/// A <see cref="bool"/> function that compares the keys for fetching a single item, for example:
/// return item1.Id == item2.Id (as an anonymous delegate).
/// </summary>
public Func<T, T, bool> KeyCompare { get; private set; }
/// <summary>
/// Creates a new data repository.
/// </summary>
/// <param name="context"></param>
/// <param name="scope"></param>
/// <param name="keyCompare">
/// A <see cref="bool"/> function that compares the keys for fetching a single item, for example:
/// return item1.Id == item2.Id (as an anonymous delegate).
/// </param>
public DataRepository(DataContext context, TransactionScope scope, Func<T, T, bool> keyCompare)
{
Context = context;
Transaction = scope;
KeyCompare = keyCompare;
}
public virtual T Item(T item)
{
return Items().SingleOrDefault(e => KeyCompare(e, item));
}
public virtual IEnumerable<T> Items()
{
return DataTable.AsEnumerable();
}
protected virtual Table<T> DataTable { get { return Context.GetTable<T>(); } }
/// <summary>
/// A method that updates the non-key fields of an existing entity with those of specified <see cref="item"/>.
/// Called by the <see cref="Save"/> method.
/// </summary>
/// <param name="existing">The existing record to update.</param>
/// <param name="item">A <see cref="T"/> object containing the values to update <see cref="existing"/> object with.</param>
/// <returns></returns>
protected abstract void UpdateExisting(T existing, T item);
/// <summary>
/// A method that updates an existing item or creates a new one, as needed.
/// </summary>
/// <param name="item">The entity containing the values to be saved.</param>
public virtual void Save(T item)
{
var existing = Item(item);
if (existing != null)
{
UpdateExisting(existing, item);
}
else
{
DataTable.InsertOnSubmit(item);
}
Context.SubmitChanges();
}
/// <summary>
/// A method that saves all specified items (creates new, updates existing).
/// </summary>
/// <param name="items">The entities to be saved.</param>
public virtual void Save(IEnumerable<T> items)
{
foreach (var item in items)
{
Save(item);
}
}
/// <summary>
/// A method that deletes specified item.
/// </summary>
/// <param name="item"></param>
public virtual void Delete(T item)
{
var existing = Item(item);
if (existing != null)
{
DataTable.DeleteOnSubmit(existing);
}
Context.SubmitChanges();
}
public virtual void Delete(IEnumerable<T> items)
{
var selection = Items().Where(e => items.Any(item => KeyCompare(e, item)));
DataTable.DeleteAllOnSubmit(selection);
Context.SubmitChanges();
}
}
プロパティは派生クラスでこのKeyCompare
ように使用されるため、基本クラスはリポジトリ内の単一のアイテムを分離する方法を認識します (すべての「エンティティ」に「Id」プロパティがあるわけではなく、一部のキーは複数の列にまたがります - このソリューションはその特定の点を解決します):
public AuthInfoRepository(DataContext context, TransactionScope scope)
: base(context, scope, (item1, item2) => { return item1.Id == item2.Id;})
{ }
このKeyCompare
プロパティは、派生クラスが次のように単にUpdateExisting
メソッドを実装できるようにする基礎です。
protected override void UpdateExisting(AuthInfo existing, AuthInfo item)
{
existing.AuthId = item.AuthId;
existing.ActiveDirectoryGroup = item.ActiveDirectoryGroup;
}
残り (実際の CRUD) はすべて基本クラスによって処理されます。この抽象的なリポジトリを使用して、具体的なものを数秒ではないにしても数分で実装し、各実装に固有のコードのみを記述しました。とても乾いているので、のどが渇いています。
はDataRepository<T>
SQL Server を扱っているため、モック用にさらに別の実装が必要でした。これを呼び出してListRepository<T>
、ほとんど同じことを行います (ただしContext
、 とTransaction
プロパティの両方が を返しますnull
)。ここに投稿する必要があるのは、コンストラクターの署名だけだと思います。
public ListRepository(IEnumerable<T> items, Func<T, T, bool> keyCompare)
これで、テストの準備が整いました。Ninject を IoC コンテナーとして使用したいと考えています。私の問題は、匿名デリゲートを次のように渡す方法がわからないことですConstructorArgument
。
Bind<IRepository<AuthInfo>>().To<ListRepository<AuthInfo>>()
.WithConstructorArgument("items", _mockAuthInfo)
.WithConstructorArgument("keyCompare", ??????);
私がやろうとしていることは実行可能ですか、それとも複雑すぎますか? それが良い/きれいなコードかどうかを尋ねるつもりはありませんが、ここで建設的なコメントを歓迎します: