Skip to content

Commit d5c9844

Browse files
committed
Draft for intern string metadata
1 parent c020a40 commit d5c9844

File tree

10 files changed

+160
-43
lines changed

10 files changed

+160
-43
lines changed

src/NHibernate/Loader/BasicLoader.cs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,21 +28,29 @@ protected override sealed ICollectionAliases[] CollectionAliases
2828
protected abstract string[] Suffixes { get; }
2929
protected abstract string[] CollectionSuffixes { get; }
3030

31+
protected virtual bool InternStringMetadata => false;
32+
3133
protected override void PostInstantiate()
3234
{
3335
ILoadable[] persisters = EntityPersisters;
36+
if (InternStringMetadata)
37+
StringHelper.Intern(Suffixes, InternLevel.Default);
38+
3439
string[] suffixes = Suffixes;
3540
descriptors = new IEntityAliases[persisters.Length];
3641
for (int i = 0; i < descriptors.Length; i++)
3742
{
38-
descriptors[i] = new DefaultEntityAliases(persisters[i], suffixes[i]);
43+
descriptors[i] = new DefaultEntityAliases(persisters[i], suffixes[i], InternStringMetadata);
3944
}
4045

4146
ICollectionPersister[] collectionPersisters = CollectionPersisters;
4247
int bagCount = 0;
4348
if (collectionPersisters != null)
4449
{
4550
string[] collectionSuffixes = CollectionSuffixes;
51+
if (InternStringMetadata)
52+
StringHelper.Intern(CollectionSuffixes, InternLevel.Default);
53+
4654
collectionDescriptors = new ICollectionAliases[collectionPersisters.Length];
4755
for (int i = 0; i < collectionPersisters.Length; i++)
4856
{
@@ -99,4 +107,4 @@ public static string[] GenerateSuffixes(int seed, int length)
99107
return suffixes;
100108
}
101109
}
102-
}
110+
}

src/NHibernate/Loader/DefaultEntityAliases.cs

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System.Collections.Generic;
22
using NHibernate.Persister.Entity;
3+
using NHibernate.Util;
34

45
namespace NHibernate.Loader
56
{
@@ -34,6 +35,34 @@ public DefaultEntityAliases(IDictionary<string, string[]> userProvidedAliases, I
3435
//rowIdAlias is generated on demand in property
3536
}
3637

38+
/// <summary>
39+
/// Calculate and cache select-clause aliases.
40+
/// </summary>
41+
public DefaultEntityAliases(ILoadable persister, string suffix, bool internAliases):this(persister, suffix)
42+
{
43+
if (internAliases == false)
44+
return;
45+
46+
const InternLevel internLevel = InternLevel.SessionFactories;
47+
48+
SuffixedKeyAliases = StringHelper.Intern(SuffixedKeyAliases, internLevel);
49+
SuffixedPropertyAliases = InternPropertiesAliases(SuffixedPropertyAliases, internLevel);
50+
SuffixedVersionAliases = StringHelper.Intern(SuffixedVersionAliases, internLevel);
51+
SuffixedDiscriminatorAlias = StringHelper.Intern(SuffixedDiscriminatorAlias, internLevel);
52+
if (persister.HasRowId)
53+
RowIdAlias = StringHelper.Intern(RowIdAlias, internLevel);
54+
}
55+
56+
private string[][] InternPropertiesAliases(string[][] propertiesAliases, InternLevel internLevel)
57+
{
58+
foreach (string[] values in propertiesAliases)
59+
{
60+
StringHelper.Intern(values, internLevel);
61+
}
62+
63+
return propertiesAliases;
64+
}
65+
3766
/// <summary>
3867
/// Returns aliases for subclass persister
3968
/// </summary>
@@ -63,7 +92,11 @@ public string[][] GetSuffixedPropertyAliases(ILoadable persister)
6392
public string[] SuffixedKeyAliases { get; }
6493

6594
// TODO: not visible to the user!
66-
public string RowIdAlias => _rowIdAlias ?? (_rowIdAlias = Loadable.RowIdAlias + _suffix);
95+
public string RowIdAlias
96+
{
97+
get => _rowIdAlias ?? (_rowIdAlias = Loadable.RowIdAlias + _suffix);
98+
private set => _rowIdAlias = value;
99+
}
67100

68101
/// <summary>
69102
/// Returns default aliases for all the properties

src/NHibernate/Loader/Entity/EntityLoader.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,5 +48,7 @@ protected override bool IsSingleRowLoader
4848
{
4949
get { return !batchLoader; }
5050
}
51+
52+
protected override bool InternStringMetadata => true;
5153
}
5254
}

src/NHibernate/Mapping/Collection.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ public bool IsLazy
176176
public string Role
177177
{
178178
get { return role; }
179-
set { role = StringHelper.InternedIfPossible(value); }
179+
set { role = StringHelper.Intern(value, InternLevel.SessionFactories); }
180180
}
181181

182182
public IEnumerable<ISelectable> ColumnIterator

src/NHibernate/Mapping/Column.cs

Lines changed: 33 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ namespace NHibernate.Mapping
1313
[Serializable]
1414
public class Column : ISelectable, ICloneable
1515
{
16+
private const char QuoteChar = '`';
17+
1618
public const int DefaultLength = 255;
1719
public const int DefaultPrecision = 19;
1820
public const int DefaultScale = 2;
@@ -82,18 +84,21 @@ public string Name
8284
get { return _name; }
8385
set
8486
{
85-
if (value[0] == '`')
86-
{
87-
_quoted = true;
88-
_name = value.Substring(1, value.Length - 2);
89-
}
90-
else
91-
{
92-
_name = value;
93-
}
87+
_name = StringHelper.Intern(GetName(value, out _quoted), InternLevel.Default);
9488
}
9589
}
9690

91+
private string GetName(string value, out bool quoted)
92+
{
93+
quoted = false;
94+
if (value[0] == QuoteChar)
95+
{
96+
quoted = true;
97+
return value.Substring(1, value.Length - 2);
98+
}
99+
return value;
100+
}
101+
97102
public string CanonicalName
98103
{
99104
get { return _quoted ? _name : _name.ToLowerInvariant(); }
@@ -436,7 +441,7 @@ public SqlType GetSqlTypeCode(IMapping mapping)
436441
/// <summary>returns quoted name as it would be in the mapping file. </summary>
437442
public string GetQuotedName()
438443
{
439-
return _quoted ? '`' + _name + '`' : _name;
444+
return _quoted ? QuoteChar + _name + QuoteChar : _name;
440445
}
441446

442447
public bool IsCaracteristicsDefined()
@@ -463,24 +468,24 @@ public bool IsLengthDefined()
463468
/// <summary> Shallow copy, the value is not copied</summary>
464469
public object Clone()
465470
{
466-
Column copy = new Column();
467-
if (_length.HasValue)
468-
copy.Length = Length;
469-
if (_precision.HasValue)
470-
copy.Precision = Precision;
471-
if (_scale.HasValue)
472-
copy.Scale = Scale;
473-
copy.Value = _value;
474-
copy.TypeIndex = _typeIndex;
475-
copy.Name = GetQuotedName();
476-
copy.IsNullable = _nullable;
477-
copy.Unique = _unique;
478-
copy.SqlType = _sqlType;
479-
copy.SqlTypeCode = _sqlTypeCode;
480-
copy.UniqueInteger = UniqueInteger; //usually useless
481-
copy.CheckConstraint = _checkConstraint;
482-
copy.Comment = _comment;
483-
copy.DefaultValue = _defaultValue;
471+
Column copy = new Column()
472+
{
473+
_length = _length,
474+
_name = _name,
475+
_quoted = _quoted,
476+
_checkConstraint = _checkConstraint,
477+
_comment = _comment,
478+
_defaultValue = _defaultValue,
479+
_nullable = _nullable,
480+
_unique = _unique,
481+
_precision = _precision,
482+
_scale = _scale,
483+
_sqlType = _sqlType,
484+
_sqlTypeCode = _sqlTypeCode,
485+
_typeIndex = _typeIndex,
486+
_value = _value,
487+
UniqueInteger = UniqueInteger,
488+
};
484489
return copy;
485490
}
486491

src/NHibernate/Mapping/OneToMany.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using System.Collections.Generic;
33
using NHibernate.Engine;
44
using NHibernate.Type;
5+
using NHibernate.Util;
56

67
namespace NHibernate.Mapping
78
{
@@ -47,7 +48,7 @@ public int ColumnSpan
4748
public string ReferencedEntityName
4849
{
4950
get { return referencedEntityName; }
50-
set { referencedEntityName = value == null ? null : string.Intern(value); }
51+
set { referencedEntityName = StringHelper.Intern(value, InternLevel.Minimal); }
5152
}
5253

5354
public Table ReferencingTable
@@ -154,4 +155,4 @@ public bool[] ColumnUpdateability
154155
get { throw new InvalidOperationException(); }
155156
}
156157
}
157-
}
158+
}

src/NHibernate/Mapping/PersistentClass.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ public string ClassName
7070
get { return className; }
7171
set
7272
{
73-
className = value == null ? null : string.Intern(value);
73+
className = StringHelper.Intern(value, InternLevel.Minimal);
7474
mappedClass = null;
7575
}
7676
}
@@ -259,7 +259,7 @@ public virtual IEnumerable<Subclass> DirectSubclasses
259259
public virtual string EntityName
260260
{
261261
get { return entityName; }
262-
set { entityName = value == null ? null : String.Intern(value); }
262+
set { entityName = StringHelper.Intern(value, InternLevel.Minimal, checkPoolIfInternIsDisabled: true); }
263263
}
264264

265265
/// <summary>
@@ -519,7 +519,7 @@ public virtual IDictionary<string, string> FilterMap
519519
public string LoaderName
520520
{
521521
get { return loaderName; }
522-
set { loaderName = value == null ? null : string.Intern(value); }
522+
set { loaderName = value == null ? null : StringHelper.Intern(value, InternLevel.SessionFactories); }
523523
}
524524

525525
public virtual ISet<string> SynchronizedTables

src/NHibernate/Mapping/Property.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ public IEnumerable<ISelectable> ColumnIterator
6666
public string Name
6767
{
6868
get { return name; }
69-
set { name = value; }
69+
set { name = StringHelper.Intern(value, InternLevel.Default); }
7070
}
7171

7272
public bool IsComposite

src/NHibernate/Proxy/NHibernateProxyBuilder.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,9 @@ public NHibernateProxyBuilder(MethodInfo getIdentifierMethod, MethodInfo setIden
4545

4646
public TypeInfo CreateProxyType(System.Type baseType, IReadOnlyCollection<System.Type> baseInterfaces)
4747
{
48-
var typeName = $"{baseType.Name}Proxy";
49-
var assemblyName = $"{typeName}Assembly";
50-
var moduleName = $"{typeName}Module";
48+
var typeName = StringHelper.Intern($"{baseType.Name}Proxy", InternLevel.AppDomains);
49+
var assemblyName = StringHelper.Intern($"{typeName}Assembly", InternLevel.AppDomains);
50+
var moduleName = StringHelper.Intern($"{typeName}Module", InternLevel.AppDomains);
5151

5252
var name = new AssemblyName(assemblyName);
5353

@@ -117,11 +117,11 @@ private void CreateProxiedMethod(TypeBuilder typeBuilder, MethodInfo method, Fie
117117
{
118118
ImplementSetIdentifier(typeBuilder, method, lazyInitializerField);
119119
}
120-
else if (!_overridesEquals && method.Name == "Equals" && method.GetBaseDefinition() == typeof(object).GetMethod("Equals", new[] {typeof(object)}))
120+
else if (!_overridesEquals && method.Name == nameof(Equals) && method.GetBaseDefinition() == typeof(object).GetMethod(nameof(Equals), new[] {typeof(object)}))
121121
{
122122
//skip
123123
}
124-
else if (!_overridesEquals && method.Name == "GetHashCode" && method.GetBaseDefinition() == typeof(object).GetMethod("GetHashCode"))
124+
else if (!_overridesEquals && method.Name == nameof(GetHashCode) && method.GetBaseDefinition() == typeof(object).GetMethod(nameof(GetHashCode)))
125125
{
126126
//skip
127127
}

src/NHibernate/Util/StringHelper.cs

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -780,5 +780,73 @@ public static bool IsAnyNewLine(this string str, int index, out int newLineLengt
780780
newLineLength = 0;
781781
return false;
782782
}
783+
784+
public static InternLevel CurrentInternLevel { get; set; } = GetInternLevelFromConfig();
785+
786+
//TODO: Get inter level from app config
787+
private static InternLevel GetInternLevelFromConfig()
788+
{
789+
return InternLevel.Default;
790+
}
791+
792+
public static string[] Intern(string[] names, InternLevel internLevel, bool checkPoolIfInternIsDisabled = false)
793+
{
794+
if (names == null)
795+
return null;
796+
797+
for (var i = 0; i < names.Length; i++)
798+
{
799+
names[i] = Intern(names[i], internLevel, checkPoolIfInternIsDisabled);
800+
}
801+
return names;
802+
}
803+
804+
public static string Intern(string name, InternLevel internLevel, bool checkPoolIfInternIsDisabled = false)
805+
{
806+
if (name == null)
807+
return null;
808+
809+
if (name.Length == 0)
810+
return string.Empty;
811+
812+
if (internLevel <= CurrentInternLevel)
813+
return string.Intern(name);
814+
815+
return checkPoolIfInternIsDisabled
816+
? StringHelper.InternedIfPossible(name)
817+
: name;
818+
}
819+
}
820+
821+
public enum InternLevel
822+
{
823+
/// <summary>
824+
/// No interning
825+
/// </summary>
826+
Disabled,
827+
828+
/// <summary>
829+
/// Very close to old behavior (prior to NHibernate 5.1)
830+
/// Makes sure that metadata with full type name (like EntityName, ReferencedEntityName) are interned.
831+
/// </summary>
832+
Minimal,
833+
834+
/// <summary>
835+
/// More aggressive interning (additionally interns column names/ property names and some other strings that expected to be repeated in the single factory)
836+
/// </summary>
837+
Default,
838+
839+
/// <summary>
840+
/// Additionally interns metadata that's not repeated (or chance to repeat is considered low) inside single factory but can be shared across multiple factories.
841+
/// Useful when multiple session factories with the same configuration needs to be created
842+
/// </summary>
843+
SessionFactories,
844+
845+
/// <summary>
846+
/// Additionally interns static string metadata (like type names for static dynamic proxy and so on...)
847+
/// Useful in scenarios when Session Factories are created in multiple app domains
848+
/// (for instance multiple web-sites/web-services running on the same App pool)
849+
/// </summary>
850+
AppDomains,
783851
}
784852
}

0 commit comments

Comments
 (0)