Расширения SQLite.net загружают один и тот же объект несколько раз, а не возвращают одну и ту же ссылку.

Я использую версию SQLite.Net Extensions для PCL в универсальном приложении Windows 10. Это мой первый раз, когда я использую его. В целом кажется, что он работает, но он загружает объект несколько раз, а не повторно использует ссылку на один и тот же объект.

Согласно документации расширений SQLite.Net:

Расширения SQLite-Net гарантируют, что любой объект загружается из базы данных только один раз, и будут разрешать циклические зависимости и обратные отношения, сохраняя при этом интегральную ссылку. Это означает, что любой возвращенный объект того же класса с тем же идентификатором будет ссылкой на точно такой же объект.

Кажется, это не происходит со мной. Вот мой код:

public class Group {
    [PrimaryKey, AutoIncrement]
    public Guid Id { get; set; }

    public string GroupName { get; set; }

    public override string ToString() {
        return string.Format("Group [ID: {0}, HashCode: {1}] GroupName={2}", Id.ToString().Last(4), GetHashCode(), GroupName);
    }

    [ManyToMany(typeof(GroupMember), CascadeOperations = CascadeOperation.CascadeRead)]
    public List<Member> Members { get; set; }

    public void DebugIt() {
        Debug.WriteLine(this);
        foreach (var member in Members) Debug.WriteLine("    " + member);
    }
}

public class Member {
    [PrimaryKey, AutoIncrement]
    public Guid Id { get; set; }

    public string Name { get; set; }

    public override string ToString() {
        return string.Format("Member [ID: {0}, HashCode: {1}] Name={2}", Id.ToString().Last(4), GetHashCode(), Name);
    }

    [ManyToMany(typeof (GroupMember), CascadeOperations = CascadeOperation.CascadeRead)]
    public List<Group> Groups { get; set; }

    public void DebugIt() {
        Debug.WriteLine(this);
        foreach (var group in Groups) Debug.WriteLine("    " + group);
    }
}

public class GroupMember {
    [PrimaryKey, AutoIncrement]
    public Guid Id { get; set; }
    [ForeignKey(typeof(Group))]
    public Guid GroupID { get; set; }
    [ForeignKey(typeof(Member))]
    public Guid MemberId { get; set; }
}

public class DatabaseGroups {
    private const string FileName = "db.sqlite";
    private SQLiteConnection _db;

    public async Task<bool> LoadAsync() {
        var exists = await FileHelper.DoesFileExistAsync(FileName);
        _db = new SQLiteConnection(new SQLitePlatformWinRT(), DatabaseFullPath,
            exists ? SQLiteOpenFlags.ReadWrite : SQLiteOpenFlags.Create | SQLiteOpenFlags.ReadWrite);
        if (!exists) InitializeWithDefaults();
        return await FileHelper.DoesFileExistAsync(FileName);
    }

    private void InitializeWithDefaults() {
        _db.CreateTable<Group>();
        _db.CreateTable<Member>();
        _db.CreateTable<GroupMember>();

        var group1 = new Group {GroupName = "Group 1"};
        var group2 = new Group {GroupName = "Group 2"};
        var member1 = new Member {Name = "Bob"};
        var member2 = new Member {Name = "Jane"};

        _db.Insert(group1);
        _db.Insert(group2);
        _db.Insert(member1);
        _db.Insert(member2);

        group1.Members = new List<Member> {member1, member2};
        _db.UpdateWithChildren(group1);

        group2.Members = new List<Member> {member1, member2};
        _db.UpdateWithChildren(group2);
    }

    private static StorageFolder DatabaseFolder {
        get { return ApplicationData.Current.LocalFolder; }
    }

    private static string DatabaseFullPath {
        get { return Path.Combine(DatabaseFolder.Path, FileName); }
    }

    public void DebugIt() {
        foreach (var groupId in _db.Table<Group>().Select(g => g.Id)) {
            var group = _db.GetWithChildren<Group>(groupId);
            group.DebugIt();
        }
        foreach (var memberId in _db.Table<Member>().Select(m => m.Id)) {
            var member = _db.GetWithChildren<Member>(memberId);
            member.DebugIt();
        }
    }
}

protected override async void OnLaunched(LaunchActivatedEventArgs e) {
    _db = new DatabaseGroups();
    await _db.LoadAsync();
    _db.DebugIt();

Когда он запускается, я создаю некоторые начальные данные. Затем я загружаю эти объекты с помощью GetWithChildren и отлаживаю их. Вот результаты:

Group[ID: 4858, HashCode: 51192825] GroupName = Group 1
    Member[ID: dbfa, HashCode: 64971671] Name = Jane
    Member[ID: b047, HashCode: 30776584] Name = Bob
Group[ID: 30f0, HashCode: 53439890] GroupName = Group 2
    Member[ID: dbfa, HashCode: 36062904] Name = Jane
    Member[ID: b047, HashCode: 9089598] Name = Bob
Member[ID: b047, HashCode: 20305449] Name = Bob
    Group[ID: 30f0, HashCode: 9648315] GroupName = Group 2
    Group[ID: 4858, HashCode: 29803642] GroupName = Group 1
Member[ID: dbfa, HashCode: 36899882] Name = Jane
    Group[ID: 30f0, HashCode: 23318221] GroupName = Group 2
    Group[ID: 4858, HashCode: 60865449] GroupName = Group 1

Как видите, объекты загружаются правильно, но ссылки на объекты для группы 1 (например) отличаются (см. хэш-код).

Возможно, я неправильно истолковываю способ, которым SQLite.Net Extensions обрабатывает ссылки на объекты? Возможно, он обрабатывает повторное использование ссылок на объекты в одном вызове GetWithChildren, но не при нескольких вызовах одного и того же SQLiteConnection?

Если это так, то как вы должны загружать более сложный граф объектов такими отношениями?


person David Cater    schedule 17.06.2015    source источник


Ответы (1)


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

Расширения SQLite-Net — это всего лишь тонкий слой над SQLite.Net, если для вас важна интегральная ссылка, вы можете вернуться к ручным запросам для более сложных операций.

Если у вас есть какие-либо предложения или запросы на вытягивание, они всегда приветствуются;)

person redent84    schedule 17.06.2015
comment
Я все еще разбираюсь в разработке приложений для Windows 10, но как только я доберусь до сути своего проекта, вероятно, мне понадобятся более продвинутые возможности ORM. Если EF 7.0 для универсальных приложений к тому времени не выйдет или его будет недостаточно для моих целей, я, вероятно, добавлю оболочку для кэширования сущностей вокруг SQLiteConnection в SQLite-Next Extensions и сделаю запрос на вытягивание. Спасибо за ответ. - person David Cater; 17.06.2015
comment
Боже, я люблю этот дивный новый мир открытого общения с разработчиками, от которого зависит моя жизнь. Спасибо, брицелам. Моя цель — быть готовым публиковать высококачественные приложения в Магазине Windows 10, когда он будет готов. Чем больше деталей встанет на свои места, чтобы это произошло, тем лучше. С нетерпением жду этого. - person David Cater; 18.06.2015