如何将外键映射到 SQLite-Net Extensions 中的对象字段?
How can a foreign key be mapped onto an object field in SQLite-Net Extensions?
上下文:
我们使用 SQLite-Net Extensions
和 Xamarin
进行本地数据缓存。我们计划部署到 iOS、Android 和 Windows Phone。我们在整个系统中使用了现有的数据结构(都实现了一个公共接口),我们希望以这种方式存储它们。
问题
如代码示例所示,[ManyToOne]
属性用于表示关系字段。这是行不通的。如 BitBucket Developer Page 所述,[ForeignKey]
属性可用于指定外键关系。这个貌似只支持一个int
。我们能否在不复制 Id 字段的属性的情况下轻松调整我们的结构以支持这些关系?例如以下是不可取的。
[ForeignKey(typeof(Address))]
public int AddressId { set; get; }
[ManyToOne]
public Address Address
{
set { address = value; }
get { return address; }
}
代码示例
using SQLite.Net.Attributes;
using SQLiteNetExtensions.Attributes;
namespace Data
{
[Table("Client")]
public class Client : IData
{
private int id = -1;
private Address address = null;
public Client() { }
public Client(int id)
{
this.id = id;
}
[PrimaryKey, AutoIncrement, Column("_id")]
public int Id
{
set { id = value; }
get { return id; }
}
[ManyToOne]
public Address Address
{
set { address = value; }
get { return address; }
}
}
[Table("Address")]
public class Address : IIdentifiable
{
private int id = -1;
private string someFields = "";
public Address() { }
public Address(int id)
{
this.id = id;
}
[PrimaryKey, AutoIncrement, Column("_id")]
public int Id
{
set { id = value; }
get { return id; }
}
public string SomeFields
{
set { someFields = value; }
get { return someFields; }
}
}
}
SQLite-Net Extensions 是 SQLite-Net 上的薄层,它使用 sqlite 数据库进行存储。关系型数据库使用外键存储关系,sqlite在这方面也不例外。因此,SQLite-Net 和 SQLite-Net Extensions 也使用了外键机制来声明关系。
作为替代方案,您可以使用中间表来存储关系,与 ManyToMany
关系的工作方式相同,但将其中一端限制为一端。这样您就可以使用 ManyToMany 关系和中间表来模拟 OneToMany
、ManyToOne
甚至 OneToOne
关系。例如:
[Table("Client")]
public class Client {
[PrimaryKey, AutoIncrement, Column("_id")]
public int Id { get; set; }
[Ignore] // This property shouldn't be persisted
public Address Address { get; set; }
// This relationship is in fact a ManyToOne relationship,
// but we model it as a ManyToMany to avoid adding foreign key to this entity
[ManyToMany(typeof(AddressesClients))]
public Address[] Addresses {
get { return Address != null ? new []{ Address } : Address; }
set { Address = value.FirstOrDefault(); }
}
}
[Table("Address")]
public class Address
{
[PrimaryKey, AutoIncrement, Column("_id")]
public int Id { get; set; }
public string SomeFields { get; set; }
[ManyToMany(typeof(AddressesClients), ReadOnly = true)]
public List<Client> Clients { get; set; }
}
// Intermediate table that defines the relationship between Address and Client
class AddressesClients {
[PrimaryKey, AutoIncrement]
public int Id { get; set; }
[ForeignKey(typeof(Client))]
public int ClientId { get; set; }
[ForeignKey(typeof(Address))]
public int AddressId { get; set; }
}
当然,这会带来一些性能损失。
至于 PrimaryKey
,您可以使用任何受支持的类型,并且您必须对相反的 ForeignKey
使用完全相同的类型,即如果您使用 Guid
作为主要类型键,指向到class的外键也必须是Guid
。在演示项目中,我们已经在使用 int
(性能最高)、string
甚至 UUID
.
上下文:
我们使用 SQLite-Net Extensions
和 Xamarin
进行本地数据缓存。我们计划部署到 iOS、Android 和 Windows Phone。我们在整个系统中使用了现有的数据结构(都实现了一个公共接口),我们希望以这种方式存储它们。
问题
如代码示例所示,[ManyToOne]
属性用于表示关系字段。这是行不通的。如 BitBucket Developer Page 所述,[ForeignKey]
属性可用于指定外键关系。这个貌似只支持一个int
。我们能否在不复制 Id 字段的属性的情况下轻松调整我们的结构以支持这些关系?例如以下是不可取的。
[ForeignKey(typeof(Address))]
public int AddressId { set; get; }
[ManyToOne]
public Address Address
{
set { address = value; }
get { return address; }
}
代码示例
using SQLite.Net.Attributes;
using SQLiteNetExtensions.Attributes;
namespace Data
{
[Table("Client")]
public class Client : IData
{
private int id = -1;
private Address address = null;
public Client() { }
public Client(int id)
{
this.id = id;
}
[PrimaryKey, AutoIncrement, Column("_id")]
public int Id
{
set { id = value; }
get { return id; }
}
[ManyToOne]
public Address Address
{
set { address = value; }
get { return address; }
}
}
[Table("Address")]
public class Address : IIdentifiable
{
private int id = -1;
private string someFields = "";
public Address() { }
public Address(int id)
{
this.id = id;
}
[PrimaryKey, AutoIncrement, Column("_id")]
public int Id
{
set { id = value; }
get { return id; }
}
public string SomeFields
{
set { someFields = value; }
get { return someFields; }
}
}
}
SQLite-Net Extensions 是 SQLite-Net 上的薄层,它使用 sqlite 数据库进行存储。关系型数据库使用外键存储关系,sqlite在这方面也不例外。因此,SQLite-Net 和 SQLite-Net Extensions 也使用了外键机制来声明关系。
作为替代方案,您可以使用中间表来存储关系,与 ManyToMany
关系的工作方式相同,但将其中一端限制为一端。这样您就可以使用 ManyToMany 关系和中间表来模拟 OneToMany
、ManyToOne
甚至 OneToOne
关系。例如:
[Table("Client")]
public class Client {
[PrimaryKey, AutoIncrement, Column("_id")]
public int Id { get; set; }
[Ignore] // This property shouldn't be persisted
public Address Address { get; set; }
// This relationship is in fact a ManyToOne relationship,
// but we model it as a ManyToMany to avoid adding foreign key to this entity
[ManyToMany(typeof(AddressesClients))]
public Address[] Addresses {
get { return Address != null ? new []{ Address } : Address; }
set { Address = value.FirstOrDefault(); }
}
}
[Table("Address")]
public class Address
{
[PrimaryKey, AutoIncrement, Column("_id")]
public int Id { get; set; }
public string SomeFields { get; set; }
[ManyToMany(typeof(AddressesClients), ReadOnly = true)]
public List<Client> Clients { get; set; }
}
// Intermediate table that defines the relationship between Address and Client
class AddressesClients {
[PrimaryKey, AutoIncrement]
public int Id { get; set; }
[ForeignKey(typeof(Client))]
public int ClientId { get; set; }
[ForeignKey(typeof(Address))]
public int AddressId { get; set; }
}
当然,这会带来一些性能损失。
至于 PrimaryKey
,您可以使用任何受支持的类型,并且您必须对相反的 ForeignKey
使用完全相同的类型,即如果您使用 Guid
作为主要类型键,指向到class的外键也必须是Guid
。在演示项目中,我们已经在使用 int
(性能最高)、string
甚至 UUID
.