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, "-"));
}
}
You are creating a list of
KeyValuePair
and thus getting the error thatKeyValuePair
has no primary key:Change it to a list of
Student
:If you need to store information in the relation, you should store it in
RegUser
class:Be aware that SQLite-Net Extensions currently doesn't support additional properties in intermediate tables and you'll have to fetch that information manually: