如何无需Switch/Case实现多类型DomainObject批量写入数据库?
替代Switch/Case的几种实用方案
针对你现在用Switch/Case处理多类型实体批量写入的场景,这里有几个更灵活、易维护的替代方案,适配不同的场景需求:
方案1:类型-处理逻辑字典映射
提前把实体类型和对应的创建逻辑绑定到字典里,遍历的时候直接根据类型取逻辑执行,不用写硬编码的Switch/Case。后续新增实体,只需要往字典里加一条条目就行。
代码示例:
先在Program类里初始化这个映射字典:
public class Program { public static List<DomainObject> allItems; public static DbContextFactory dbContextFactory = new DbContextFactory(); // 提前构建类型到创建委托的映射 private static readonly Dictionary<Type, Func<DomainObject, Task>> _entityCreators; static Program() { _entityCreators = new Dictionary<Type, Func<DomainObject, Task>> { { typeof(Resistor), async item => await new GenericDataService<Resistor>(dbContextFactory).Create((Resistor)item) }, { typeof(Capacitor), async item => await new GenericDataService<Capacitor>(dbContextFactory).Create((Capacitor)item) }, { typeof(Transistor), async item => await new GenericDataService<Transistor>(dbContextFactory).Create((Transistor)item) } }; } static void Main(string[] args) { allItems = new List<DomainObject> { new Resistor(), new Capacitor(), new Transistor(), }; } }
然后修改批量写入的方法(注意改成异步,之前的代码没等待Create完成,会有异步问题):
public static async Task AddItemsToDatabase() { foreach(var item in allItems) { if (_entityCreators.TryGetValue(item.GetType(), out var createLogic)) { await createLogic(item); } else { // 处理未知类型,比如抛异常或者打日志 throw new NotSupportedException($"不支持的实体类型:{item.GetType().Name}"); } } }
方案2:反射动态调用泛型服务
用反射自动创建对应类型的GenericDataService实例并调用Create方法,不用手动维护每个类型的映射,适合实体类型多、经常新增的场景。
代码示例:
public static async Task AddItemsToDatabase() { foreach(var item in allItems) { Type entityType = item.GetType(); // 动态构建GenericDataService<T>的类型 Type serviceType = typeof(GenericDataService<>).MakeGenericType(entityType); // 创建服务实例 object dataService = Activator.CreateInstance(serviceType, dbContextFactory); // 获取Create方法并调用 MethodInfo createMethod = serviceType.GetMethod(nameof(GenericDataService<DomainObject>.Create)); await (Task)createMethod.Invoke(dataService, new[] { item }); } }
如果担心反射性能,可以缓存一下serviceType和createMethod,比如用一个静态字典存类型对应的方法,避免每次循环都反射。
方案3:访问者模式(需修改实体类)
如果允许修改DomainObject及其子类,用访问者模式可以完全消除Switch/Case,符合开闭原则,新增实体时只需要扩展代码,不用修改原有批量逻辑。
第一步,定义访问者接口:
public interface IEntityVisitor { Task Visit(Resistor resistor); Task Visit(Capacitor capacitor); Task Visit(Transistor transistor); }
第二步,给DomainObject抽象类加Accept方法:
public abstract class DomainObject { public ItemType ItemType { get; set; } public abstract Task Accept(IEntityVisitor visitor); }
第三步,每个实体类实现Accept:
public class Resistor : DomainObject { public override async Task Accept(IEntityVisitor visitor) { await visitor.Visit(this); } } public class Capacitor : DomainObject { public override async Task Accept(IEntityVisitor visitor) { await visitor.Visit(this); } } public class Transistor : DomainObject { public override async Task Accept(IEntityVisitor visitor) { await visitor.Visit(this); } }
第四步,实现创建逻辑的访问者:
public class EntityCreatorVisitor : IEntityVisitor { private readonly DbContextFactory _dbContextFactory; public EntityCreatorVisitor(DbContextFactory dbContextFactory) { _dbContextFactory = dbContextFactory; } public async Task Visit(Resistor resistor) { await new GenericDataService<Resistor>(_dbContextFactory).Create(resistor); } public async Task Visit(Capacitor capacitor) { await new GenericDataService<Capacitor>(_dbContextFactory).Create(capacitor); } public async Task Visit(Transistor transistor) { await new GenericDataService<Transistor>(_dbContextFactory).Create(transistor); } }
最后修改批量写入方法:
public static async Task AddItemsToDatabase() { var creatorVisitor = new EntityCreatorVisitor(dbContextFactory); foreach(var item in allItems) { await item.Accept(creatorVisitor); } }
内容的提问来源于stack exchange,提问作者Iliya Belan
相关产品推荐
相关产品推荐

