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

NH-3693 - AliasToBeanResultTransformer fails under Firebird #597

Merged
merged 3 commits into from
Apr 23, 2017

Conversation

fredericDelaporte
Copy link
Member

@fredericDelaporte fredericDelaporte commented Apr 12, 2017

NH-3693 - AliasToBeanResultTransformer fails under Firebird, fix and test cases

This is based on #330, rebased, squashed in one commit for original work of @amroel, another one for the "done at the same time" fix and test cases for NH-1904 (trouble with struct having properties with same name but different case), and a last one for my proposed implementation of setter resolver (with test case for the ambiguous match case).

{
if (_propertiesByNameCaseSensitive.TryGetValue(alias, out var property))
{
checkMember(property);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On case sensitive case, we should never have an ambiguous case, but better be on the safe side.

properties.AddRange(
currentType
.GetProperties(bindingFlags)
.Where(p => p.CanWrite)
Copy link
Member

@hazzik hazzik Apr 12, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need to check p.GetIndexParameters().Length ==0 as well

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. I have done some cleanup by the way.

.SetResultTransformer(transformer)
.List<PublicInheritedPropertiesSimpleDTO>();
Assert.That(l.Count, Is.EqualTo(2));
Assert.That(l, Has.All.Not.Null);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you also check that all properties get the desired values?

if (member.AmbiguousMembers == null || member.AmbiguousMembers.Length < 2)
throw new InvalidOperationException($"{nameof(NamedMember<T>.Member)} missing and {nameof(NamedMember<T>.AmbiguousMembers)} invalid.");

throw new AmbiguousMatchException(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we need tests for these exceptional cases

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about the ToPropertiesInsensitivelyDuplicated_WithoutAnyProjections test I have added with the third commit?

.GroupBy(m => m.Member.Name,
(k, g) =>
new NamedMember<T>(k,
g
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need to have a test for this behaviour

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, pushed, tests heavily rewritten.

@fredericDelaporte
Copy link
Member Author

Attempt to fix the new cases for the 3 failing DB. Guessing they yield full lower case aliases instead of uppercase as I was believing.
And added some more xml doc comments.

@hazzik
Copy link
Member

hazzik commented Apr 15, 2017

@fredericDelaporte different tests fail on different DBs

@hazzik
Copy link
Member

hazzik commented Apr 15, 2017

It seems that Oracle and Firebird return upper case, and PostgreSQL - lowercase

@fredericDelaporte
Copy link
Member Author

Yes I have seen there are troubles, I have already made some attempts but it looks like I will have to debug them with local installs.

@fredericDelaporte
Copy link
Member Author

Two unrelated files were added in my previous commit amendment, I have removed them.

Unless told otherwise, I intend to merge this PR in the days to come then close #330.

@hazzik hazzik self-requested a review April 20, 2017 21:41
/// sorts them by inheritance depth then by visibility from public to private, and takes those ranking first.
/// </summary>
[Serializable]
public class QueryAliasToObjectPropertySetter
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this please be internal?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or, actually inlined (not just make a nested class, but actually inlined) into the AliasToBeanTransformer


return new QueryAliasToObjectPropertySetter(fieldsByNameCaseSensitive, fieldsByNameCaseInsensitive, propertiesByNameCaseSensitive, propertiesByNameCaseInsensitive);

int getFieldVisibilityRank(FieldInfo field)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ReSharper suggests that these shall be named as usual method.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PascalCase? Microsoft seems to mandate camelCase instead, for exhibiting their local nature.

var currentType = objType;
var rank = 1;
// For grasping private members, we need to manually walk the hierarchy.
while (currentType != null && currentType != typeof(object))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you move the while loop to a separate method?

@hazzik
Copy link
Member

hazzik commented Apr 20, 2017

To be honest I'm not a big fun of local functions, and would like to avoid them until necessary.

/// if no matching property or field was found, retry with a case insensitive match. For members having the same name, it
/// sorts them by inheritance depth then by visibility from public to private, and takes those ranking first.
/// </summary>
[Serializable]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this class is Serializable we need to implement IDeserializationCallback because we have IDictionary<,> fields. Or we need to exclude these fields non serialized and add a deserialization constructor for a class to repopulate these fields

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe better simply store the concrete implementation which is serializable.

@fredericDelaporte
Copy link
Member Author

To be honest I'm not a big fun of local functions, and would like to avoid them until necessary.

I find them handy for avoiding bloating a class with helper functions useful only for a single method. But well, we have cope without them for many years and can go on without them.

@fredericDelaporte
Copy link
Member Author

Bunch of changes done.

@@ -29,37 +31,46 @@ namespace NHibernate.Transform
/// sorts them by inheritance depth then by visibility from public to private, and takes those ranking first.
/// </remarks>
[Serializable]
public class AliasToBeanResultTransformer : AliasedTupleSubsetResultTransformer
public class AliasToBeanResultTransformer : AliasedTupleSubsetResultTransformer, IEquatable<AliasToBeanResultTransformer>
Copy link
Member

@hazzik hazzik Apr 22, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still need IDeserializationCallback. Or [NonSerialized] + deserialization constructor.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will add a serialization/deserialization test. Your link on SO seems unrelated: the trouble was attempting to use the map during the deserialization process. The OP states that the map was correclty filled after, but he needed it during.

@fredericDelaporte
Copy link
Member Author

Serialization/deserialization test added, no troubles.

Now if we consider serialization should be spatially optimized (consumed memory) rather than temporally (execution time), flagging dictionaries as non serializable and reconstructing them after deserialization would be better. (I do not tend to bother with those considerations when I do not actually understand the serialization use case for the application: hard to know which optimization to favor in such case.)

Copy link
Member

@hazzik hazzik left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

@hazzik hazzik added this to the 5.0.0 milestone Apr 23, 2017
@fredericDelaporte
Copy link
Member Author

Rebased and last three commits squashed in one.

@fredericDelaporte fredericDelaporte merged commit 2d2095b into nhibernate:master Apr 23, 2017
@fredericDelaporte fredericDelaporte deleted the NH-3693 branch April 23, 2017 09:34
@fredericDelaporte
Copy link
Member Author

Thanks for the reviews.

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

Successfully merging this pull request may close these issues.

None yet

3 participants