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-3670 - Dynamic component should allow generic dictionary #305

Merged
merged 7 commits into from
Jun 7, 2018
Merged

NH-3670 - Dynamic component should allow generic dictionary #305

merged 7 commits into from
Jun 7, 2018

Conversation

rjperes
Copy link
Member

@rjperes rjperes commented Aug 20, 2014

https://nhibernate.jira.com/browse/NH-3670 (#755)

Created additional tests with generic properties for NH-1796, NH-1039, NH-3571 and NH-2664

@oskarb
Copy link
Member

oskarb commented Aug 20, 2014

Can you check if the reference documentation section on this would benefit from an update regarding this?

Should we deprecate the use of non-generic dictionary in this context and log an issue for NH5.0 to remove support for it?

@hazzik
Copy link
Member

hazzik commented Aug 26, 2014

@rjperes could you investigate the failure?

@rjperes
Copy link
Member Author

rjperes commented Aug 27, 2014

Will do

@hazzik
Copy link
Member

hazzik commented Aug 27, 2014

Also, please rebase on top of master as @oskarb pushed line endings changes.

@rjperes
Copy link
Member Author

rjperes commented Aug 31, 2014

Fixed problem. Need some help with the rebasing part
https://nhibernate.jira.com/browse/NH-3670

@oskarb
Copy link
Member

oskarb commented Sep 2, 2014

In git command line:
git checkout NH-3670
git rebase master

You will get conflicts.

I use TortoiseGit to resolve:

  1. Open TortoiseGit commit (DON'T commit).
  2. Double-click on each conflicted file. I find that the merge gui tool is at this point actually clever enough to automatically resolve conflicts that in fact were only line ending changes. So you may just need to click on Mark as resolved in the toolbar, or there may be residual conflicts to adjust manually.
  3. When you've resolved all conflicts cancel the TortoiseGit commit dialog.
  4. Use git status and git add, to make sure all modified files are scheduled for commit.
  5. Type git rebase --continue

If all is well you can now force push the new branch, which I believe should just replace the commits in this PR with your new rebased commits.

@oskarb oskarb changed the title NH-3670 NH-3670 Dynamic Component Should Allow Generic Dictionary Sep 2, 2014
@oskarb
Copy link
Member

oskarb commented Sep 2, 2014

Don't we have any tests outside of the NHSpecificTest directory as basic feature tests for dynamic components? There should be generic versions added to them.

@rjperes
Copy link
Member Author

rjperes commented Sep 2, 2014

Created unit tests for classes MappingByCode\MappersTests\DynamicComponentMapperTests, MappingByCode\MappersTests and MappingByCode\ExplicitMappingTests

@rjperes
Copy link
Member Author

rjperes commented Sep 2, 2014

BTW, git rebase told me that current branch is up to date... ?

@hazzik
Copy link
Member

hazzik commented Sep 21, 2014

BTW, git rebase told me that current branch is up to date... ?

Probably you did not do git fetch --all

PS: I rebased everything

@hazzik hazzik added this to the 4.1.0 milestone Sep 21, 2014
@hazzik hazzik changed the title NH-3670 Dynamic Component Should Allow Generic Dictionary NH-3670 - Dynamic Component Should Allow Generic Dictionary Apr 22, 2015
@hazzik hazzik modified the milestones: 4.2.0, 4.1.0 Aug 12, 2016
@hazzik hazzik modified the milestones: 4.2.0, 5.0.0 Dec 1, 2016
Copy link
Member

@fredericDelaporte fredericDelaporte left a comment

Choose a reason for hiding this comment

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

It looks to me as good modernization. Now there are some points to tackle/investigate.

src/NHibernate/Linq/Functions/DictionaryGenerator.cs Outdated Show resolved Hide resolved
src/NHibernate/Linq/NhRelinqQueryParser.cs Outdated Show resolved Hide resolved
@@ -9,21 +13,48 @@ namespace NHibernate.Tuple.Component
[Serializable]
public class DynamicMapComponentTuplizer : AbstractComponentTuplizer
{
private readonly Mapping.Component component;
Copy link
Member

Choose a reason for hiding this comment

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

Many non underscore prefixed fields in NHibernate seems to have their root in the Java port having kept Java naming convention. Shouldn't we adopt .Net convention for our own variables at least? (I personally tend to add the _ on code I port from Java.)

{
return typeof(IDictionary);
}
}
Copy link
Member

Choose a reason for hiding this comment

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

This does not look lightweight for a property getter. Maybe should this be initialized in constructor instead, or implemented with a private Lazy<System.Type>.

It looks overly complicated when we look at implementation of below DynamicMapInstantiator: typeof(IDictionary<string, object>).IsAssignableFrom(this.component.Type.ReturnedClass). What causes this simpler implementation not to be used here?

Since 5.0 is about modernization too, I think we should default to IDictionary<string, object> instead of defaulting to IDictionary. Of course this would require some changes on later files below.

Copy link
Member

@hazzik hazzik Jun 6, 2018

Choose a reason for hiding this comment

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

What causes this simpler implementation not to be used here?

Because the this.component.Type.ReturnedClass property calls this property:

/// <summary></summary>
public override System.Type ReturnedClass
{
get { return ComponentTuplizer.MappedClass; }
}

So, you cannot fetch the value of this property out of the void.

@@ -48,7 +50,14 @@ public object Instantiate()

protected virtual IDictionary GenerateMap()
{
return new Hashtable();
if ((this.component != null) && (typeof(IDictionary<string, object>).IsAssignableFrom(this.component.Type.ReturnedClass) == true))
Copy link
Member

Choose a reason for hiding this comment

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

As stated in another review, I do understand neither the added value of comparing to true, nor the unneeded parenthesis.

if (this.component != null && typeof(IDictionary<string, object>).IsAssignableFrom(this.component.Type.ReturnedClass)

Gets the job done and is more readable, isn't it?

Copy link
Member

Choose a reason for hiding this comment

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

It's just his code style, however this needs to be changed to align with our code style.

else
{
return new Hashtable();
}
Copy link
Member

Choose a reason for hiding this comment

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

If we want to default to strong typed dictionary, we should invert the null logic here:

if (this.component == null ||
	typeof(IDictionary<string, object>).IsAssignableFrom(this.component.Type.ReturnedClass)
{
	return new Dictionary<string, object>();
}
else
{
	return new Hashtable();
}

Copy link
Member

@hazzik hazzik Jun 6, 2018

Choose a reason for hiding this comment

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

We cannot - MapInstantiator is also used for EntityMode.Map and that was not migrated to generic dictionary.

@@ -65,7 +74,7 @@ public bool IsInstance(object obj)
}
else
{
return false;
return obj is IDictionary<string, object>;
}
Copy link
Member

Choose a reason for hiding this comment

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

If defaulting to strong typed dictionary, we would have to change the logic here too:

var that = obj as IDictionary<string, object>;
if (that != null)
{
	if (entityName == null)
	{
		return true;
	}
	string type = (string)that[KEY];
	return type == null || isInstanceEntityNames.Contains(type);
}
else
{
	return obj is IDictionary;
}

Copy link
Member

Choose a reason for hiding this comment

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

This piece code here only for EntityMode.Map instantiator.

@hazzik hazzik assigned hazzik and unassigned hazzik Jul 25, 2017
@hazzik hazzik modified the milestones: 5.0, 5.1 Sep 1, 2017
[Serializable]
internal class DynamicComponentInstantiator : IInstantiator
{
private readonly bool _isGeneric;
Copy link
Member

Choose a reason for hiding this comment

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

This is just for compatibility in case someone has declared a property holding the component as Hashtable instead of IDictionary. Otherwise Dictionary<string, object> when casted to IDictionary behaves the same as a Hashtable.

If we do not require/want this level of compatibility we can simplify DynamicComponentInstantiator to this:

[Serializable]
internal class DynamicComponentInstantiator : IInstantiator
{
	public object Instantiate(object id) => Instantiate();
	public object Instantiate() => new Dictionary<string, object>();
	public bool IsInstance(object obj) => obj is IDictionary;
}

Copy link
Member

@hazzik hazzik Jun 6, 2018

Choose a reason for hiding this comment

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

@rjperes, @fredericDelaporte your thoughts? I think this can be easily declare as possible breaking change.

Copy link
Member

Choose a reason for hiding this comment

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

A possible breaking change, yes it is, but still a grey area one. The documentation clearly states to map it as an IDictionary.

The <dynamic-component> element allows an IDictionary to be mapped as a component, where the property names refer to keys of the dictionary.

The fact it was a HashTable is an implementation detail on which the user should not rely:

Users may also plug in their own tuplizers. Perhaps you require that a System.Collections.IDictionary implementation other than System.Collections.Hashtable be used while in the dynamic-map entity-mode;

I think we can accept this possible breaking change in a minor release. (With a small update to above example in documentation.)

Copy link
Member

Choose a reason for hiding this comment

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

Users may also plug in their own tuplizers.

<dynamic-component> does not support custom tuplizers.

Copy link
Member

Choose a reason for hiding this comment

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

I have removed the _isGeneric check as it did not work in all the cases. It seems that some code depends on ComponentClass being null for <dynamic-component>

Copy link
Member

Choose a reason for hiding this comment

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

<dynamic-component> does not support custom tuplizers.

Ok, I did not realize this PR was leaving dynamic entity mode "as-is", using HashTable, and was just changing the dynamic component. This creates some kind of inconsistency, although that still remains internal implementation details.

Copy link
Member

@hazzik hazzik Jun 6, 2018

Choose a reason for hiding this comment

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

Yes, this PR's only about dynamic component.

else if (reflectedClass != null)
{
model.ComponentClass = reflectedClass;
model.IsEmbedded = false;
model.IsDynamic = componentMapping is HbmDynamicComponent;
}
Copy link
Member

Choose a reason for hiding this comment

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

Change to this file can be reverted if we do not care about "invalid" property declarations (see my comment).

@fredericDelaporte fredericDelaporte dismissed their stale review June 6, 2018 10:22

Old review with a bunch of false assumptions

@hazzik hazzik added t: Fix and removed needs work labels Jun 6, 2018
@hazzik hazzik modified the milestones: 6.0, 5.2 Jun 6, 2018
@hazzik hazzik changed the title NH-3670 - Dynamic Component Should Allow Generic Dictionary NH-3670 - Dynamic component should allow generic dictionary Jun 6, 2018
@hazzik
Copy link
Member

hazzik commented Jun 6, 2018

This is now ready for review.

Copy link
Member

@fredericDelaporte fredericDelaporte left a comment

Choose a reason for hiding this comment

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

So now we can map a dynamic component as an IDictionary<string, object>, but we can not yet query a dynamic entity as an IDictionary<string, object>. An additional PR for this would be preferable, for avoiding what could be seen as a discrepancy.

@@ -140,6 +140,14 @@ protected override void RegisterComponentMapping<TComponent>(Expression<Func<TEn
base.RegisterDynamicComponentMapping(property, mapping);
}

protected override void RegisterDynamicComponentMapping<TComponent>(Expression<Func<TEntity, IDictionary<string, object>>> property
, Action<IDynamicComponentMapper<TComponent>> mapping)
Copy link
Member

Choose a reason for hiding this comment

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

Formatting style oddness, NHibernate code normally keeps comma at end of lines rather than at start.

@hazzik
Copy link
Member

hazzik commented Jun 7, 2018

@fredericDelaporte I want to make dynamic component to support dynamic property, but this is for another PR as it requires much more work.

@hazzik
Copy link
Member

hazzik commented Jun 7, 2018 via email

@hazzik hazzik merged commit dc71c7d into nhibernate:master Jun 7, 2018
@hazzik hazzik added the r: Fixed label Jun 7, 2018
@hazzik
Copy link
Member

hazzik commented Jun 7, 2018

I just realised that some of the interfaces has changed. I'll make a follow-up PR to remove the breaking changes.

hazzik added a commit to hazzik/nhibernate-core that referenced this pull request Jun 7, 2018
hazzik added a commit to hazzik/nhibernate-core that referenced this pull request Jun 7, 2018
hazzik added a commit to hazzik/nhibernate-core that referenced this pull request Jun 8, 2018
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

4 participants