Предположим, у меня есть модель предметной области, которая имеет соответствие 1: 1 физической модели.Также:
- все таблицы в физической модели имеют имя столбца 'Id' в качестве первичного ключа
- Многие из таблиц имеют столбец отметки времени LastChanged
- Некоторые таблицы содержат локализованные данные
Цель : создать классы модели предметной области (POCO) и соответствующие репозитории.Инструменты - VS2010, EF4.1
Наиболее очевидный подход состоит в том, чтобы сгенерировать модель EF из БД и затем запустить над ней генератор POCO T4.Вывод представляет собой набор классов POCO.
//POCO classes
public class Country
{
public int Id { get; set; }
public string Name { get; set; }
public DateTime LastModified { get; set; }
...
}
public class User
{
public int Id { get; set; }
public string FirtsName { get; set; }
public DateTime LastModified { get; set; }
...
}
Пока все хорошо, но мы сталкиваемся с некоторыми дублирующими кодами при реализации репозиториев.И на уровне определения интерфейса:
public interface ICountryRepository
{
IEnumerable<Country> FindAll();
Country FindById(int id);
void Add(Country country);
void Delete(Country country);
}
//Here the ONLY difference is the type of the entity
public interface IUserRepository
{
IEnumerable<User> FindAll();
User FindById(int id);
void Add(User user);
void Delete(User user);
}
И на уровне реализации:
class CountryRepository : ICountryRepository
{
IEnumerable<Country> FindAll()
{
//implementation
}
Country FindById(int id)
{
//find by id logic
}
void Add(Country country)
{
//logic
}
void Delete(Country country)
{
//logic
}
}
class UserRepository : IUserRepository
{
IEnumerable<User> FindAll()
{
//the only difference in the implementation
//is the type of returned entity
}
User FindById(int id)
{
//the only difference in the implementation
//is the type of returned entity
}
void Add(User user)
{
//the only difference in the implementation
//is the type of returned entity
}
void Delete(User user)
{
//the only difference in the implementation
//is the type of returned entity
}
}
Принимая во внимание, что большая часть приведенного выше кода может быть написана более обобщенно , мы также можем использовать следующий подход.
Создать иерархию объектов POCO:
public class EntityBase
{
public int Id { get; set; }
}
public class TrackableEntity : EntityBase
{
public DateTime LastChanged { get; set; }
}
public class LocalizedEntity : TrackableEntity
{
public int ResourceId { get; set; }
}
public class Country : LocalizedEntity
{
}
И затем иерархию хранилища с базовой реализацией:
public interface IRepository<TEntity> where TEntity : EntityBase
{
IEnumerable<TEntity> FindAll();
TEntity FindById(int id);
void Add(TEntity entity);
void Delete(TEntity entity);
}
public interface ILocalizedRepository<TEntity> : IRepository<TEntity> where TEntity : LocalizedEntity
{
IEnumerable<TEntity> FindByCultureIso2(string cultureIso2);
}
public interface ICountryRepository : ILocalizedRepository<Country>
{
}
internal class RepositoryBase<TEntity> : IRepository<TEntity> where TEntity : EntityBase
{
private readonly IObjectSet<TEntity> _objectSet;
public RepositoryBase(ObjectContext database)
{
_objectSet = database.CreateObjectSet<TEntity>();
}
protected virtual IQueryable<TEntity> All()
{
return _objectSet;
}
public virtual IEnumerable<TEntity> FindAll()
{
return All().ToList();
}
public virtual TEntity FindById(int id)
{
return All().Where(entity => entity.Id == id).SingleOrDefault();
}
public virtual void Add(TEntity entity)
{
_objectSet.AddObject(entity);
}
public virtual void Delete(TEntity entity)
{
_objectSet.DeleteObject(entity);
}
}
internal class LocalizedRepositoryBase<TEntity> : RepositoryBase<TEntity>, ILocalizedRepository<TEntity> where TEntity : LocalizedEntity
{
public LocalizedRepositoryBase(ObjectContext database) : base(database)
{
}
protected override IQueryable<TEntity> All()
{
return (base.All() as ObjectSet<TEntity>).Include("Resource.LocalizedResources.Culture");
}
public IEnumerable<TEntity> FindByCultureIso2(string cultureIso2)
{
IEnumerable<TEntity> entities = All().Where(...);
return entities.ToList();
}
}
internal class CountryRepository : LocalizedRepositoryBase<Country>, ICountryRepository
{
public CountryRepository(ObjectContext database) : base(database)
{
}
}
Неоспоримым преимуществом последнего подхода является то, что код гораздо более структурирован, что позволяет избежать дублирования кода.
Но этот сценарий вряд ли поддается генерации кода T4, который открывает поверхность для большого объема ручной работы.
Буду признателен, если вы дадите мне знать, что вы думаете о следующем:
- Как вы думаете, действительно ли красота кода действительно стоит того, чтобы реализовать его вручную?
- Есть ли другие способы избавиться от дублирования кода?