使用SQLite

时间:2017-08-20 09:06:00

标签: c# sqlite xamarin software-design

我们说我有这样的界面:

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的真正实现是什么。我理解为什么,但我真的不想删除界面并将真正的实现暴露给使用这些模型的所有客户端。在我的项目结构中,我有一个具有所有模型接口的Core项目,模型实现在数据访问项目中。

我是如何解决这个问题的?我认为我不是第一个遇到这样的问题的人。保持模型接口(然后使用哪些存储库等作为返回类型,参数和类似的东西)并在数据访问项目中实现实际的具体模型,这是完全正常的做法吗?

有人可以解释为什么我不能这样做:

public class User : IUser
{
    public int Id { get; set; }

    public string Name { get; set; }

    public List<MonthlyBudget> MonthlyBudget { get; set; }
}   

MonthlyBudget实现了IMonthlyBudget,当具体模型实际实现接口时,不应该使用具体模型作为类型而不是接口吗?

1 个答案:

答案 0 :(得分:1)

这里有几个问题,所以我将其分解为几个部分:

使用界面

接口执行操作的类绝对是一种好习惯。例如,您可能拥有一个数据服务(即数据访问层)接口,该接口允许您执行操作以读取和修改持久性存储中的数据。但是,您可能有多个该数据服务的实现。一个实现可以保存到文件系统,另一个实现可以保存到DBMS,另一个是单元测试的模拟等等。

但是,在许多情况下,您不需要连接模型类。如果您正在使用贫乏的业务对象方法(而不是富业务对象),那么模型类通常应该只是数据容器或普通旧CLR对象(PO​​CO)。这意味着这些对象没有任何真正的功能可言,并且它们不引用任何特殊的库或类。我将放在POCO中的唯一“功能”是仅依赖于自身的功能。例如,如果您有一个具有FirstName和LastName属性的User对象,则可以创建一个名为FullName的只读属性,该属性返回两者的串联。  POCO对于如何填充它们是不可知的,因此可以用于您的数据服务的任何实现。

这应该是您使用贫血业务对象方法时的默认方向,但至少有一个例外我可以想到您可能想要在哪里与模型接口。您可能希望支持例如SQLite数据服务和Realm(NoSQL)数据服务。 Realm对象碰巧要求您的模型派生自RealmObject。因此,如果您想在SQLite和Realm之间切换数据访问层,那么您必须按照自己的方式连接模型。我只是以Realm为例,但如果您想在其他平台上使用模型,例如在UWP应用程序中创建可观察的基类,这也是正确的。

确定是否应该为模型创建界面的关键试金石测试是问自己这个问题:

“我是否需要在各种消费者中使用这些模型,那些消费者是否需要我为我的模型定义特定的基类才能在这些消费者中正常工作?”

如果对此的回答是“是”,那么您应该为模型创建接口。如果答案是“否”,则创建模型接口是无关紧要的工作,您可以放弃它,让您的数据服务实现处理其底层数据存储的细节。

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()方法。

这背后的原因与类型差异有关。这里有一篇很好的文章可以解释为什么:

Covariance and Contravariance

我希望有所帮助! : - )