C#多态类类型匹配问题及API缓存封装设计求助
用泛型基类实现通用逻辑复用
问题根源
你遇到的编译错误,本质是C#类型系统的约束:派生类重写基类方法时,不能将返回/参数类型从接口直接替换为具体实现(哪怕这个实现继承了接口)。基类ZymonicFilter定义的DbSet()返回DbSet<IZymonicFilterResult>,派生类返回DbSet<CbosFilStockLookupResult>属于类型不匹配;同理WhereClause方法的参数类型也无法自动兼容。
核心解决方案:泛型基类
将基类改造为泛型基类,通过类型参数约束绑定具体的结果类型,让基类的通用逻辑(缓存查询、API调用、数据库更新)能适配不同API,派生类只需实现专属细节。
步骤1:定义结果接口
保留统一的结果接口,作为所有API结果的约束:
public interface IZymonicFilterResult { // 所有结果共有的属性,如缓存标识、创建时间 string CacheKey { get; set; } }
步骤2:实现泛型基类
基类通过泛型参数TResult约束为IZymonicFilterResult,封装所有通用逻辑:
public abstract class ZymonicFilter<TResult> where TResult : class, IZymonicFilterResult, new() { // 通用数据库操作:返回对应类型的DbSet protected abstract DbSet<TResult> DbSet(); // 通用查询条件构建:派生类提供具体规则 protected abstract string WhereClause(TResult searchParams); // 通用缓存查询逻辑 public virtual IObservable<IChangeSet<TResult>> QueryCache(string searchTerm) { // 从ObservableCache查询的通用逻辑,示例: // return _cache.Connect().Filter(item => item.CacheKey.Contains(searchTerm)); } // 通用API调用+缓存更新流程 public virtual async Task FetchAndUpdateCache(string searchTerm) { // 1. 查询本地数据库缓存 var searchParams = BuildSearchParams(searchTerm); var dbResults = await DbSet().FromSqlRaw($"SELECT * FROM {typeof(TResult).Name} WHERE {WhereClause(searchParams)}").ToListAsync(); // 2. 更新ObservableCache _cache.Edit(updater => updater.AddOrUpdate(dbResults)); // 3. 调用服务器API var apiResponse = await CallApi(searchTerm); // 4. 映射API结果到本地实体 var apiResults = MapApiToResult(apiResponse); // 5. 更新数据库和缓存 await DbSet().AddRangeAsync(apiResults); await _dbContext.SaveChangesAsync(); _cache.Edit(updater => updater.AddOrUpdate(apiResults)); } // 以下为派生类需实现的API专属逻辑 protected abstract Task<JObject> CallApi(string searchTerm); // 调用具体API protected abstract TResult BuildSearchParams(string searchTerm); // 构建搜索参数 protected abstract List<TResult> MapApiToResult(JObject apiResponse); // API结果转本地实体 }
步骤3:实现派生类
派生类仅需实现API专属方法,通用逻辑完全复用基类:
// 该API的专属结果实体 public class CbosFilStockLookupResult : IZymonicFilterResult { public string StockCode { get; set; } public string StockName { get; set; } public string CacheKey { get; set; } // 实现接口属性 } // 派生类:仅处理该API的专属逻辑 public class CbosFilStockLookup : ZymonicFilter<CbosFilStockLookupResult> { private readonly YourDbContext _dbContext; private readonly ObservableCache<CbosFilStockLookupResult, string> _cache; public CbosFilStockLookup(YourDbContext dbContext, ObservableCache<CbosFilStockLookupResult, string> cache) { _dbContext = dbContext; _cache = cache; } protected override DbSet<CbosFilStockLookupResult> DbSet() { return _dbContext.CbosFilStockLookupResults; } protected override string WhereClause(CbosFilStockLookupResult searchParams) { return $"StockCode LIKE '%{searchParams.StockCode}%' OR StockName LIKE '%{searchParams.StockName}%'"; } protected override async Task<JObject> CallApi(string searchTerm) { // 调用该API的具体请求逻辑 var client = new HttpClient(); var requestContent = new StringContent(JsonConvert.SerializeObject(new { SearchTerm = searchTerm }), Encoding.UTF8, "application/json"); var response = await client.PostAsync("https://your-server/api/cbos-stock-lookup", requestContent); return JObject.Parse(await response.Content.ReadAsStringAsync()); } protected override CbosFilStockLookupResult BuildSearchParams(string searchTerm) { return new CbosFilStockLookupResult { StockCode = searchTerm, StockName = searchTerm }; } protected override List<CbosFilStockLookupResult> MapApiToResult(JObject apiResponse) { return apiResponse["Data"].ToObject<List<CbosFilStockLookupResult>>(); } }
关键说明
- 泛型约束:
where TResult : class, IZymonicFilterResult, new()确保TResult是引用类型、实现了统一接口、且有无参构造函数,满足EF和缓存操作的要求。 - 逻辑复用:基类中的
FetchAndUpdateCache、QueryCache等方法无需派生类修改,所有API共享同一套缓存、数据库、API交互流程。 - 类型安全:通过泛型指定具体
TResult,避免了类型转换的繁琐,同时彻底解决了之前的编译错误。
内容的提问来源于stack exchange,提问作者Alex Masidlover
相关产品推荐
相关产品推荐

