jonwagner / Insight.Database

Fast, lightweight .NET micro-ORM
Other
859 stars 145 forks source link

Better Support for OneToOne mappings with repeated child classes #384

Closed jonwagner closed 4 years ago

jonwagner commented 6 years ago

It would be great to support use cases where subobjects are returned in a single record, and they are of the same class. Currently there are some limitations that make this difficult.

[From StackOverflow question: https://stackoverflow.com/questions/52859453/inisght-database-auto-repository-and-custom-column-mapping]

Some code to illustrate the use case:

        public class TestDataWithDuplicateChild
        {
            public TestDuplicateChild Child1;
            public TestDuplicateChild Child2;
        }

        public class TestDuplicateChild
        {
            public int Value;
        }

        public interface IReturnDuplicateChildren
        {
            [Sql("SELECT Value=1, Value=2")]
            [Recordset(0, typeof(TestDataWithDuplicateChild), typeof(TestDuplicateChild), typeof(TestDuplicateChild))]
            TestDataWithDuplicateChild GetDuplicateChildren();
        }

There are a few things we'll need to tackle:

  1. The built-in code to assemble objects into the result doesn't support repeated types. This can probably be fixed with changes to CreateGraphDeserializer.
  2. There needs to be a way to specify which repeated values go with which child. By default, we're selecting them in the order they appear in the class, but that is likely runtime-dependent.
  3. It would be nice if there is column mapping support that lets the right values make it into the right object.
buist-sharris commented 6 years ago

I was the original poster in Stack Exchange. I came up with a work around for now, but I'm not super fond of it. I modified my query output to return 3 record sets...

Select * From TestDataWithDuplicateChild
Select TestDataWithDuplicateChild.Id, TestDuplicateChild.* FROM TestDuplicateChild JOIN ON Child1
Select TestDataWithDuplicateChild.Id, TestDuplicateChild.* FROM TestDuplicateChild JOIN ON Child2

The used the RecordSet Attriubutes to insert into...

public interface IReturnDuplicateChildren
        {
            [Recordset(0, typeof(TestDataWithDuplicateChild))]
                        [Recordset(1, typeof(TestDuplicateChild), IsChild = true, Into="Child1")]
                        [Recordset(2, typeof(TestDuplicateChild), IsChild = true, Into="Child2")]
            TestDataWithDuplicateChild GetDuplicateChildren();
        }
jonwagner commented 5 years ago

v6.2.7 has some updates related to this. You can now allow duplicate child classes to be returned using the RecordsetAttribute or with other structure-defining mechanisms.

The mapper will map the sub-objects in property-name order. That is, object ChildA will always be read from the recordset before ChildB.

I'd like to be able to allow the order to be customized, but it wasn't an easy change to make. I'll leave this open for a while to see if we can solve that issue as well.

buist-sharris commented 5 years ago

I just tried to modify my project to use this and id doesn't appear to be working.

class Child {
    int Id {get;set;}
    string Name {get;set;}
}
class TestData{
    int Id {get;set;}
    int Child1Id {get; set;}
    int Child2Id {get;set;}
    Child Child1 {get; set;}
    Child Child2 {get; set;}
}
Select * From TestDataWithDuplicateChild
Select TestDataWithDuplicateChild.Id, TestDuplicateChild.* FROM TestDuplicateChild JOIN ON Child1
Select TestDataWithDuplicateChild.Id, TestDuplicateChild.* FROM TestDuplicateChild JOIN ON Child2
[RecordSet(typeof(TestData), typeof(Child), typeof(Child)]
TestData GetTestData(int id);

My recordset is Id=1, Child1Id=1, Child2Id=2, Id=1, Name="Child1", Id=2, Name="Child2"

But my object is... Id=1, Child1Id=1, Child2Id=2, Child1 = {Id=2, Name=Child2}, Child2 = {Id=1, Name=Child1}

Reordering the Child properties on TestData has no affect on it either.

Using 6.2.8

buist-sharris commented 5 years ago

The mapper will map the sub-objects in property-name order. That is, object ChildA will always be read from the recordset before ChildB.

Perhaps this is where my confusion is coming from now that I think of it. When you say "property-name order", do you mean alphabetically, and not in the order defined in the class? If so, then it's working correctly.

jonwagner commented 5 years ago

Correct. I chose "lexicographical order" because "declaration order" isn't guaranteed. I'm still working on adding options to override the binding, but it's proving difficult.

jonwagner commented 4 years ago

Closing my own issue. :)