Saved items are empty when retrieving at a later point

208 views Asked by At

I am using Xamarin and C# to make a cross-platform app. SQLite.Net Extensions are being used for the local database. The User table contains a list of registers, as seen below:

public class User
{
    [PrimaryKey, AutoIncrement]
    public int UserID { get; set; }
    public string firstName { get; set; }
    public string lastName { get; set; }
    public string title { get; set; }
    public string emailAddress { get; set; }
    public string password { get; set; }      
    //public ObservableCollection<Register> registerList { get; set; }

    [ManyToMany(typeof(RegStudent))]
    public ObservableCollection<Register> Registers { get; set; } 

    public User()
    {
        this.Registers = new ObservableCollection<Register>();
    }

    public void addRegisterAccess(Register register)
    {
        Registers.Add(register);
    }
}

When a register is made, it is added to the register table, then added to this list, done as below:

//Set the register to the updated version of this register that is stored on db, 
//as current register that has been created through user input does not have an ID.
register = database.db.GetRegisterByNameAndLocation(register.Name, register.Location);

//Add the register to the current user list of registers. Then fetch the new info and set the current user
// from the db.
database.db.GetUserByEmail(currentUser.user.emailAddress).addRegisterAccess(register);

When I try to access the list of registers, through the user object, it says the list does not contain any elements.

I have looked around for solutions and they seem to mention the SQLite UpdateWithChildren, but this method is not one that exists in my SQLite Connection or in my db. The list of registers is a ManyToMany relationship, how does the update fit into all of this?

**EDIT: ** I have now got the update method, but seem to be getting an exception when it is hit. Below is the method in my 'DatabaseManager' that adds a register to a user list:

public void addRegisterAccess(User user, Register register)
    {
        user.Registers.Add(register);
        _connection.InsertOrReplaceWithChildren(user, true);
    }

The exception below is given when a register is added, I thought the code to link the classes (using intermediate table) did not need to be called by user code?

    02-10 12:09:51.662 E/AndroidRuntime( 4461): Caused by: md52ce486a14f4bcd95899665e9d932190b.JavaProxyThrowable: SQLiteNetExtensions.Exceptions.IncorrectRelationshipException: User.Registers: ManyToMany relationship origin must have a foreign key defined in the intermediate type
02-10 12:09:51.662 E/AndroidRuntime( 4461):   at SQLiteNetExtensions.Extensions.WriteOperations.Assert (Boolean assertion, System.Type type, System.Reflection.PropertyInfo property, System.String message) <0xd84bb6e8 + 0x00087> in <filename unknown>:0 
02-10 12:09:51.662 E/AndroidRuntime( 4461):   at SQLiteNetExtensions.Extensions.WriteOperations.UpdateManyToManyForeignKeys (SQLite.Net.SQLiteConnection conn, System.Object element, System.Reflection.PropertyInfo relationshipProperty) <0xd84b9750 + 0x0026b> in <filename unknown>:0 
02-10 12:09:51.662 E/AndroidRuntime( 4461):   at SQLiteNetExtensions.Extensions.WriteOperations.UpdateInverseForeignKeys (SQLite.Net.SQLiteConnection conn, System.Object element) <0xd84b9578 + 0x0014f> in <filename unknown>:0 
02-10 12:09:51.662 E/AndroidRuntime( 4461):   at SQLiteNetExtensions.Extensions.WriteOperations.UpdateWithChildren (SQLite.Net.SQLiteConnection conn, System.Object element) <0xd84b8a68 + 0x0003b> in <filename unknown>:0 
02-10 12:09:51.662 E/AndroidRuntime( 4461):   at SQLiteNetExtensions.Extensions.WriteOperations.InsertWithChildrenRecursive (SQLite.Net.SQLiteConnection conn, System.Object element, Boolean replace, Boolean recursive, ISet`1 objectCache) <0xd84b76f8 + 0x000af> in <filename unknown>:0 
02-10 12:09:51.662 E/AndroidRuntime( 4461):   at SQLiteNetExtensions.Extensions.WriteOperations.InsertOrReplaceWithChildren (SQLite.Net.SQLiteConnection conn, System.Object element, Boolean recursive) <0xd84b76a8 + 0x0002f> in <filename unknown>:0 
02-10 12:09:51.662 E/AndroidRuntime( 4461):   at RegistrationApp.Data.DatabaseManager.addRegisterAccess (RegistrationApp.User user, RegistrationApp.Models.Register register) <0xd84b7578 + 0x0003f> in <filename unknown>:0 
02-10 12:09:51.662 E/AndroidRuntime( 4461):   at RegistrationApp.CreateRegisterPage+<OnCreateButtonClicked>d__1b.MoveNext () <0xd87ad788 + 0x006c3> in <filename unknown>:0 
02-10 12:09:51.662 E/AndroidRuntime( 4461): --- End of stack trace from previous location where exception was thrown ---

Just to add, my intermediate table class looks like this:

public class RegUser
    {
        [ForeignKey(typeof(User))]
        public int UserID { get; set; }

        [ForeignKey(typeof(Register))]
        public int RegisterID { get; set; }
    }

**EDIT: ** Below is the Register Class itself:

public class Register
    {
        [PrimaryKey, AutoIncrement]
        public int RegisterID { get; set; }
        public string Name { get; set; }
        public string Type { get; set; }
        public string Location { get; set; }
        public DateTime Date { get; set; }
        public string StartTime { get; set; }
        public string EndTime { get; set; }
        public Boolean Recurring { get; set; }

        [ManyToMany(typeof(RegStudent), "StudentID", "Registers")]
        public ObservableCollection<KeyValuePair<Student, string>> StudentList { get; set; }

        [ManyToMany(typeof(RegUser), "UserID", "Registers")]
        public ObservableCollection<User> UserList { get; set; }

        public Register()
        {
            this.StudentList = new ObservableCollection<KeyValuePair<Student, string>>();
            this.UserList = new ObservableCollection<User>();
        }

        //Adds a student to the Student List for this register, marking the string value as '-', to show the register has not been taken yet.
        //This value will be set to the status of the student, to show them as Present, AWOL, Sick, Late, etc.
        public void addStudent(Student student)
        {
            StudentList.Add(new KeyValuePair<Student, string>(student, "-"));
        }
    } 
1

There are 1 answers

4
redent84 On BEST ANSWER

You are creating a list of KeyValuePair and thus getting the error that KeyValuePair has no primary key:

[ManyToMany(typeof(RegStudent), "StudentID", "Registers")]
public ObservableCollection<KeyValuePair<Student, string>> StudentList { get; set; }

Change it to a list of Student:

[ManyToMany(typeof(RegStudent), "StudentID", "Registers")]
public ObservableCollection<Student> StudentList { get; set; }

If you need to store information in the relation, you should store it in RegUser class:

public class RegUser
{
    [ForeignKey(typeof(User))]
    public int UserID { get; set; }

    [ForeignKey(typeof(Register))]
    public int RegisterID { get; set; }

    public string Status { get; set; }
}

Be aware that SQLite-Net Extensions currently doesn't support additional properties in intermediate tables and you'll have to fetch that information manually:

var regUser = conn.Table<RegUser>.Where(ru => ru.UserID == user.ID && ru.RegisterID == register.ID).FirstOrDefault();
if (regUser != null) {
    var status = regUser.Status;
}