Improve discovery of inverse relationship

Issue #17 new
Guillermo Gutiérrez created an issue

In order to improve discovery of recursive inverse relationships, we can take into account the type of relationship that it represents.

For example, a OneToMany relationship should always have a ManyToOne relationship. In order to help inverse relationships in OneToOne and ManyToMany we can also take into account the property name.

Comments (4)

  1. Guillermo Gutiérrez reporter

    This sample code reproduces the issue:

    public class Employee
    {
        [PrimaryKey, AutoIncrement]
        public int Id { get; set; }
    
        public string Name { get; set; }
    
        [OneToMany]
        public List<Employee> Subordinates { get; set; }
    
        [ManyToOne]
        public Employee Supervisor { get; set; }
    
        [ForeignKey(typeof(Employee))]
        public int SupervisorId { get; set; }
    }
    
    [Test]
    public void TestIssue() {
        var conn = Utils.CreateConnection();
        conn.DropTable<Employee>();
        conn.CreateTable<Employee>();
    
        var employee1 = new Employee { 
            Name = "Albert" 
        };
        conn.Insert(employee1);
    
        var employee2 = new Employee {
            Name = "Leonardo",
            SupervisorId = employee1.Id
        };
        conn.Insert(employee2);
    
        var result = conn.GetWithChildren<Employee>(employee1.Id); // This line crashes
    }
    

    And to make it work currently we have to manually specify the inverse property like this:

    public class Employee
    {
        [PrimaryKey, AutoIncrement]
        public int Id { get; set; }
    
        public string Name { get; set; }
    
        [OneToMany(inverseProperty: "Supervisor")]
        public List<Employee> Subordinates { get; set; }
    
        [ManyToOne(inverseProperty: "Subordinates")]
        public Employee Supervisor { get; set; }
    
        [ForeignKey(typeof(Employee))]
        public int SupervisorId { get; set; }
    }
    
  2. Keith Roney

    Hi Guillermo,

    How would you do this for a recursive many-to-many relationship?

    As an example:

      public class Employee
      {
        [PrimaryKey, AutoIncrement]
        public int Id { get; set; }
    
        public string Name { get; set; }
    
        [ManyToMany(typeof(EmployeeFriends))]
        public List<Employee> Friends { get; set; }
      }
    
      public class EmployeeFriends
      {
        [ForeignKey (typeof(Employee))]
        public int EmployeeId { get; set; }
    
        [ForeignKey (typeof(Employee))]
        public int FriendId { get; set; }
      }
    

    Thanks

    Keith

  3. Guillermo Gutiérrez reporter

    That's pretty much the same sample that I'm using in the recursive write and recursive read tests:

    public class TwitterUser {
        [PrimaryKey, AutoIncrement]
        public int Id { get; set; }
    
        public string Name { get; set; }
    
        [ManyToMany(typeof(FollowerLeaderRelationshipTable), "LeaderId", "Followers",
            CascadeOperations = CascadeOperation.All)]
        public List<TwitterUser> FollowingUsers { get; set; }
    
        // ReadOnly is required because we're not specifying the followers manually, but want to obtain them from database
        [ManyToMany(typeof(FollowerLeaderRelationshipTable), "FollowerId", "FollowingUsers",
            CascadeOperations = CascadeOperation.CascadeRead, ReadOnly = true)]
        public List<TwitterUser> Followers { get; set; }
    }
    
    // Intermediate class, not used directly anywhere in the code, only in ManyToMany attributes and table creation
    public class FollowerLeaderRelationshipTable {
        public int LeaderId { get; set; }
        public int FollowerId { get; set; }
    }
    

    You have to explicitly specify both the inverse relationship and the foreign key. So your sample would be like this:

    public class Employee
    {
        [PrimaryKey, AutoIncrement]
        public int Id { get; set; }
    
        public string Name { get; set; }
    
        [ManyToMany(typeof(EmployeeFriends), "EmployeeId", "IsFriendOf")]
        public List<Employee> Friends { get; set; }
    
        [ManyToMany(typeof(EmployeeFriends), "FriendId", "Friends")]
        public List<Employee> IsFriendOf { get; set; }
    }
    
    public class EmployeeFriends
    {
        public int EmployeeId { get; set; }
        public int FriendId { get; set; }
    }
    
  4. Log in to comment