分层 POCO 对象 &存储库
假设我有一个与物理模型一一对应的领域模型。另外:
- 物理模型中的所有表都将列名“Id”作为主键
- 许多表都有“LastChanged”时间戳列
- 一些表包含本地化数据
目标:创建域模型类(POCO)和适当的存储库。工具 - VS2010、EF4.1
最明显的方法是从数据库生成 EF 模型,然后在其上运行 T4 POCO 生成器。输出是一组 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 代码生成,因为 T4 代码生成为大量手动工作开辟了空间。
如果您让我知道您对以下问题的想法,我将不胜感激:
- 您认为代码的美丽是否真的值得手动实现它?
- 还有其他方法可以消除代码重复吗?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
如果您最关心的是 POCO 的手动实现,请使用接口来定义共享功能而不是基类:
现在您不需要手动实现 POCO。生成器将为您创建实体,您只需为每个实体定义您的部分即可定义它实现的接口。
如果您对 T4 修改感到满意,您甚至可以直接向 T4 添加接口分辨率,而无需定义那些部分部分。
If your biggest concern is manual implementation of POCOs use interfaces to define shared features instead of base classes:
Now you don't need to implement POCOs manually. Generator will create entities for you and you will only need to define your partial part for each entity to define which interfaces it implements.
If you are happy with T4 modification you can even add interface resolution directly to T4 and you will not need to define those partial parts.
如果您正在生成代码,则可以复制,只需不要触摸生成的代码就可以了。
If you are generating code it's ok to duplicate, just don't touch generated code and you'll be just fine.