c# - 在定义仅改变它们处理的参数的数据类型的函数类时,如何避免代码重复?
问题描述
我有一个使用 SQLite.SQLiteAsyncConnection 执行数据库操作的程序。我想使用两种不同的模型,比如客户和汽车。客户可以定义为
public class Customer
{
[PrimaryKey]
public string Id {get; set;}
public string Name {get; set;}
public int NumberOfKids {get; set;}
public decimal Budget {get; set;}
}
和 Cars 可以定义为
public class Car
{
[PrimaryKey]
public string Id {get; set;}
public int Year {get; set;}
public string Make {get; set;}
public string Model {get; set;}
public int Mileage {get; set;}
}
请注意,模型具有不同数量和类型的成员,但都使用字符串 Id 作为主键。数据库操作在我从公共接口 IDataStore 继承的类中定义,该接口声明了添加、更新、删除和获取项目的操作:
public interface IDataStore<T>
{
Task<bool> AddItemAsync(T item);
Task<bool> UpdateItemAsync(T item);
Task<bool> DeleteItemAsync(T item);
Task<T> GetItemAsync(string id);
Task<IEnumerable<T>> GetItemsAsync(bool forceRefresh = false);
}
我是否必须为 Cars 和 Customers 定义单独的 DataStore 类或单独的重载函数,即使除了它们采用和用作参数的数据类型之外,这两个模型的这些操作完全相同?我希望能够声明的是
public class SQLiteItemDataStore : IDataStore<var>
{
...
public async Task<var> GetItemAsync(string Id)
{
return await DrinkDatabase.Table<var>().Where(i => i.Id == id).FirstOrDefaultAsync();
}
...
}
但这当然会产生编译器错误,因为您只能在变量声明中使用“var”,所以我必须为 Cars 创建一个类
public class SQLiteItemDataStore : IDataStore<Car>
{
...
public async Task<Car> GetItemAsync(string Id)
{
return await DrinkDatabase.Table<Car>().Where(i => i.Id == id).FirstOrDefaultAsync();
}
...
}
另一个给客户
public class SQLiteItemDataStore : IDataStore<Customer>
{
...
public async Task<Customer> GetItemAsync(string Id)
{
return await DrinkDatabase.Table<Customer>().Where(i => i.Id == id).FirstOrDefaultAsync();
}
...
}
是否有一些标准方法可以避免为我想与数据库一起使用的每种模型类型重复数据库操作功能?
解决方案
我认为您正在寻找以下解决方案:
public interface IDataStore
{
Task<bool> AddItemAsync<T>(T item);
Task<bool> UpdateItemAsync<T>(T item);
Task<bool> DeleteItemAsync<T>(T item);
Task<T> GetItemAsync(string id);
Task<IEnumerable<T>> GetItemsAsync(bool forceRefresh = false);
}
public class SQLiteDataStore
{
...
public async Task<T> GetItemAsync<T>(string Id)
{
return await DrinkDatabase.Table<T>().Where(i => i.Id == id).FirstOrDefaultAsync();
}
...
}
但这不是常用的方法,而且它不起作用,因为您不知道 T 具有 Id 属性。
尝试这个:
您应该定义一个基实体类并让您的所有模型都继承自它
public abstract class Entity { public abstract string Id {get;set;} } public class Car : Entity { //all others cars properties } public class Customer : Entity { //all others customers properties }
您应该定义一个通用的通用存储库接口(就像您对 IDataStore< T > 所做的那样)
public interface IRepository<T> where T : Entity // T must inherit from Entity { //all the methods you defined in IDataStore<T> }
创建一个实现 IRepository 的所有方法的抽象类
public abstract class RepositoryBase<T> : IRepository<T> where T : Entity { //all the methods of IRepository<T> are implemented in this class //now you don't have problems when calling t.Id if t is of type T because compiler knows that T derives from Entity }
为您的实体创建特定接口
public interface ICarRepository : IRepository<Car> { //add car specific methods, i.e. IQueryable<Car> GetCarByCustomer(string customerId); } public interface ICustomerRepository : IRepository<Customer> { //add customer specific methods, i.e. Task<Customer> GetCustomerByCar(string carId); }
创建具体类
public class CarRepository : RepositoryBase<Car>, ICarRepository { //here you have to implement only car specific methods, i.e. IQueryable<Car> GetCarByCustomer(string customerId); }
这是最常用的方法,又名存储库模式
推荐阅读
- elixir - Elixir:集成测试误报,为什么会失败以及如何防止进一步的误报?
- graphql - 如何为 Urql 客户端设置授权标头?
- c# - 如何在长方法中显示弹出通知?触发客户端加载中间方法?
- security - 向 Internet 公开内部 Web 应用程序
- c++ - std::vector::~vector 的时间复杂度是多少?
- python - Anaconda 中 Python Spyder 中的文本编辑器不断滞后
- java - 如果继续重新实例化对象,Java SQL .close() 是否必要?
- python - 使用 QGraphicsSvgItem 元素的 UI
- flutter - Flutter 是否具有等效的 onOpen 或 onStart 触发器功能?
- javascript - 如何在输入字符串中找到特殊字符?