You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何重构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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 09:30:52