简体   繁体   中英

How can a foreign key be mapped onto an object field in SQLite-Net Extensions?

Context: We are using SQLite-Net Extensions for local data caching with Xamarin . We plan on deploying to iOS, Android and Windows Phone. We have existing data structures used throughout our system (all implementing a common interface) that we would like to store in this manner.

Problem As shown in the code sample, the [ManyToOne] attribute is used to signify a relation field. This does not work. As described on the BitBucket Developer Page the [ForeignKey] attribute can be used to specify the foreign key relation. This seemingly only supports an int . Can we easily adapt our structure to support these relations without duplicating the properties for the Id field. eg the following is undesirable.

    [ForeignKey(typeof(Address))]
    public int AddressId { set; get; }

    [ManyToOne]
    public Address Address
    {
        set { address = value; }
        get { return address; }
    }

Code Sample

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 is a thin layer over SQLite-Net, and it uses sqlite database for the storage. Relational databases store relations using foreign keys, and sqlite is no different in this aspect. Therefore, SQLite-Net and SQLite-Net Extensions also uses the foreign key mechanism for declaring relationships.

As an alternative, you could use intermediate tables to store relationships, the same way ManyToMany relationships work, but limit one of the ends to one. This way you would mimic a OneToMany , ManyToOne or even OneToOne relationship using ManyToMany relationships and intermediate tables. For example:

[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; }
}

Of course, this would have some performance penalties.

As for the PrimaryKey , you can use any supported type, and you have to use the exact same type for the opposite ForeignKey , ie if you use Guid as primary key, the foreign key that points to that class must be a Guid too . In the demo project we're already using int (the most performant), string and even UUID .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM