Repositoryパターン(aka DAOパターン)について

SomaではRepositoryパターンは必須というわけではないですが、推奨しています。
次のような抽象クラスを提供しています(メソッドとコンストラクタはシグネチャだけを表示)。

public abstract class RepositoryBase<TObjectContext, TEntity> where TObjectContext: ObjectContext where TEntity: EntityObject
{
    // Fields
    private readonly Func<TObjectContext> _contextProvider;
    private readonly string _entitySetName;

    // Methods
    protected RepositoryBase(Func<TObjectContext> contextProvider);
    protected RepositoryBase(Func<TObjectContext> contextProvider, Func<Type, string> entitySetNameResolver);
    protected virtual TObjectContext GetObjectContext();
    protected virtual TResult Execute<TResult>(Func<TObjectContext, TResult> contextHandler);
    protected virtual IList<TElement> ExecuteResourceQuery<TElement>(Expression<Func<string>> sqlKeyExpression, object param = null);
    protected virtual void IterateResourceQuery<TElement>(Action<TElement, IterationContext> callback, Expression<Func<string>> sqlKeyExpression, object param = null);
    protected virtual int ExecuteResourceCommand(Expression<Func<string>> sqlKeyExpression, object param = null);
    public virtual void Insert(TEntity entity);
    public virtual void Update(TEntity entity);
    public virtual void Delete(TEntity entity);
}

このクラスを使うと、ObjectContextが隠蔽されるのでObjectContextに対してアプリがusingステートメントを使う必要がなくなります(RepositoryBaseがObjectContextの破棄を引き受けます)。usingステートメントは便利ですが、必ず使い忘れる人がいるので、抽象度の高いAPIには向いていないと思っています。

SQLファイルを実行できるのは、次のメソッドです。

  • ExecuteResourceQuery
    • 検索系SQLファイルを扱います。
  • IterateResourceQuery
    • 検索系SQLファイルを使ってエンティティを1件ずつ処理します。
  • ExecuteResourceCommand
    • 更新系SQLファイルを扱います。

LINQ to Entityは次のメソッドで扱います(わざわざこのメソッドを用意しているのはObjectContextを抽象クラス側でDisposeできるようにするため)。

  • Execute

自動生成の更新系はEntity Frameworkにおまかせするpublicなメソッドを用意しています。

  • Insert
  • Update
  • Delete


RepositoryBaseの利用例ですが、こんな感じになります。この例はEmployeeエンティティに対するRepositoryです。

internal class EmployeeRepository : RepositoryBase<SampleEntities, Employee>
{
	public EmployeeRepository()
		: base(() => new SampleEntities("name=Soma.SampleEntities"))
	{
	}

	// LINQ to Entityで1件取得
	public Employee SelectById(int id)
	{
		return Execute(context => context.Employee.Where(e => e.Id == id).Single());
	}

	// SQLファイルを指定して取得
	public Employee SelectByName(string name)
	{
		return ExecuteResourceQuery<Employee>(() => EmployeeResources.SelectByName, new {name}).First();
	}

	// SQLファイルを指定して実行し、1件ごとに指定されたcallbackを呼び出す
	public void Iterate(Action<Employee, IterationContext> callback)
	{
		IterateResourceQuery(callback, () => EmployeeResources.SelectAll);
	}
}