|
From: <aye...@us...> - 2010-01-24 21:46:57
|
Revision: 4925
http://nhibernate.svn.sourceforge.net/nhibernate/?rev=4925&view=rev
Author: ayenderahien
Date: 2010-01-24 21:46:35 +0000 (Sun, 24 Jan 2010)
Log Message:
-----------
adding support for ghost properties
Modified Paths:
--------------
trunk/nhibernate/src/NHibernate/Cfg/MappingSchema/Hbm.generated.cs
trunk/nhibernate/src/NHibernate/Cfg/MappingSchema/HbmManyToOne.cs
trunk/nhibernate/src/NHibernate/Cfg/XmlHbmBinding/PropertiesBinder.cs
trunk/nhibernate/src/NHibernate/Event/Default/AbstractSaveEventListener.cs
trunk/nhibernate/src/NHibernate/Intercept/AbstractFieldInterceptor.cs
trunk/nhibernate/src/NHibernate/Intercept/DefaultFieldInterceptor.cs
trunk/nhibernate/src/NHibernate/Intercept/FieldInterceptionHelper.cs
trunk/nhibernate/src/NHibernate/Intercept/IFieldInterceptor.cs
trunk/nhibernate/src/NHibernate/Mapping/Property.cs
trunk/nhibernate/src/NHibernate/Persister/Entity/AbstractEntityPersister.cs
trunk/nhibernate/src/NHibernate/Tuple/Entity/EntityMetamodel.cs
trunk/nhibernate/src/NHibernate/Tuple/Entity/PocoEntityTuplizer.cs
trunk/nhibernate/src/NHibernate/nhibernate-mapping.xsd
trunk/nhibernate/src/NHibernate.ByteCode.Castle/LazyFieldInterceptor.cs
trunk/nhibernate/src/NHibernate.Test/LazyProperty/LazyPropertyFixture.cs
trunk/nhibernate/src/NHibernate.Test/NHibernate.Test.csproj
Added Paths:
-----------
trunk/nhibernate/src/NHibernate.Test/GhostProperty/
trunk/nhibernate/src/NHibernate.Test/GhostProperty/GhostPropertyFixture.cs
trunk/nhibernate/src/NHibernate.Test/GhostProperty/Mappings.hbm.xml
trunk/nhibernate/src/NHibernate.Test/GhostProperty/Order.cs
Modified: trunk/nhibernate/src/NHibernate/Cfg/MappingSchema/Hbm.generated.cs
===================================================================
--- trunk/nhibernate/src/NHibernate/Cfg/MappingSchema/Hbm.generated.cs 2010-01-24 17:41:59 UTC (rev 4924)
+++ trunk/nhibernate/src/NHibernate/Cfg/MappingSchema/Hbm.generated.cs 2010-01-24 21:46:35 UTC (rev 4925)
@@ -672,11 +672,19 @@
/// <remarks/>
[System.Xml.Serialization.XmlAttributeAttribute("not-null")]
public bool notnull;
-
+
/// <remarks/>
[System.Xml.Serialization.XmlIgnoreAttribute()]
public bool notnullSpecified;
-
+
+ /// <remarks/>
+ [System.Xml.Serialization.XmlAttributeAttribute("force-load-on-property-access")]
+ public bool forceloadonpropertyaccess;
+
+ /// <remarks/>
+ [System.Xml.Serialization.XmlAttributeAttribute()]
+ public bool forceloadonpropertyaccessSpecified;
+
/// <remarks/>
[System.Xml.Serialization.XmlAttributeAttribute()]
[System.ComponentModel.DefaultValueAttribute(false)]
Modified: trunk/nhibernate/src/NHibernate/Cfg/MappingSchema/HbmManyToOne.cs
===================================================================
--- trunk/nhibernate/src/NHibernate/Cfg/MappingSchema/HbmManyToOne.cs 2010-01-24 17:41:59 UTC (rev 4924)
+++ trunk/nhibernate/src/NHibernate/Cfg/MappingSchema/HbmManyToOne.cs 2010-01-24 21:46:35 UTC (rev 4925)
@@ -28,6 +28,11 @@
get { return optimisticlock; }
}
+ public bool ForceLoadOnPropertyAccess
+ {
+ get { return forceloadonpropertyaccess; }
+ }
+
#endregion
#region Overrides of AbstractDecoratable
Modified: trunk/nhibernate/src/NHibernate/Cfg/XmlHbmBinding/PropertiesBinder.cs
===================================================================
--- trunk/nhibernate/src/NHibernate/Cfg/XmlHbmBinding/PropertiesBinder.cs 2010-01-24 17:41:59 UTC (rev 4924)
+++ trunk/nhibernate/src/NHibernate/Cfg/XmlHbmBinding/PropertiesBinder.cs 2010-01-24 21:46:35 UTC (rev 4925)
@@ -107,6 +107,7 @@
var value = new ManyToOne(table);
BindManyToOne(manyToOneMapping, value, propertyName, true);
property = CreateProperty(entityPropertyMapping, className, value, inheritedMetas);
+ property.IsGhostProperty = manyToOneMapping.ForceLoadOnPropertyAccess;
BindManyToOneProperty(manyToOneMapping, property);
}
else if ((componentMapping = entityPropertyMapping as HbmComponent) != null)
Modified: trunk/nhibernate/src/NHibernate/Event/Default/AbstractSaveEventListener.cs
===================================================================
--- trunk/nhibernate/src/NHibernate/Event/Default/AbstractSaveEventListener.cs 2010-01-24 17:41:59 UTC (rev 4924)
+++ trunk/nhibernate/src/NHibernate/Event/Default/AbstractSaveEventListener.cs 2010-01-24 21:46:35 UTC (rev 4925)
@@ -292,7 +292,7 @@
{
if (FieldInterceptionHelper.IsInstrumented(entity))
{
- IFieldInterceptor interceptor = FieldInterceptionHelper.InjectFieldInterceptor(entity, persister.EntityName, null, source);
+ IFieldInterceptor interceptor = FieldInterceptionHelper.InjectFieldInterceptor(entity, persister.EntityName, null, null, source);
interceptor.MarkDirty();
}
}
Modified: trunk/nhibernate/src/NHibernate/Intercept/AbstractFieldInterceptor.cs
===================================================================
--- trunk/nhibernate/src/NHibernate/Intercept/AbstractFieldInterceptor.cs 2010-01-24 17:41:59 UTC (rev 4924)
+++ trunk/nhibernate/src/NHibernate/Intercept/AbstractFieldInterceptor.cs 2010-01-24 21:46:35 UTC (rev 4925)
@@ -1,6 +1,7 @@
using System;
using Iesi.Collections.Generic;
using NHibernate.Engine;
+using NHibernate.Proxy;
namespace NHibernate.Intercept
{
@@ -12,16 +13,18 @@
[NonSerialized]
private ISessionImplementor session;
private ISet<string> uninitializedFields;
+ private ISet<string> uninitializedGhostFieldNames;
private readonly string entityName;
[NonSerialized]
private bool initializing;
private bool isDirty;
- protected internal AbstractFieldInterceptor(ISessionImplementor session, ISet<string> uninitializedFields, string entityName)
+ protected internal AbstractFieldInterceptor(ISessionImplementor session, ISet<string> uninitializedFields, ISet<string> uninitializedGhostFieldNames, string entityName)
{
this.session = session;
this.uninitializedFields = uninitializedFields;
+ this.uninitializedGhostFieldNames = uninitializedGhostFieldNames;
this.entityName = entityName;
}
@@ -74,11 +77,9 @@
get { return initializing; }
}
- public object Intercept(object target, string fieldName)
+ public object Intercept(object target, string fieldName, object value)
{
- if (initializing ||
- uninitializedFields == null ||
- !uninitializedFields.Contains(fieldName))
+ if (initializing)
return InvokeImplementation;
if (session == null)
@@ -90,11 +91,38 @@
throw new LazyInitializationException("session is not connected");
}
+ if (uninitializedFields != null && uninitializedFields.Contains(fieldName))
+ {
+ return InitializeField(fieldName, target);
+ }
+ if (value is INHibernateProxy && uninitializedGhostFieldNames != null && uninitializedGhostFieldNames.Contains(fieldName))
+ {
+ return InitializeOrGetAssociation((INHibernateProxy)value);
+ }
+ return InvokeImplementation;
+ }
+
+ private object InitializeOrGetAssociation(INHibernateProxy value)
+ {
+ if(value.HibernateLazyInitializer.IsUninitialized)
+ {
+ value.HibernateLazyInitializer.Initialize();
+ var association = value.HibernateLazyInitializer.GetImplementation(session);
+ var narrowedProxy = session.PersistenceContext.ProxyFor(association);
+ // we set the narrowed impl here to be able to get it back in the future
+ value.HibernateLazyInitializer.SetImplementation(narrowedProxy);
+ }
+ return value.HibernateLazyInitializer.GetImplementation(session);
+ }
+
+ private object InitializeField(string fieldName, object target)
+ {
object result;
initializing = true;
try
{
- result = ((ILazyPropertyInitializer)session.Factory.GetEntityPersister(entityName)).InitializeLazyProperty(fieldName, target, session);
+ var lazyPropertyInitializer = ((ILazyPropertyInitializer) session.Factory.GetEntityPersister(entityName));
+ result = lazyPropertyInitializer.InitializeLazyProperty(fieldName, target, session);
}
finally
{
Modified: trunk/nhibernate/src/NHibernate/Intercept/DefaultFieldInterceptor.cs
===================================================================
--- trunk/nhibernate/src/NHibernate/Intercept/DefaultFieldInterceptor.cs 2010-01-24 17:41:59 UTC (rev 4924)
+++ trunk/nhibernate/src/NHibernate/Intercept/DefaultFieldInterceptor.cs 2010-01-24 21:46:35 UTC (rev 4925)
@@ -5,8 +5,8 @@
{
public class DefaultFieldInterceptor : AbstractFieldInterceptor
{
- public DefaultFieldInterceptor(ISessionImplementor session, ISet<string> uninitializedFields, string entityName)
- : base(session, uninitializedFields, entityName)
+ public DefaultFieldInterceptor(ISessionImplementor session, ISet<string> uninitializedFields, ISet<string> uninitializedGhostFieldNames, string entityName)
+ : base(session, uninitializedFields, uninitializedGhostFieldNames, entityName)
{
}
}
Modified: trunk/nhibernate/src/NHibernate/Intercept/FieldInterceptionHelper.cs
===================================================================
--- trunk/nhibernate/src/NHibernate/Intercept/FieldInterceptionHelper.cs 2010-01-24 17:41:59 UTC (rev 4924)
+++ trunk/nhibernate/src/NHibernate/Intercept/FieldInterceptionHelper.cs 2010-01-24 21:46:35 UTC (rev 4925)
@@ -32,12 +32,15 @@
return fieldInterceptorAccessor == null ? null : fieldInterceptorAccessor.FieldInterceptor;
}
- public static IFieldInterceptor InjectFieldInterceptor(object entity, string entityName, ISet<string> uninitializedFieldNames, ISessionImplementor session)
+ public static IFieldInterceptor InjectFieldInterceptor(object entity, string entityName,
+ ISet<string> uninitializedFieldNames,
+ ISet<string> uninitializedGhostFieldNames,
+ ISessionImplementor session)
{
var fieldInterceptorAccessor = entity as IFieldInterceptorAccessor;
if (fieldInterceptorAccessor != null)
{
- var fieldInterceptorImpl = new DefaultFieldInterceptor(session, uninitializedFieldNames, entityName);
+ var fieldInterceptorImpl = new DefaultFieldInterceptor(session, uninitializedFieldNames, uninitializedGhostFieldNames, entityName);
fieldInterceptorAccessor.FieldInterceptor = fieldInterceptorImpl;
return fieldInterceptorImpl;
}
Modified: trunk/nhibernate/src/NHibernate/Intercept/IFieldInterceptor.cs
===================================================================
--- trunk/nhibernate/src/NHibernate/Intercept/IFieldInterceptor.cs 2010-01-24 17:41:59 UTC (rev 4924)
+++ trunk/nhibernate/src/NHibernate/Intercept/IFieldInterceptor.cs 2010-01-24 21:46:35 UTC (rev 4925)
@@ -28,6 +28,6 @@
void ClearDirty();
/// <summary> Intercept field set/get </summary>
- object Intercept(object target, string fieldName);
+ object Intercept(object target, string fieldName, object value);
}
}
\ No newline at end of file
Modified: trunk/nhibernate/src/NHibernate/Mapping/Property.cs
===================================================================
--- trunk/nhibernate/src/NHibernate/Mapping/Property.cs 2010-01-24 17:41:59 UTC (rev 4924)
+++ trunk/nhibernate/src/NHibernate/Mapping/Property.cs 2010-01-24 21:46:35 UTC (rev 4925)
@@ -132,10 +132,10 @@
{
bool[] columnUpdateability = propertyValue.ColumnUpdateability;
return updateable &&
- (
- // columnUpdateability.Length == 0 ||
- !ArrayHelper.IsAllFalse(columnUpdateability)
- );
+ (
+ // columnUpdateability.Length == 0 ||
+ !ArrayHelper.IsAllFalse(columnUpdateability)
+ );
}
set { updateable = value; }
}
@@ -146,10 +146,10 @@
{
bool[] columnInsertability = propertyValue.ColumnInsertability;
return insertable &&
- (
- columnInsertability.Length == 0 ||
- !ArrayHelper.IsAllFalse(columnInsertability)
- );
+ (
+ columnInsertability.Length == 0 ||
+ !ArrayHelper.IsAllFalse(columnInsertability)
+ );
}
set { insertable = value; }
}
@@ -206,7 +206,7 @@
public MetaAttribute GetMetaAttribute(string attributeName)
{
- if(metaAttributes == null)
+ if (metaAttributes == null)
{
return null;
}
@@ -226,7 +226,7 @@
{
if (propertyValue is SimpleValue)
{
- return ((SimpleValue) propertyValue).NullValue;
+ return ((SimpleValue)propertyValue).NullValue;
}
else
return null;
@@ -304,5 +304,7 @@
get { return nodeName; }
set { nodeName = value; }
}
+
+ public bool IsGhostProperty { get; set; }
}
}
Modified: trunk/nhibernate/src/NHibernate/Persister/Entity/AbstractEntityPersister.cs
===================================================================
--- trunk/nhibernate/src/NHibernate/Persister/Entity/AbstractEntityPersister.cs 2010-01-24 17:41:59 UTC (rev 4924)
+++ trunk/nhibernate/src/NHibernate/Persister/Entity/AbstractEntityPersister.cs 2010-01-24 21:46:35 UTC (rev 4925)
@@ -3615,7 +3615,7 @@
}
else
{
- IFieldInterceptor fieldInterceptor = FieldInterceptionHelper.InjectFieldInterceptor(entity, EntityName, null, session);
+ IFieldInterceptor fieldInterceptor = FieldInterceptionHelper.InjectFieldInterceptor(entity, EntityName, null, null, session);
fieldInterceptor.MarkDirty();
}
}
Modified: trunk/nhibernate/src/NHibernate/Tuple/Entity/EntityMetamodel.cs
===================================================================
--- trunk/nhibernate/src/NHibernate/Tuple/Entity/EntityMetamodel.cs 2010-01-24 17:41:59 UTC (rev 4924)
+++ trunk/nhibernate/src/NHibernate/Tuple/Entity/EntityMetamodel.cs 2010-01-24 21:46:35 UTC (rev 4925)
@@ -153,6 +153,11 @@
{
hasLazy = true;
}
+ if (prop.IsGhostProperty)
+ {
+ hasGhostProperties = true;
+ }
+
propertyLaziness[i] = lazyProperty;
propertyNames[i] = properties[i].Name;
@@ -230,20 +235,23 @@
else
{
log.Info("lazy property fetching available for: " + name);
- foreach (var prop in persistentClass.PropertyClosureIterator)
- {
- if (prop.IsLazy == false)
- continue;
-
- var getter = prop.GetGetter(persistentClass.MappedClass);
- if(getter.Method == null ||
- getter.Method.IsDefined(typeof(CompilerGeneratedAttribute), false) == false)
- {
- log.ErrorFormat("Lazy property {0}.{1} is not an auto property, which may result in uninitialized property access", persistentClass.EntityName, prop.Name);
- }
- }
+ VerifyCanInterceptPropertiesForLazyOrGhostProperties(persistentClass);
}
}
+ if(hasGhostProperties)
+ {
+ if (lazy == false)
+ {
+ log.WarnFormat("Disabled ghost properies fetching for {0} beacuse it does not support lazy at the entity level", name);
+ hasGhostProperties = false;
+ }
+ else
+ {
+ log.Info("Ghost property fetching available for: " + name);
+ if (hasLazy == false) // avoid double checking
+ VerifyCanInterceptPropertiesForLazyOrGhostProperties(persistentClass);
+ }
+ }
mutable = persistentClass.IsMutable;
@@ -291,6 +299,22 @@
tuplizerMapping = new EntityEntityModeToTuplizerMapping(persistentClass, this);
}
+ private static void VerifyCanInterceptPropertiesForLazyOrGhostProperties(PersistentClass persistentClass)
+ {
+ foreach (var prop in persistentClass.PropertyClosureIterator)
+ {
+ if (prop.IsLazy == false && prop.IsGhostProperty)
+ continue;
+
+ var getter = prop.GetGetter(persistentClass.MappedClass);
+ if(getter.Method == null ||
+ getter.Method.IsDefined(typeof(CompilerGeneratedAttribute), false) == false)
+ {
+ log.ErrorFormat("Lazy or ghost property {0}.{1} is not an auto property, which may result in uninitialized property access", persistentClass.EntityName, prop.Name);
+ }
+ }
+ }
+
private ValueInclusion DetermineInsertValueGenerationType(Mapping.Property mappingProperty, StandardProperty runtimeProperty)
{
if (runtimeProperty.IsInsertGenerated)
@@ -659,6 +683,7 @@
#region Tuplizer
private readonly EntityEntityModeToTuplizerMapping tuplizerMapping;
+ private bool hasGhostProperties;
public IEntityTuplizer GetTuplizer(EntityMode entityMode)
{
@@ -681,6 +706,11 @@
get { return naturalIdPropertyNumbers != null; }
}
+ public bool HasGhostProperties
+ {
+ get { return hasGhostProperties; }
+ }
+
public bool HasNonIdentifierPropertyNamedId
{
get { return hasNonIdentifierPropertyNamedId; }
Modified: trunk/nhibernate/src/NHibernate/Tuple/Entity/PocoEntityTuplizer.cs
===================================================================
--- trunk/nhibernate/src/NHibernate/Tuple/Entity/PocoEntityTuplizer.cs 2010-01-24 17:41:59 UTC (rev 4924)
+++ trunk/nhibernate/src/NHibernate/Tuple/Entity/PocoEntityTuplizer.cs 2010-01-24 21:46:35 UTC (rev 4925)
@@ -25,6 +25,7 @@
private readonly bool islifecycleImplementor;
private readonly bool isValidatableImplementor;
private readonly HashedSet<string> lazyPropertyNames = new HashedSet<string>();
+ private readonly HashedSet<string> ghostPropertyNames = new HashedSet<string>();
[NonSerialized]
private IReflectionOptimizer optimizer;
private readonly IProxyValidator proxyValidator;
@@ -54,6 +55,8 @@
{
if (property.IsLazy)
lazyPropertyNames.Add(property.Name);
+ if (property.IsGhostProperty)
+ ghostPropertyNames.Add(property.Name);
}
SetReflectionOptimizer();
@@ -74,10 +77,10 @@
public override bool IsInstrumented
{
- get {
- return
- EntityMetamodel.HasLazyProperties &&
- FieldInterceptionHelper.IsInstrumented(MappedClass);
+ get
+ {
+ return (EntityMetamodel.HasLazyProperties || EntityMetamodel.HasGhostProperties)
+ && FieldInterceptionHelper.IsInstrumented(MappedClass);
}
}
@@ -101,12 +104,12 @@
if (optimizer == null)
{
log.Debug("Create Instantiator without optimizer for:" + persistentClass.MappedClass.FullName);
- return new PocoInstantiator(persistentClass, null, ProxyFactory, EntityMetamodel.HasLazyProperties);
+ return new PocoInstantiator(persistentClass, null, ProxyFactory, EntityMetamodel.HasLazyProperties || EntityMetamodel.HasGhostProperties);
}
else
{
log.Debug("Create Instantiator using optimizer for:" + persistentClass.MappedClass.FullName);
- return new PocoInstantiator(persistentClass, optimizer.InstantiationOptimizer, ProxyFactory, EntityMetamodel.HasLazyProperties);
+ return new PocoInstantiator(persistentClass, optimizer.InstantiationOptimizer, ProxyFactory, EntityMetamodel.HasLazyProperties || EntityMetamodel.HasGhostProperties);
}
}
@@ -226,7 +229,7 @@
HashedSet<string> lazyProps = lazyPropertiesAreUnfetched && EntityMetamodel.HasLazyProperties ? lazyPropertyNames : null;
//TODO: if we support multiple fetch groups, we would need
// to clone the set of lazy properties!
- FieldInterceptionHelper.InjectFieldInterceptor(entity, EntityName, lazyProps, session);
+ FieldInterceptionHelper.InjectFieldInterceptor(entity, EntityName, lazyProps, ghostPropertyNames, session);
}
}
Modified: trunk/nhibernate/src/NHibernate/nhibernate-mapping.xsd
===================================================================
--- trunk/nhibernate/src/NHibernate/nhibernate-mapping.xsd 2010-01-24 17:41:59 UTC (rev 4924)
+++ trunk/nhibernate/src/NHibernate/nhibernate-mapping.xsd 2010-01-24 21:46:35 UTC (rev 4925)
@@ -879,6 +879,8 @@
</xs:attribute>
<xs:attribute name="unique" default="false" type="xs:boolean">
</xs:attribute>
+ <xs:attribute name="force-load-on-property-access" default="false" type="xs:boolean">
+ </xs:attribute>
<xs:attribute name="unique-key" type="xs:string" />
<xs:attribute name="index" type="xs:string" />
<xs:attribute name="cascade" type="xs:string" />
Modified: trunk/nhibernate/src/NHibernate.ByteCode.Castle/LazyFieldInterceptor.cs
===================================================================
--- trunk/nhibernate/src/NHibernate.ByteCode.Castle/LazyFieldInterceptor.cs 2010-01-24 17:41:59 UTC (rev 4924)
+++ trunk/nhibernate/src/NHibernate.ByteCode.Castle/LazyFieldInterceptor.cs 2010-01-24 21:46:35 UTC (rev 4925)
@@ -18,20 +18,22 @@
{
if (ReflectHelper.IsPropertyGet(invocation.Method))
{
- var result = FieldInterceptor.Intercept(invocation.InvocationTarget, ReflectHelper.GetPropertyName(invocation.Method));
- if (result == AbstractFieldInterceptor.InvokeImplementation)
+ invocation.Proceed(); // get the existing value
+
+ var result = FieldInterceptor.Intercept(
+ invocation.InvocationTarget,
+ ReflectHelper.GetPropertyName(invocation.Method),
+ invocation.ReturnValue);
+
+ if (result != AbstractFieldInterceptor.InvokeImplementation)
{
- invocation.Proceed();
- }
- else
- {
invocation.ReturnValue = result;
}
}
else if (ReflectHelper.IsPropertySet(invocation.Method))
{
FieldInterceptor.MarkDirty();
- FieldInterceptor.Intercept(invocation.InvocationTarget, ReflectHelper.GetPropertyName(invocation.Method));
+ FieldInterceptor.Intercept(invocation.InvocationTarget, ReflectHelper.GetPropertyName(invocation.Method), null);
invocation.Proceed();
}
}
Property changes on: trunk/nhibernate/src/NHibernate.Test/GhostProperty
___________________________________________________________________
Added: bugtraq:url
+ http://jira.nhibernate.org/browse/%BUGID%
Added: bugtraq:logregex
+ NH-\d+
Added: trunk/nhibernate/src/NHibernate.Test/GhostProperty/GhostPropertyFixture.cs
===================================================================
--- trunk/nhibernate/src/NHibernate.Test/GhostProperty/GhostPropertyFixture.cs (rev 0)
+++ trunk/nhibernate/src/NHibernate.Test/GhostProperty/GhostPropertyFixture.cs 2010-01-24 21:46:35 UTC (rev 4925)
@@ -0,0 +1,81 @@
+using System.Collections;
+using NHibernate.ByteCode.Castle;
+using NHibernate.Cfg;
+using NUnit.Framework;
+
+namespace NHibernate.Test.GhostProperty
+{
+ [TestFixture]
+ public class GhostPropertyFixture : TestCase
+ {
+ protected override string MappingsAssembly
+ {
+ get { return "NHibernate.Test"; }
+ }
+
+ protected override IList Mappings
+ {
+ get { return new[] { "GhostProperty.Mappings.hbm.xml" }; }
+ }
+
+ protected override void Configure(NHibernate.Cfg.Configuration configuration)
+ {
+ configuration.SetProperty(Environment.ProxyFactoryFactoryClass,
+ typeof(ProxyFactoryFactory).AssemblyQualifiedName);
+ }
+
+ protected override void OnSetUp()
+ {
+ using (var s = OpenSession())
+ using (var tx = s.BeginTransaction())
+ {
+ var wireTransfer = new WireTransfer
+ {
+ Id = 1
+ };
+ s.Persist(wireTransfer);
+ s.Persist(new Order
+ {
+ Id = 1,
+ Payment = wireTransfer
+ });
+ tx.Commit();
+ }
+
+ }
+
+ protected override void OnTearDown()
+ {
+ using (var s = OpenSession())
+ using (var tx = s.BeginTransaction())
+ {
+ s.Delete("from Order");
+ s.Delete("from Payment");
+ tx.Commit();
+ }
+ }
+
+ [Test]
+ public void CanGetActualValueFromLazyManyToOne()
+ {
+ using (ISession s = OpenSession())
+ {
+ var order = s.Get<Order>(1);
+
+ Assert.IsTrue(order.Payment is WireTransfer);
+ }
+ }
+
+ [Test]
+ public void GhostPropertyMaintainIdentityMap()
+ {
+ using (ISession s = OpenSession())
+ {
+ var order = s.Get<Order>(1);
+
+ Assert.AreSame(order.Payment, s.Load<Payment>(1));
+ }
+ }
+
+ }
+}
\ No newline at end of file
Added: trunk/nhibernate/src/NHibernate.Test/GhostProperty/Mappings.hbm.xml
===================================================================
--- trunk/nhibernate/src/NHibernate.Test/GhostProperty/Mappings.hbm.xml (rev 0)
+++ trunk/nhibernate/src/NHibernate.Test/GhostProperty/Mappings.hbm.xml 2010-01-24 21:46:35 UTC (rev 4925)
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
+ assembly="NHibernate.Test"
+ namespace="NHibernate.Test.GhostProperty">
+
+ <class name="Order" table="Orders">
+ <id name="Id">
+ <generator class="assigned" />
+ </id>
+ <many-to-one name="Payment" force-load-on-property-access="true"/>
+ </class>
+
+
+ <class name="Payment" abstract="true">
+ <id name="Id">
+ <generator class="assigned" />
+ </id>
+ <discriminator column="Type" type="System.String"/>
+ <subclass name="WireTransfer" discriminator-value="WT">
+
+ </subclass>
+ <subclass name="CreditCard" discriminator-value="CC">
+
+ </subclass>
+
+ </class>
+
+</hibernate-mapping>
Added: trunk/nhibernate/src/NHibernate.Test/GhostProperty/Order.cs
===================================================================
--- trunk/nhibernate/src/NHibernate.Test/GhostProperty/Order.cs (rev 0)
+++ trunk/nhibernate/src/NHibernate.Test/GhostProperty/Order.cs 2010-01-24 21:46:35 UTC (rev 4925)
@@ -0,0 +1,16 @@
+namespace NHibernate.Test.GhostProperty
+{
+ public class Order
+ {
+ public virtual int Id { get; set; }
+ public virtual Payment Payment { get; set; }
+ }
+
+ public abstract class Payment
+ {
+ public virtual int Id { get; set; }
+ }
+
+ public class WireTransfer : Payment{}
+ public class CreditCard : Payment { }
+}
\ No newline at end of file
Modified: trunk/nhibernate/src/NHibernate.Test/LazyProperty/LazyPropertyFixture.cs
===================================================================
--- trunk/nhibernate/src/NHibernate.Test/LazyProperty/LazyPropertyFixture.cs 2010-01-24 17:41:59 UTC (rev 4924)
+++ trunk/nhibernate/src/NHibernate.Test/LazyProperty/LazyPropertyFixture.cs 2010-01-24 21:46:35 UTC (rev 4925)
@@ -85,7 +85,7 @@
[Test]
public void ShouldGenerateErrorForNonAutoPropLazyProp()
{
- Assert.IsTrue(log.Contains("Lazy property NHibernate.Test.LazyProperty.Book.ALotOfText is not an auto property, which may result in uninitialized property access"));
+ Assert.IsTrue(log.Contains("Lazy or ghost property NHibernate.Test.LazyProperty.Book.ALotOfText is not an auto property, which may result in uninitialized property access"));
}
[Test]
Modified: trunk/nhibernate/src/NHibernate.Test/NHibernate.Test.csproj
===================================================================
--- trunk/nhibernate/src/NHibernate.Test/NHibernate.Test.csproj 2010-01-24 17:41:59 UTC (rev 4924)
+++ trunk/nhibernate/src/NHibernate.Test/NHibernate.Test.csproj 2010-01-24 21:46:35 UTC (rev 4925)
@@ -341,6 +341,8 @@
<Compile Include="GenericTest\SetGeneric\A.cs" />
<Compile Include="GenericTest\SetGeneric\B.cs" />
<Compile Include="GenericTest\SetGeneric\SetGenericFixture.cs" />
+ <Compile Include="GhostProperty\Order.cs" />
+ <Compile Include="GhostProperty\GhostPropertyFixture.cs" />
<Compile Include="HQL\Animal.cs" />
<Compile Include="HQL\Ast\Address.cs" />
<Compile Include="HQL\Ast\Animal.cs" />
@@ -2113,6 +2115,7 @@
<EmbeddedResource Include="CfgTest\Loquacious\EntityToCache.hbm.xml" />
<EmbeddedResource Include="DriverTest\SqlServerCeEntity.hbm.xml" />
<Content Include="DynamicEntity\package.html" />
+ <EmbeddedResource Include="GhostProperty\Mappings.hbm.xml" />
<EmbeddedResource Include="NHSpecificTest\NH2065\Mappings.hbm.xml" />
<EmbeddedResource Include="NHSpecificTest\NH2009\Mappings.hbm.xml" />
<EmbeddedResource Include="NHSpecificTest\NH1989\Mappings.hbm.xml" />
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|