如何重构Data Access Layer并保留Bar的实现特性?
嘿,这个问题太常见了——DAL层堆满重复的CRUD代码,看着就头疼!咱们可以用泛型抽象类+接口的组合来完美解决:既把Foo和FooBar的公共逻辑彻底复用,又能给Bar留足空间实现它的特殊逻辑。下面一步步来拆解:
重构方案
1. 先定义通用数据访问契约
首先咱们给所有DAL操作定一个统一的接口,确保Create、Get、Set这三个方法的规范一致:
// 先给所有实体加一个带Id的接口,方便后续通用逻辑处理 public interface IHasId { int Id { get; set; } } // 定义通用DAL接口,约束实体必须实现IHasId public interface IDataAccess<T> where T : class, IHasId { bool Create(T entity); T Get(int id); bool Set(T entity); }
给实体加IHasId是为了后续通用逻辑里不用写反射或者表达式树,直接用x.Id就能定位实体,更优雅。
2. 实现通用泛型基类
把Foo和FooBar共用的CRUD逻辑全部抽到这个基类里,这是复用的核心:
public class BaseDataAccess<T> : IDataAccess<T> where T : class, IHasId { // 用工厂方法封装DbContext创建,方便后续扩展(比如用DI或者连接字符串配置) private readonly Func<FooModelDBcontext> _dbContextFactory; private readonly DbSet<T> _dbSet; public BaseDataAccess(FooModelDBcontext dbContext) { _dbContextFactory = () => dbContext; _dbSet = dbContext.Set<T>(); } // 通用Create逻辑 public virtual bool Create(T entity) { using (var db = _dbContextFactory()) { _dbSet.Add(entity); return db.SaveChanges() > 0; } } // 通用Get逻辑 public virtual T Get(int id) { using (var db = _dbContextFactory()) { return _dbSet.FirstOrDefault(x => x.Id == id); } } // 通用Set逻辑 public virtual bool Set(T entity) { using (var db = _dbContextFactory()) { var temp = _dbSet.SingleOrDefault(x => x.Id == entity.Id); if (temp == null) return false; db.Entry(temp).CurrentValues.SetValues(entity); return db.SaveChanges() > 0; } } }
这里的virtual关键字很关键——它允许子类(比如Bar的DAL)重写这些方法,实现自己的特殊逻辑。
3. 快速实现Foo和FooBar的DAL
因为它们的逻辑和基类完全一致,所以直接继承基类就行,不用写任何额外代码:
// Foo的DAL public class FooDataAccess : BaseDataAccess<FooDlo> { public FooDataAccess(FooModelDBcontext dbContext) : base(dbContext) { } } // FooBar的DAL public class FooBarDataAccess : BaseDataAccess<FooBarDlo> { public FooBarDataAccess(FooModelDBcontext dbContext) : base(dbContext) { } }
一行重复代码都不用写,直接复用基类的所有逻辑。
4. 为Bar实现特殊化DAL
Bar的逻辑复杂?没关系,继承基类之后,只需要重写需要特殊处理的方法就行,不用全部重写:
public class BarDataAccess : BaseDataAccess<BarDlo> { public BarDataAccess(FooModelDBcontext dbContext) : base(dbContext) { } // 重写Create方法,加入Bar专属的业务逻辑 public override bool Create(BarDlo entity) { // 先执行基类的通用创建逻辑 var baseResult = base.Create(entity); if (baseResult) { // 这里加Bar的特殊逻辑:比如同步更新关联表、记录操作日志、验证特殊字段等等 // ...你的特殊代码 } return baseResult; } // 完全重写Set方法,替换基类的通用逻辑 public override bool Set(BarDlo entity) { using (var db = _dbContextFactory()) { var temp = db.Bars.SingleOrDefault(x => x.Id == entity.Id); if (temp == null) return false; // 比如只更新指定字段,而不是全部覆盖 temp.Name = entity.Name; temp.Status = entity.Status; // ...其他特殊处理逻辑 return db.SaveChanges() > 0; } } }
这样既保留了Bar的特殊性,又不用重复写那些通用的DbContext创建、SaveChanges代码。
5. 调用方式示例
现在用起来就很清爽了:
// 创建FooBar var fooBarDal = new FooBarDataAccess(new FooModelDBcontext()); fooBarDal.Create(new FooBarDlo { Id = 1, Name = "Test FooBar" }); // 获取Bar var barDal = new BarDataAccess(new FooModelDBcontext()); var targetBar = barDal.Get(2); // 更新Foo var fooDal = new FooDataAccess(new FooModelDBcontext()); fooDal.Set(new FooDlo { Id = 3, Name = "Updated Foo" });
额外优化建议
- 如果项目用了依赖注入(DI),可以把
IDataAccess<T>和对应的实现注册到容器里,不用手动new DbContext和DAL类,更符合SOLID原则。 - 可以把DbContext的创建改成工厂模式,避免每次都new,提升性能和可测试性。
内容的提问来源于stack exchange,提问作者xdtTransform
相关产品推荐
相关产品推荐

