I am having some trouble with what seems to be a relatively simple cascading operation, but which I simply cannot get to work. I have four (simplified) classes:
class Class
{
uint? ID; //IDs are null when they are created, the database automatically generates IDs. IDs are never null when retrieved from Database.
Classroom Classroom;
List<ClassStudent> Students;
}
class Student
{
uint? ID; //IDs are null when they are created, the database automatically generates IDs. IDs are never null when retrieved from Database.
string Name;
Classroom Homeroom;
}
class Classroom
{
uint? ID; //IDs are null when they are created, the database automatically generates IDs. IDs are never null when retrieved from Database.
string FloorNumber;
string RoomNumber;
}
class ClassStudent
{
Class Class;
uint SeatNumber;
Student Student;
}
When I save a Class
, I want to be able to just type in personal and address information without worrying about whether these Student
s and Classroom
s already exist in the database, and I want the Cascade logic to automatically persist new Classroom
s and Student
s, and reference existing ones based not on their ID
s, but on their information.
For example, saving a Classroom
with FloorNumber: "69", RoomNumber: 420
while an identical Classroom
exists in the database should result in a reference to the Classroom.ID
instead of persisting a new Classroom
. Student
works the exact same way.
(Oh, and, of course this is a silly way to handle School information; the classes are stand-ins. I just find it easier to work with tangible examples rather than foo
s and bar
s)
However, I cannot get the Cascade to work properly.
The first thing I have tried is of course just to have my ClassMap
s' references Cascade.SaveUpdate()
, for example:
class ClassesMap: ClassMap<Class>
{
public ClassesMap()
{
Id(x => x.ID)
.GeneratedBy.Native();
References(x => x.Classroom)
.Cascade.SaveUpdate();
HasMany(x => x.ClassStudent)
.KeyColumn("ClassID")
.Cascade.All();
}
}
class ClassStudentMap: ClassMap<ClassStudent>
{
public ClassStudentMap()
{
CompositeId()
.KeyReference(x => x.Class, "ClassID")
.KeyProperty(x => x.SeatNumber)
References(x => x.Student)
.Cascade.SaveUpdate();
}
}
class StudentMap: ClassMap<Student>
{
public StudentMap()
{
Id(x => x.ID);
.GeneratedBy.Native();
Map(x => x.Name);
References(x => x.Homeroom)
.Cascade.SaveUpdate();
}
}
class ClassroomMap: ClassMap<Classroom>
{
public ClassroomMap()
{
Id(x => x.ID);
.GeneratedBy.Native();
Map(x => x.FloorNumber);
Map(x => x.RoomNumber);
}
}
The result of this is that there is no prevention of duplicate Classroom
s and Student
s; inserting a new Class
will also insert its Classroom
and Student
s, regardless of whether identical Classroom
s and Student
s already exist in the database.
So, I tried to declare the Student
s' and Classroom
s' information as Unique
class StudentMap: ClassMap<Student>
{
public StudentMap()
{
Id(x => x.ID);
.GeneratedBy.Native();
Map(x => x.Name).UniqueKey("UniqueStudent");
References(x => x.Homeroom)
.Cascade.SaveUpdate()
.UniqueKey("UniqueStudent");
}
}
class ClassroomMap: ClassMap<Classroom>
{
public ClassroomMap()
{
Id(x => x.ID);
.GeneratedBy.Native();
Map(x => x.FloorNumber)
.UniqueKey("UniqueClassroom");
Map(x => x.RoomNumber)
.UniqueKey("UniqueClassroom");
}
}
However, this resulted in Class
es being unable to save to the database, because it could not save its Student
s and Classroom
because they conflict with existing Unique
entries.
Third thing I tried was a custom Cascade Logic. I replaced the Cascade.SaveUpdate()
s with Cascade.None()
, and rewrote my code so I would GetOrCreate
each Classroom
, and then GetOrCreate
each Student
, and only then save the Class
Example of GetOrCreate
as applies to Classroom
(Student
has one just like it):
Classroom GetOrCreate(Classroom newClassroom)
{
Classroom? extant = GetFromDatabase(newClassroom);
if(extant != null)
return extant;
SaveToDatabase(newClassroom);
return GetFromDatabase(newClassroom);
}
But to no avail; now when I save a Class
it complains that it references a transient Classroom
(and if I skip the Classroom
, ClassStudent
references a transient Student
), and that I must persist it to the Database - despite the fact that the transient Classroom
it references was retrieved from the Database, and I therefor know it is persistent.
I need help figuring out what to do, please.