0

I am trying to use SQLite-Net Extensions to create a Relational Database. I'm running into an issue when trying to pull the Term object from the database. It successfully pulls over its associated courses, but not the courses associated assessments and notes. I'm not sure if the problem lies in how I insert the objects into the database, how I pull the objects from the database, or how I have the objects attributes listed.

I feel like the SQLite-Net Extensions documentation is extremely limited, so I'm not even sure what's going on. I've tried it many different ways, including adding CascadeOperations, but non of those seemed to help.

Here is the (simplified) code for my objects:

[Table("Terms")]
public class Term
{
    [PrimaryKey, AutoIncrement]
    public int ID { get; set; }
    public string Name { get; set; }
    [OneToMany]
    public List<Course> Courses { get; set; }

    public Term() { }
    public Term(string name, List<Course> courses)
    {
        Name = name;
        Courses = courses;
    }

Courses

[Table("Courses")]
public class Course
{
    [PrimaryKey, AutoIncrement]
    public int ID { get; set; }
    [ForeignKey(typeof(Term))]
    public int TermID { get; set; }
    public string Name { get; set; }
    [OneToMany]
    public List<Assessment> Assessments { get; set; }
    [OneToMany]
    public List<Note> Notes { get; set; }

    public Course() { }

    public Course(string name, List<Assessment> assessments, List<Note> notes)
    {
        Name = name;
        Assessments = assessments;
        Notes = notes;
    }
}

Assessments

[Table("Assessments")]
public class Assessment
{
    [PrimaryKey, AutoIncrement]
    public int ID { get; set; }
    [ForeignKey(typeof(Course))]
    public int CourseID { get; set; }
    public string Name { get; set; }

    public Assessment() { }
    public Assessment(string name)
    {
        Name = name;
    }
}

Notes

[Table("Notes")]
public class Note
{
    [PrimaryKey, AutoIncrement]
    public int ID { get; set; }
    [ForeignKey(typeof(Course))]
    public int CourseID { get; set; }
    public string Name { get; set; }
    public string Text { get; set; }

    public Note() { }
    public Note(string name, string note)
    {
        Name = name;
        Text = note;
    }
}

And here is the code for inserting and getting objects: Inserting

public bool SaveTermAsync(Term term)
    {
        if (term.ID != 0)
        {
            _database.UpdateWithChildrenAsync(term);
            return true;
        }
        else
        {
            foreach (var course in term.Courses)
            {
                foreach (var assessment in course.Assessments)
                {
                    _database.InsertAsync(assessment);
                }
                foreach (var note in course.Notes)
                {
                    _database.InsertAsync(note);
                }
                _database.InsertAsync(course);
            }
            _database.InsertAsync(term);
            _database.UpdateWithChildrenAsync(term);
            return false;
        }
    }

Getting

public Task<List<Term>> GetTermsAsync()
    {
        return _database.GetAllWithChildrenAsync<Term>();
    }

I know it's a bit of a code dump, but I have no idea where or what could be going wrong. If anyone could give any information about what is potentially going wrong, that would be awesome. Perhaps I'm simply expecting something to happen that isn't actually how it works. I don't know.

Also, if anyone has any links to some better documentation than https://bitbucket.org/twincoders/sqlite-net-extensions/src/master/ that would be awesome

EDIT

I tried using Cascading Options as well, CascadeRead, CascadeInsert, and CascadeAll. Using CascadeInsert or CascadeAll with _database.InsertWithChildrenAsync(term, true) resulted in a crash. The crash does not provide any error messages, and even wrapping the InsertWithChildren with a try catch block didn't work. Removing the recursive bool caused the program not to crash, and actually get the closest to what I'm looking for. Assessments and Notes are no longer null, but are still empty. Here's my updated code:

Saving and Getting:

public async Task<List<Term>> GetTermsAsync()
    {
        return await _database.GetAllWithChildrenAsync<Term>(recursive: true);
    }

public async void SaveTermAsync(Term term)
    {
        if (term.ID != 0)
        {
            await _database.UpdateWithChildrenAsync(term);
        }
        else
        {
            //Trying this with recursion results in crash
            await _database.InsertWithChildrenAsync(term);
        }
    }

One-To-Many Relationships:

//In Term
[OneToMany(CascadeOperations = CascadeOperation.All)]
public List<Course> Courses { get; set; }

//In Courses
[OneToMany(CascadeOperations = CascadeOperation.All)]
public List<Assessment> Assessments { get; set; }
[OneToMany(CascadeOperations = CascadeOperation.All)]
public List<Note> Notes { get; set; }

Also, I forgot to include last time how I'm populating the tables in the first place.

public bool CreateTables()
    {
        _database.CreateTableAsync<Term>().Wait();
        _database.CreateTableAsync<Course>().Wait();
        _database.CreateTableAsync<Assessment>().Wait();
        _database.CreateTableAsync<Note>().Wait();
        return true;
    }

    public Task<int> ClearTablesTest()
    {
        _database.DropTableAsync<Term>();
        _database.DropTableAsync<Course>();
        _database.DropTableAsync<Assessment>();
        return _database.DropTableAsync<Note>();
    }

async public Task<int> PopulateTestData()
    {
        await ClearTablesTest();
        CreateTables();

        Term term = new Term("Test Term", true, DateTime.Now, DateTime.Now.AddDays(10),
            new List<Course>
            {
                new Course("Test Course", CourseStatus.Completed, "Guys Name", "(999)-999-9999", "email@gmail.com", 6, DateTime.Now, DateTime.Now.AddDays(10),
                new List<Assessment>
                {
                    new Assessment("Test Assessment", AssessmentType.Objective, false, DateTime.Now, DateTime.Now.AddDays(10))
                },
                new List<Note>
                {
                    new Note("Test Note", "This is a test note.")
                })
            });
        App.Database.SaveTermAsync(term);
        return 0;
    }
Knight Steele
  • 169
  • 4
  • 14
  • I think if you just use Insert, you need to establish the relationships manually after the keys have been created. To avoid this, use InsertWithChildren. Have you actually examined the db tables to establish whether the relationships are being created correctly? – Jason Jan 21 '21 at 01:49
  • I've tried using InsertWithChildren, but it doesn't seem to change anything. When I call GetAllWithChildrenAsync to get the term, the course list gets returned, but the Assessments and notes are null, whether I do InsertWithChildren or not. As for examining the database tables, I'm not sure what you mean. Are there methods to check if a relationship exists? – Knight Steele Jan 21 '21 at 01:56
  • there are a variety of SQLite management tools you can use to examine your db. Look at the tables and verify that the FK and PK ids are set in the data like you expect them to be. – Jason Jan 21 '21 at 02:00
  • @Jason Do you by chance have any examples of those SQLite management tools? – Knight Steele Jan 21 '21 at 02:17
  • https://www.google.com/search?q=sqlite+management+tools – Jason Jan 21 '21 at 02:22
  • As others have said, your foreign keys are probably not being set correctly, at least for the `Assessment` and `Notes` entities for sure. In addition to the documentation you have a lot of working test cases that make use of all the functionality. You may find this particular example very useful: https://bitbucket.org/twincoders/sqlite-net-extensions/src/master/Tests/IntegrationTests/Tests/RecursiveWriteAsyncTests.cs – redent84 Jan 21 '21 at 09:10
  • @redent84 I seriously have no idea what I'm missing here. As far as I can tell, the Assessments and Notes classes are constructed correctly. No matter what I seem to do, Assessment and Notes get returned as null. [link](https://stackoverflow.com/questions/44820237/sqlite-retrieve-child-element-from-object-in-list) This question had a structure closest to mine, but following some of the things he did, still didn't work. Having CascadeRead and recursion true on everything actually causes my app to crash. – Knight Steele Jan 22 '21 at 02:19
  • @KnightSteele can you update your sample code and provide the error that you're getting? – redent84 Jan 22 '21 at 13:21
  • @redent84 I added some more code and clarifications on what occurred with CascadeOperations. I feel like I'm really close to getting it working now, but still can't quite figure out why it's not working. As I mention in the edit, I'm now able to pull Assessments and Notes, but unfortunately they are empty. At least they are no longer null. – Knight Steele Jan 22 '21 at 22:52
  • The list is empty because you're not saving them to database in the first place. If you're unable to use recursive operations, you have to insert (and update) them into database manually. – redent84 Jan 23 '21 at 14:57
  • @redent84 Do you know why the recursion would be crashing? It seems like my classes are set up just fine. I switched to the none async version just to see if it would work. It still doesn't, but instead of crashing on the recursive insert, it is now crashing on the recursive getwithchildren. Insert is now working just fine. – Knight Steele Jan 25 '21 at 02:00
  • If you don't attach the crash log it's impossible to know why. You can start working with the sample code and see what makes it crash from that onwards. – redent84 Jan 25 '21 at 10:32

1 Answers1

0

I finally figured out what was causing the crash as well as causing general confusion within SQLite-Net Extensions.

In my Assessment class, the property

public string BackgroundColor
    {
        get { return IsComplete ? "#558f45" : "Gray"; }
        set { BackgroundColor = value; }
    }

was causing the crash when recursion was used. I've been scouring the web for over two weeks looking for solutions to this issue, but haven't found anything similar to this. I submitted a bug report on the SQLite-Net Extensions bitbucket.

If anyone knows why this specific line would cause issues, I'd love to hear your input. Until then I'm going to mark this question as answered and continue work on my app.

Thanks @redent84 for your help thus far on this issue.

Knight Steele
  • 169
  • 4
  • 14