Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Non-trivial construction #28

Closed
mgravell opened this issue Aug 3, 2023 · 0 comments · Fixed by #30
Closed

Non-trivial construction #28

mgravell opened this issue Aug 3, 2023 · 0 comments · Fixed by #30
Assignees

Comments

@mgravell
Copy link
Member

mgravell commented Aug 3, 2023

Right now, the materializer is akin to:

TheType obj = new();
while (...)
{
    switch (...)
    {
        case 4223423:
            obj.Foo = /* read the value */
            break;
    }
}
return obj;

This only works for trivial construction; it does not support more complex scenarios:

  1. custom constructors
  2. init-only properties
  3. read-only fields (deferred for now, as this also requires materializer colocation)

A custom constructor (at most one per type) is identified by:

  • ignore parameterless constructors and constructors that are marked [DapperAot(false)]
  • if only a single constructor remains, it is selected whether or not it is marked [DapperAot]/[DapperAot(true)]
  • if multiple constructors remain, and multiple are marked [DapperAot]/[DapperAot(true)], a generator error is emitted and no constructor is selected
  • if multiple constructors remain, and exactly one is marked [DapperAot]/[DapperAot(true)], it is selected
  • in all other cases, no constructor is selected

If a custom constructor is selected, the parameters are checked against the member list using normalized / insensitive rules; if any paired members do not match field type (question: nullability?), a generator error is emitted and the constructor is deselected


Init-only properties are any non-static properties that have the init modifier, for example public string Name { get; init; }


In any of these scenarios, we must collect the values into locals, and defer construction; I would propose simply value0, value1, etc where the index is the position of the discovered member; each should be assigned default, awaiting values from the database; we then collect fields into these variables instead of the object:

int value0 = default;
string? value1 = default;
DateTime value2 = default;

while (...)
{
    switch (...)
    {
        case 4223423:
            value1 = /* read the value */
            break;
    }
}
// construction not shown yet

The construction step depends on whether a custom constructor is selected, and whether the parameters for such custom constructor covers all members; there are 3 possible outcomes:

  1. custom constructor covers all members
return new TheType(value0, value2, value1);

(noting that the parameter order does not necessarily match our declaration order, so it is not necessarily strict order)

  1. custom constructor covers some but not all members (which may or may not include init-only properties)
return new TheType(value0, value2)
{
    Foo = value1,
};
  1. no custom constructor (and which by elimination: must include at least one init-only property)
return new TheType
{
    Foo = value1,
};

Implementation notes:

DRY:

  • ideally the 3 non-trivial construction techniques (with/without constructor, with/without additional members) should not use 3 separate paths (it is fine to keep the original simple construction TheType obj = new() line separate, for simplicity; see †)
  • ideally the field read loop should not be duplicated between simple/custom construction; it should just change the target of the read statement between obj.TheMember = vs value42 =

†: the 4th quadrant in that with/without matrix is: simple construction, so: already handled - i.e. without constructor, everything is additional members, none of which are init-only - the difference being that in that simple scenario, we're not using the stack to gather the values - we're pushing them directly onto the object

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants