Ради замысла этой темы вот что я в итоге сделал:
Я реализовал комбинацию шаблонов «Единица работы» и «Репозиторий». Класс Unit of Work - это то, с чем работает потребляющий код, и он предоставляет все операции, которые могут быть выполнены с моими корневыми объектами. Существует одно UoW на корневую сущность. UoW использует класс репозитория через интерфейс. Фактическая реализация хранилища зависит от используемой технологии доступа к данным.
Так, например, если у меня есть сущность клиента, и мне нужно поддерживать извлечение и обновление каждой записи, у меня будет что-то вроде:
public interface ICustomerManager
{
ICustomer GetCustomer(Guid customerId);
void SaveCustomer(ICustomer customer);
}
public class CustomerManager : ICustomerManager
{
public CustomerManager(ICustomerRepository repository)
{
Repository = repository;
}
public ICustomerRepository Repository { get; private set; }
public ICustomer GetCustomer(Guid customerId)
{
return Repository.SingleOrDefault(c => c.ID == customerId);
}
public void SaveCustomer(ICustomer customer)
{
Repository.Save(customer);
}
}
public interface ICustomerRepository : IQueryable<ICustomer>
{
void Save(ICustomer customer);
}
Я использую платформу Inversion of Control для внедрения реализации ICustomerRepository в класс CustomerManager во время выполнения. Класс реализации будет находиться в отдельной сборке, которую можно заменить при изменении технологии доступа к данным. Все, что нас беспокоит, - это то, что хранилище реализует каждый метод, используя контракт, определенный выше.
В качестве примечания, чтобы сделать это с Linq-to-SQL, я просто создал класс LinqCustomerRepository, который реализует ICustomerRepository, и добавил частичный класс для сгенерированного класса сущностей Customer, который реализует ICustomer. Затем я могу вернуть сущность L2S из репозитория в качестве реализации интерфейса ICustomer для работы с UoW и вызывающим кодом, и они не будут мудрее, если сущность возникла из кода L2S.