泛型Dialog类主键类型适配求助:无法从int切换为short
你现在的核心困境是:基类Dialog<T>把服务的主键类型硬编码成了int,但遇到了主键为tinyint(对应C#的short)的实体,又没法修改基类的泛型参数定义,用new覆盖属性也因为基类代码依赖Service而失效。下面给你两个可行的解决方案:
方案一:通过非泛型接口抽象解耦(推荐,更优雅)
这个方案的核心是把基类对服务的依赖从带主键泛型的接口,改成不带主键的通用接口,让基类只关心它需要的方法,不纠结主键类型。
步骤1:定义非泛型的服务基接口
先给你的IEntityService<T, TKey>加一个非泛型基接口,把基类Dialog<T>用到的方法(比如Search)放到这个基接口里:
// 非泛型基接口,只包含基类需要的方法 public interface IEntityService<T> where T : class, IEntity { Task Search(Expression<Func<T, bool>> criteria); // 基类用到的其他公共方法也可以放在这里 } // 原有的泛型接口继承这个基接口,保留主键相关的方法 public interface IEntityService<T, TKey> : IEntityService<T> where T : class, IEntity where TKey : struct { // 依赖主键类型的方法,比如根据ID查询 Task<T> GetById(TKey id); }
步骤2:修改基类的Service属性类型
把基类Dialog<T>里的Service属性类型改成刚才定义的非泛型接口IEntityService<T>:
public class Dialog<T> : IDialogAble<T> where T : class, IEntity { // 现在基类只依赖非泛型接口,不关心主键类型 public IEntityService<T> Service { get; set; } public async Task Suchen(Expression<Func<T, bool>> criteria) { Cursor = Cursors.Wait; await Service.Search(criteria); Cursor = Cursors.Arrow; } }
步骤3:在派生类中使用带主键的服务
现在派生类可以自由定义对应主键类型的服务,同时把它赋值给基类的Service属性,确保基类方法正常工作:
public class MyObjectDialog : Dialog<MyObject> { // 派生类自己的带short主键的服务 public new IEntityService<MyObject, short> Service { get; set; } public MyObjectDialog(IServiceFactory serviceFactory, DbContext context) { // 创建short版本的服务 Service = serviceFactory.CreateEntityService<MyObject, short>(context); // 把派生服务赋值给基类的Service属性,保证基类方法能调用到 base.Service = Service; } }
这样一来,基类的Suchen方法可以正常调用Search,派生类也能直接使用Service调用依赖short主键的方法(比如GetById((short)1))。
方案二:适配器模式(无需修改基类接口)
如果不能修改基类的Service属性类型(比如基类是第三方库代码),可以用适配器模式把IEntityService<T, short>包装成IEntityService<T, int>,让基类可以正常使用。
步骤1:创建适配器类
写一个适配器,把short版本的服务转换成int版本的:
public class EntityServiceShortToIntAdapter<T> : IEntityService<T, int> where T : class, IEntity { private readonly IEntityService<T, short> _innerService; public EntityServiceShortToIntAdapter(IEntityService<T, short> innerService) { _innerService = innerService; } // 适配Search方法,直接转发 public async Task Search(Expression<Func<T, bool>> criteria) { await _innerService.Search(criteria); } // 适配GetById方法,把int转成short(注意范围检查) public async Task<T> GetById(int id) { if (id > short.MaxValue || id < short.MinValue) throw new ArgumentOutOfRangeException(nameof(id), "ID超出short类型的取值范围"); return await _innerService.GetById((short)id); } // 其他方法同理,根据实际需求进行类型转换适配 }
步骤2:在派生类中使用适配器
在派生类的构造函数里,创建short版本的服务,然后用适配器包装后赋值给基类的Service属性:
public class MyObjectDialog : Dialog<MyObject> { // 如果派生类需要直接用short版本的服务,也可以单独保存 private IEntityService<MyObject, short> _shortService; public MyObjectDialog(IServiceFactory serviceFactory, DbContext context) { _shortService = serviceFactory.CreateEntityService<MyObject, short>(context); // 用适配器包装成int版本,赋值给基类的Service Service = new EntityServiceShortToIntAdapter<MyObject>(_shortService); } // 派生类自己的方法可以直接用_shortService调用short主键的方法 public async Task<MyObject> GetByShortId(short id) { return await _shortService.GetById(id); } }
这个方案不需要修改基类的任何代码,只要保证主键的值在short的范围内,就能正常工作。
内容的提问来源于stack exchange,提问作者Presi

