SQLite-Net Extensions  один к одному

Во втором коротком посте из серии SQLite-Net Extensions мы рассмотрим, как создавать отношения один-к-одному, используя этот крошечный ORM.

Это самый простой тип связи с базой данных. В качестве примера можно привести транспортное средство и свидетельство о регистрации — каждое транспортное средство имеет один и только одно свидетельство о регистрации, а одно свидетельство о регистрации связано с одним и только одним транспортным средством (за исключением некоторых чрезвычайных правовых норм в других странах, которые я не знаю 🙂).

Мы можем смоделировать это двумя способами:

  • как односторонние отношения — в этом случае только один из концов отношений знает о другом
  • как двусторонние (с инверсией) отношения — оба конца отношений знают друг о друге.

Один-к-одному без инверсии (в одну сторону)

Этот вид отношений выглядит следующим образом:

Мы используем его, когда предполагаем, что достаточно, чтобы Vehicle знал о RegistrationCertificate, но документ не обязательно должен знать, с каким автомобилем / мотором он связан (по крайней мере, напрямую).

Затем в коде мы создаем два класса моделей с использованием SQLite-Ne Extensionst:

   [Table("RegistrationCertificates")]
    public class RegistrationCertificate
    {
        [PrimaryKey, AutoIncrement]
        public int Id { get; set; }

        public string RegistrationNumber { get; set; }

        public string VIN { get; set; }

        public string OwnerData { get; set; }
    }
 [Table("Vehicles")]
    public class Vehicle
    {
        [PrimaryKey, AutoIncrement]
        public int Id { get; set; }

        public string Brand { get; set; }

        public DateTime ProductionDate { get; set; }

        public decimal EngineCapacity { get; set; }

        [ForeignKey(typeof(RegistrationCertificate))]
        public int RegistrationCertificateId { get; set; }

        [OneToOne]
        public RegistrationCertificate RegistrationCertificate { get; set; }
    }

Здесь интересен атрибут ForeignKeyAttribute, определенный в свойстве RegistrationCertificateId. Это — как говорит его имя — внешний ключ к первичному ключу связанной сущности (типа RegistrationCertificate).

Само свойство связанной сущности украшено OneToOneAttribute.

Нам больше ничего не нужно делать, чтобы смоделировать эти отношения. Мы уже можем использовать это:

  var db = new SQLiteConnection(new SQLitePlatformAndroid(), Constants.DbFilePath);
    db.CreateTable<Vehicle>();
    db.CreateTable<RegistrationCertificate>();

    var vehicle = new Vehicle
    {
        Brand = "Renault",
        EngineCapacity = 1.9m,
        ProductionDate = new DateTime(2001, 01, 01)
    };

    var certificate = new RegistrationCertificate
    {
        RegistrationNumber = "AB 12345",
        OwnerData = "Dawid Sibiński",
        VIN = "1312BS1312ASDSSVVW"
    };

    db.Insert(vehicle);
    db.Insert(certificate);

    vehicle.RegistrationCertificate = certificate;
    db.UpdateWithChildren(vehicle);

    var vehicleStored = db.GetWithChildren<Vehicle>(vehicle.Id);

Здесь нет ничего особенного, верно? Это выглядит очень похоже на то, что мы видели в предыдущем посте об отношениях «многие ко многим». Что нас интересует, так это то, что в конце, когда объект Vehicle извлекается из базы данных с помощью метода GetWithChildren, его свойство RegistrationCertificate также заполняется:

Один-к-одному с инверсией (в обе стороны)

Этот вид отношений моделирует себя, как показано ниже:

Что действительно изменилось на диаграмме по сравнению с отношением без инверсии, так это то, что теперь RegistrationCertificate имеет свойство типа Vehicle (в коде — ссылка на объект, связанный с транспортным средством, и внешний ключ).

В нашем случае это еще более «реально» — было бы неплохо, если бы Транспортное средство узнало о своем Регистрационном Сертификате, но также когда мы смотрим на сертификат, нам бы хотелось узнать, к какому автомобилю он принадлежит.

Теперь мы можем смоделировать это в коде. Единственное, что изменяется в модельных классах — это добавление ссылочного и внешнего ключа к Vehicle в модели RegistrationCertificate, поэтому я представляю только обновленный код этого класса:

[Table("RegistrationCertificates")]
    public class RegistrationCertificate
    {
        [PrimaryKey, AutoIncrement]
        public int Id { get; set; }

        public string RegistrationNumber { get; set; }

        public string VIN { get; set; }

        public string OwnerData { get; set; }

        // added to have Inversion relationship
        [ForeignKey(typeof(Vehicle))]
        public int VehicleId { get; set; }

        [OneToOne]
        public Vehicle Vehicle { get; set; }
    }

Что здесь круто, так это то, что нам не нужно изменять наш код для сохранения сущностей со связями в базе данных SQLite. Мы можем просто добавить следующую строку в конце:

var certificateStored = db.GetWithChildren<RegistrationCertificate>(certificate.Id);

и обратите внимание, что certificateStored уже содержит связанный с ним объект Vehicle:

Резюме

В сегодняшнем коротком посте мы увидели, как создать отношения «один к одному» между двумя объектами в базе данных SQLite, используя SQLite-Net Extensions ORM. Это очень просто и не требует использования Entity Framework writing или написания SQL-запросов непосредственно в нашем коде.

Мы рассмотрели два типа отношений один-к-одному: односторонние и двусторонние. Выбор между этими двумя зависит от использования и цели отношений, которые мы моделируем.

Про отношение «многие ко многим» вы можете прочитать в этом посте.

Я надеюсь, что вы найдете это полезным однажды 😉