通过 SQLite 在模型中使用接口
Using interfaces in models with SQLite
假设我有这样的界面:
public interface IUser
{
int Id { get; }
string Name { get; }
List<IMonthlyBudget> MonthlyBudget { get; }
}
然后我有一个实现这个的模型:
public class User : IUser
{
public int Id { get; set; }
public string Name { get; set; }
public List<IMonthlyBudget> MonthlyBudget { get; set; }
}
这里我有 IMonthlyBudget:
public interface IMonthlyBudget
{
int Id { get; }
float MonthlyMax { get; }
float CurrentSpending { get; }
float MonthlyIncome { get; }
}
现在我有了我的模型。但是问题来自于使用 SQLite。 SQLite 无法理解 IMonthlyBudget 的真正实现是什么。我明白为什么,但我真的不想删除接口并将真正的实现暴露给所有使用这些模型的客户。在我的项目结构中,我有一个具有所有模型接口的核心项目,模型实现在数据访问项目中。
我处理这个问题的方式有问题吗?我想我不是第一个 运行 遇到这种问题的人。保留模型接口(然后存储库等用作它们的 return 类型、参数和类似的东西)并在数据访问项目中实现实际的具体模型不是完全正常的做法吗?
谁能解释一下为什么我不能这样做:
public class User : IUser
{
public int Id { get; set; }
public string Name { get; set; }
public List<MonthlyBudget> MonthlyBudget { get; set; }
}
MonthlyBudget 实现 IMonthlyBudget,当具体模型实际实现接口时,使用具体模型作为类型而不是接口不是应该完全没问题吗?
这里有几个问题,所以我会分成几个部分:
接口的使用
接口 classes 绝对是执行操作 的好习惯。例如,您可能有一个数据服务(即数据访问层)接口,允许您执行读取和修改持久存储中数据的操作。但是,您可能有多个该数据服务的实现。一种实现可以保存到文件系统,另一种可以保存到 DBMS,另一种是用于单元测试的模拟等。
但是,在许多情况下,您不需要连接模型 classes。如果您使用贫乏的业务对象方法(与丰富的业务对象相反),那么模型 classes 通常应该只是数据的容器,或普通旧 CLR 对象 (POCO)。这意味着这些对象没有任何实际功能可言,它们不引用任何特殊库或 classes。我要放入 POCO 的唯一 "functionality" 是仅依赖于自身的一个。例如,如果您有一个具有 FirstName 和 LastName 属性 的 User 对象,您可以创建一个名为 FullName 的只读 属性,它 returns 是两者的串联。
POCO 不知道它们是如何填充的,因此可以在您的数据服务的任何实现中使用。
这应该是您使用贫乏的业务对象方法时的默认方向,但至少有一个例外,我可以想到您可能希望在何处连接您的模型。例如,您可能希望支持 SQLite 数据服务和 Realm (NoSQL) 数据服务。 Realm 对象恰好需要您的模型派生自 RealmObject。所以,如果你想在 SQLite 和 Realm 之间切换你的数据访问层,那么你必须像你正在做的那样连接你的模型。我只是以 Realm 为例,但如果您想跨其他平台使用您的模型,这也适用,例如在 UWP 应用程序中创建一个可观察的基础 class。
确定是否应该为模型创建接口的关键试金石是问自己这个问题:
"Will I need to consume these models in various consumers and will those consumers require me to define a specific base class for my models to work properly in those consumers?"
如果这个问题的答案是"yes",那么你应该为你的模型制作接口。如果答案是 "no",那么创建模型接口是无关紧要的工作,您可以放弃它,让您的数据服务实现处理其底层数据存储的细节。
SQLite 问题
无论你是否继续使用模型接口,你仍然应该有一个 SQLite 的数据访问实现,它知道它正在处理特定于 SQLite 的模型,然后你可以直接在那些特定的实现上执行所有的 CRUD 操作你的模型。那么既然你指的是一个特定的模型实现,SQLite 应该照常工作。
类型兼容性
要回答你的最后一个问题,类型系统看不到这个...
List<IMonthlyBudget> MonthlyBudget
与此类型兼容...
List<MonthlyBudget> MonthlyBudget
在我们看来,如果我有一个苹果列表,那么它应该与一个水果列表类型兼容。编译器将苹果视为一种水果,而不是将苹果列表视为 类型的水果列表。所以你不能像这样在他们之间投射...
List<IMonthlyBudget> myMonthlyBudget = (List<IMonthlyBudget>) new List<MonthlyBudget>();
但是您可以像这样将 MonthlyBudget 对象添加到 IMonthlyBudget 对象列表中...
List<IMonthlyBudget> myMonthlyBudget = new List<IMonthlyBudget>();
myMonthlyBudget.Add(new MonthlyBudget());
如果您想一次转换整个列表,也可以使用 LINQ .Cast() 方法。
这背后的原因与类型差异有关。这里有一篇很好的文章可以阐明原因:
希望对您有所帮助! :-)
假设我有这样的界面:
public interface IUser
{
int Id { get; }
string Name { get; }
List<IMonthlyBudget> MonthlyBudget { get; }
}
然后我有一个实现这个的模型:
public class User : IUser
{
public int Id { get; set; }
public string Name { get; set; }
public List<IMonthlyBudget> MonthlyBudget { get; set; }
}
这里我有 IMonthlyBudget:
public interface IMonthlyBudget
{
int Id { get; }
float MonthlyMax { get; }
float CurrentSpending { get; }
float MonthlyIncome { get; }
}
现在我有了我的模型。但是问题来自于使用 SQLite。 SQLite 无法理解 IMonthlyBudget 的真正实现是什么。我明白为什么,但我真的不想删除接口并将真正的实现暴露给所有使用这些模型的客户。在我的项目结构中,我有一个具有所有模型接口的核心项目,模型实现在数据访问项目中。
我处理这个问题的方式有问题吗?我想我不是第一个 运行 遇到这种问题的人。保留模型接口(然后存储库等用作它们的 return 类型、参数和类似的东西)并在数据访问项目中实现实际的具体模型不是完全正常的做法吗?
谁能解释一下为什么我不能这样做:
public class User : IUser
{
public int Id { get; set; }
public string Name { get; set; }
public List<MonthlyBudget> MonthlyBudget { get; set; }
}
MonthlyBudget 实现 IMonthlyBudget,当具体模型实际实现接口时,使用具体模型作为类型而不是接口不是应该完全没问题吗?
这里有几个问题,所以我会分成几个部分:
接口的使用
接口 classes 绝对是执行操作 的好习惯。例如,您可能有一个数据服务(即数据访问层)接口,允许您执行读取和修改持久存储中数据的操作。但是,您可能有多个该数据服务的实现。一种实现可以保存到文件系统,另一种可以保存到 DBMS,另一种是用于单元测试的模拟等。
但是,在许多情况下,您不需要连接模型 classes。如果您使用贫乏的业务对象方法(与丰富的业务对象相反),那么模型 classes 通常应该只是数据的容器,或普通旧 CLR 对象 (POCO)。这意味着这些对象没有任何实际功能可言,它们不引用任何特殊库或 classes。我要放入 POCO 的唯一 "functionality" 是仅依赖于自身的一个。例如,如果您有一个具有 FirstName 和 LastName 属性 的 User 对象,您可以创建一个名为 FullName 的只读 属性,它 returns 是两者的串联。 POCO 不知道它们是如何填充的,因此可以在您的数据服务的任何实现中使用。
这应该是您使用贫乏的业务对象方法时的默认方向,但至少有一个例外,我可以想到您可能希望在何处连接您的模型。例如,您可能希望支持 SQLite 数据服务和 Realm (NoSQL) 数据服务。 Realm 对象恰好需要您的模型派生自 RealmObject。所以,如果你想在 SQLite 和 Realm 之间切换你的数据访问层,那么你必须像你正在做的那样连接你的模型。我只是以 Realm 为例,但如果您想跨其他平台使用您的模型,这也适用,例如在 UWP 应用程序中创建一个可观察的基础 class。
确定是否应该为模型创建接口的关键试金石是问自己这个问题:
"Will I need to consume these models in various consumers and will those consumers require me to define a specific base class for my models to work properly in those consumers?"
如果这个问题的答案是"yes",那么你应该为你的模型制作接口。如果答案是 "no",那么创建模型接口是无关紧要的工作,您可以放弃它,让您的数据服务实现处理其底层数据存储的细节。
SQLite 问题
无论你是否继续使用模型接口,你仍然应该有一个 SQLite 的数据访问实现,它知道它正在处理特定于 SQLite 的模型,然后你可以直接在那些特定的实现上执行所有的 CRUD 操作你的模型。那么既然你指的是一个特定的模型实现,SQLite 应该照常工作。
类型兼容性
要回答你的最后一个问题,类型系统看不到这个...
List<IMonthlyBudget> MonthlyBudget
与此类型兼容...
List<MonthlyBudget> MonthlyBudget
在我们看来,如果我有一个苹果列表,那么它应该与一个水果列表类型兼容。编译器将苹果视为一种水果,而不是将苹果列表视为 类型的水果列表。所以你不能像这样在他们之间投射...
List<IMonthlyBudget> myMonthlyBudget = (List<IMonthlyBudget>) new List<MonthlyBudget>();
但是您可以像这样将 MonthlyBudget 对象添加到 IMonthlyBudget 对象列表中...
List<IMonthlyBudget> myMonthlyBudget = new List<IMonthlyBudget>();
myMonthlyBudget.Add(new MonthlyBudget());
如果您想一次转换整个列表,也可以使用 LINQ .Cast() 方法。
这背后的原因与类型差异有关。这里有一篇很好的文章可以阐明原因:
希望对您有所帮助! :-)