0

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, "-"));
        }
    } 
user2283597
  • 229
  • 2
  • 20
  • Try adding `using SQLiteNetExtensions.Extensions;` to make `UpdateWithChildren` method available. – redent84 Feb 10 '16 at 16:58
  • @redent84 Cannot believe I missed that. I have used the method but it is giving out an exception I haven't seen before. I have updated to question to show the code and exception. – user2283597 Feb 10 '16 at 17:21
  • @redent84 I was trying to use `InsertOrUpdateWithChildren` but tried to use `UpdateWithChildren` instead. I am now getting a different exception, one that says ` SQLiteNetExtensions.Exceptions.IncorrectRelationshipException: Register.StudentList: ManyToMany relationship destination must have Primary Key`... the only solution I could find online did not help to resolve this. Any idea where I should be looking to prevent the excpetion? Thanks for the help – user2283597 Feb 11 '16 at 14:47
  • Does your `Register` class have a `PrimaryKey`? – redent84 Feb 11 '16 at 16:21
  • @redent84 It does, it's a `[PrimaryKey, AutoIncrement]` `public int RegisterID { get; set; }`. – user2283597 Feb 11 '16 at 16:38
  • Try adding a `PrimaryKey` to `RegUser` – redent84 Feb 11 '16 at 16:43
  • @redent84 I have added a PrimaryKey (an auto incrementing one to `RegUser` and `RegStudent`, but I still get the exception saying `Register.StudentList: ManyToMany relationship destination must have Primary Key`, when trying to add a register to a user list. – user2283597 Feb 11 '16 at 16:59
  • Can you post your `Register` code? The issue is probably there – redent84 Feb 11 '16 at 18:01
  • @redent84 added the `Register` code to the question – user2283597 Feb 11 '16 at 19:07

1 Answers1

1

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;
}
redent84
  • 18,901
  • 4
  • 62
  • 85
  • Thanks for the answer, it fixes the exception problem completely. However, the original problem of lists being empty even though items have been added still exists. I have tried using `UpdateWithChildren(register)` and also tried `InsertOrReplaceWithChildren(register)`, at the time of creation of a student or register, but the lists still does not show anything. – user2283597 Feb 14 '16 at 15:45
  • I've had a further look at the way in which I am adding items to the database, it seems the items are added correctly, but the lists are always empty. Is there a specific way to add items to a `SQLiteNetExtensions` object list? – user2283597 Feb 17 '16 at 13:34
  • If they are added correctly to the database, maybe you are not retrieving them. To fetch relationships you have to use any of the `WithChildren` fetch methods or use `GetChildren` method later on the object. – redent84 Feb 17 '16 at 14:35
  • Ah, I was using the `Table` method from an example based on `SQLite.Net`, not the Extensions. The list is now being populated without any exceptions. Tanks – user2283597 Feb 18 '16 at 15:52