Skip to content

Port Hibernate's BytecodeEnhancementMetadata #1946

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

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/NHibernate.Test/Async/NHSpecificTest/GH1439/Fixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ public async Task LazyPropertyShouldBeUninitializedAndLoadableAsync()
Is.False,
"Lazy property initialization status");
Assert.That(
FieldInterceptionHelper.IsInstrumented(e1),
e1 is IFieldInterceptorAccessor,
Is.True,
"Entity IsInstrumented");
Assert.That(
Expand All @@ -117,7 +117,7 @@ public async Task LazyPropertyShouldBeUninitializedAndLoadableWithComponentIdAsy
Is.False,
"Lazy property initialization status");
Assert.That(
FieldInterceptionHelper.IsInstrumented(e2),
e2 is IFieldInterceptorAccessor,
Is.True,
"Entity IsInstrumented");
Assert.That(
Expand Down
4 changes: 2 additions & 2 deletions src/NHibernate.Test/NHSpecificTest/GH1439/Fixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ public void LazyPropertyShouldBeUninitializedAndLoadable()
Is.False,
"Lazy property initialization status");
Assert.That(
FieldInterceptionHelper.IsInstrumented(e1),
e1 is IFieldInterceptorAccessor,
Is.True,
"Entity IsInstrumented");
Assert.That(
Expand All @@ -105,7 +105,7 @@ public void LazyPropertyShouldBeUninitializedAndLoadableWithComponentId()
Is.False,
"Lazy property initialization status");
Assert.That(
FieldInterceptionHelper.IsInstrumented(e2),
e2 is IFieldInterceptorAccessor,
Is.True,
"Entity IsInstrumented");
Assert.That(
Expand Down
2 changes: 1 addition & 1 deletion src/NHibernate/Async/Engine/Cascade.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ public async Task CascadeOnAsync(IEntityPersister persister, object parent, obje

IType[] types = persister.PropertyTypes;
CascadeStyle[] cascadeStyles = persister.PropertyCascadeStyles;
var uninitializedLazyProperties = persister.GetUninitializedLazyProperties(parent);
var uninitializedLazyProperties = persister.EntityMetamodel.BytecodeEnhancementMetadata.GetUninitializedLazyProperties(parent);
for (int i = 0; i < types.Length; i++)
{
CascadeStyle style = cascadeStyles[i];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -389,7 +389,7 @@ protected virtual async Task EntityIsDetachedAsync(MergeEvent @event, IDictionar
await (CopyValuesAsync(persister, entity, target, source, copyCache, cancellationToken)).ConfigureAwait(false);

//copyValues works by reflection, so explicitly mark the entity instance dirty
MarkInterceptorDirty(entity, target);
MarkInterceptorDirty(entity, persister, target);

@event.Result = result;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
using Property=NHibernate.Mapping.Property;
using NHibernate.SqlTypes;
using System.Linq;
using NHibernate.Bytecode;

namespace NHibernate.Persister.Entity
{
Expand Down
70 changes: 70 additions & 0 deletions src/NHibernate/Bytecode/IBytecodeEnhancementMetadata.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using NHibernate.Engine;
using NHibernate.Intercept;
using NHibernate.Tuple.Entity;

namespace NHibernate.Bytecode
{
/// <summary>
/// Encapsulates bytecode enhancement information about a particular entity.
///
/// Author: Steve Ebersole
/// </summary>
public interface IBytecodeEnhancementMetadata
{
/// <summary>
/// The name of the entity to which this metadata applies.
/// </summary>
string EntityName { get; }

/// <summary>
/// Has the entity class been bytecode enhanced for lazy loading?
/// </summary>
bool EnhancedForLazyLoading { get; }

/// <summary>
/// Has the information about all lazy properties
/// </summary>
LazyPropertiesMetadata LazyPropertiesMetadata { get; }

/// <summary>
/// Has the information about all properties mapped as lazy="no-proxy"
/// </summary>
UnwrapProxyPropertiesMetadata UnwrapProxyPropertiesMetadata { get; }

/// <summary>
/// Build and inject an interceptor instance into the enhanced entity.
/// </summary>
/// <param name="entity">The entity into which built interceptor should be injected.</param>
/// <param name="lazyPropertiesAreUnfetched">Whether all lazy properties were unfetched or not.</param>
/// <param name="session">The session to which the entity instance belongs.</param>
/// <returns>The built and injected interceptor.</returns>
// TODO: Remove lazyPropertiesAreUnfetched when interceptor will be injected on entity instantiation
IFieldInterceptor InjectInterceptor(object entity, bool lazyPropertiesAreUnfetched, ISessionImplementor session);

/// <summary>
/// Extract the field interceptor instance from the enhanced entity.
/// </summary>
/// <param name="entity">The entity from which to extract the interceptor.</param>
/// <returns>The extracted interceptor.</returns>
IFieldInterceptor ExtractInterceptor(object entity);

/// <summary>
/// Retrieve the uninitialized lazy properties from the enhanced entity.
/// </summary>
/// <param name="entity">The entity from which to retrieve the uninitialized lazy properties.</param>
/// <returns>The uninitialized property names.</returns>
ISet<string> GetUninitializedLazyProperties(object entity);

/// <summary>
/// Retrieve the uninitialized lazy properties from the entity state.
/// </summary>
/// <param name="entityState">The entity state from which to retrieve the uninitialized lazy properties.</param>
/// <returns>The uninitialized property names.</returns>
ISet<string> GetUninitializedLazyProperties(object[] entityState);
}
}
58 changes: 58 additions & 0 deletions src/NHibernate/Bytecode/LazyPropertiesMetadata.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Iesi.Collections.Generic;
using NHibernate.Util;

namespace NHibernate.Bytecode
{
/// <summary>
/// Information about all of the bytecode lazy properties for an entity
///
/// Author: Steve Ebersole
/// </summary>
[Serializable]
public class LazyPropertiesMetadata
{
public static LazyPropertiesMetadata NonEnhanced(string entityName)
{
return new LazyPropertiesMetadata(entityName, null);
}

public static LazyPropertiesMetadata From(
string entityName,
IEnumerable<LazyPropertyDescriptor> lazyPropertyDescriptors)
{
// TODO: port lazy fetch groups
return new LazyPropertiesMetadata(
entityName,
lazyPropertyDescriptors.ToDictionary(o => o.Name));
}

private readonly IDictionary<string, LazyPropertyDescriptor> _lazyPropertyDescriptors;

public LazyPropertiesMetadata(
string entityName,
IDictionary<string, LazyPropertyDescriptor> lazyPropertyDescriptors)
{
EntityName = entityName;
_lazyPropertyDescriptors = lazyPropertyDescriptors;
HasLazyProperties = _lazyPropertyDescriptors?.Count > 0;
LazyPropertyNames = HasLazyProperties
? new ReadOnlySet<string>(new HashSet<string>(_lazyPropertyDescriptors.Keys))
: CollectionHelper.EmptySet<string>();
// TODO: port lazy fetch groups
}

public string EntityName { get; }

public bool HasLazyProperties { get; }

public ISet<string> LazyPropertyNames { get; }

public IEnumerable<LazyPropertyDescriptor> LazyPropertyDescriptors =>
_lazyPropertyDescriptors?.Values ?? Enumerable.Empty<LazyPropertyDescriptor>();
}
}
70 changes: 70 additions & 0 deletions src/NHibernate/Bytecode/LazyPropertyDescriptor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using NHibernate.Type;

namespace NHibernate.Bytecode
{
/// <summary>
/// Descriptor for a property which is enabled for bytecode lazy fetching
///
/// Author: Steve Ebersole
/// </summary>
[Serializable]
public class LazyPropertyDescriptor
{
public static LazyPropertyDescriptor From(
Mapping.Property property,
int propertyIndex,
int lazyIndex)
{
// TODO: port lazy fetch groups

return new LazyPropertyDescriptor(
propertyIndex,
lazyIndex,
property.Name,
property.Type
);
}

private LazyPropertyDescriptor(
int propertyIndex,
int lazyIndex,
string name,
IType type)
{
if (propertyIndex < lazyIndex)
{
throw new InvalidOperationException("Property index is lower than the lazy index.");
}

PropertyIndex = propertyIndex;
LazyIndex = lazyIndex;
Name = name;
Type = type;
}

/// <summary>
/// Access to the index of the property in terms of its position in the entity persister
/// </summary>
public int PropertyIndex { get; }

/// <summary>
/// Access to the index of the property in terms of its position within the lazy properties of the persister
/// </summary>
public int LazyIndex { get; }

/// <summary>
/// Access to the name of the property
/// </summary>
public string Name { get; }

/// <summary>
/// Access to the property's type
/// </summary>
public IType Type { get; }
}
}
31 changes: 31 additions & 0 deletions src/NHibernate/Bytecode/NotInstrumentedException.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.Text;
using System.Threading.Tasks;

namespace NHibernate.Bytecode
{
/// <summary>
/// Indicates a condition where an instrumented/enhanced class was expected, but the class was not
/// instrumented/enhanced.
///
/// Author: Steve Ebersole
/// </summary>
[Serializable]
public class NotInstrumentedException : HibernateException
{
/// <summary>
/// Constructs a NotInstrumentedException.
/// </summary>
/// <param name="message">Message explaining the exception condition.</param>
public NotInstrumentedException(string message) : base(message)
{
}

/// <inheritdoc />
protected NotInstrumentedException(SerializationInfo info, StreamingContext context)
: base(info, context) {}
}
}
63 changes: 63 additions & 0 deletions src/NHibernate/Bytecode/UnwrapProxyPropertiesMetadata.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Iesi.Collections.Generic;
using NHibernate.Util;

namespace NHibernate.Bytecode
{
/// <summary>
/// Information about all properties mapped as lazy="no-proxy" for an entity
/// </summary>
[Serializable]
public class UnwrapProxyPropertiesMetadata
{
public static UnwrapProxyPropertiesMetadata NonEnhanced(string entityName)
{
return new UnwrapProxyPropertiesMetadata(entityName, null);
}

public static UnwrapProxyPropertiesMetadata From(
string entityName,
IEnumerable<UnwrapProxyPropertyDescriptor> unwrapProxyPropertyDescriptors)
{
return new UnwrapProxyPropertiesMetadata(
entityName,
unwrapProxyPropertyDescriptors.ToDictionary(o => o.Name));
}

private readonly IDictionary<string, UnwrapProxyPropertyDescriptor> _unwrapProxyPropertyDescriptors;

public UnwrapProxyPropertiesMetadata(
string entityName,
IDictionary<string, UnwrapProxyPropertyDescriptor> unwrapProxyPropertyDescriptors)
{
EntityName = entityName;
_unwrapProxyPropertyDescriptors = unwrapProxyPropertyDescriptors;
HasUnwrapProxyProperties = unwrapProxyPropertyDescriptors?.Count > 0;
UnwrapProxyPropertyNames = HasUnwrapProxyProperties
? new ReadOnlySet<string>(new HashSet<string>(unwrapProxyPropertyDescriptors.Keys))
: CollectionHelper.EmptySet<string>();
}

public string EntityName { get; }

public bool HasUnwrapProxyProperties { get; }

public ISet<string> UnwrapProxyPropertyNames { get; }

public IEnumerable<UnwrapProxyPropertyDescriptor> UnwrapProxyPropertyDescriptors =>
_unwrapProxyPropertyDescriptors?.Values ?? Enumerable.Empty<UnwrapProxyPropertyDescriptor>();

public int GetUnwrapProxyPropertyIndex(string propertyName)
{
if (!_unwrapProxyPropertyDescriptors.TryGetValue(propertyName, out var descriptor))
{
throw new InvalidOperationException(
$"Property {propertyName} is not mapped as lazy=\"no-proxy\" on entity {EntityName}");
}

return descriptor.PropertyIndex;
}
}
}
Loading