Repository 패턴 구현 경험
-
01-07-2019 - |
문제
새로운 asp.net 웹 프로젝트를 시작할 준비를 하고 있으며 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 유용한.
다른 팁
모든 테이블에 대해 저장소를 생성해서는 안 됩니다.
대신 도메인 모델에 존재하는 모든 '엔터티 루트'(또는 집계 루트)에 대한 저장소를 만들어야 합니다.패턴에 대해 자세히 알아보고 여기에서 실제 예제를 볼 수 있습니다.