Skip to content

Commit 263be64

Browse files
committed
Draft for EntityProjections
1 parent efae48b commit 263be64

File tree

6 files changed

+754
-6
lines changed

6 files changed

+754
-6
lines changed
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
using System;
2+
using System.Collections.Generic;
3+
4+
namespace NHibernate.Test.NHSpecificTest.GH948
5+
{
6+
public class EntitySimpleChild
7+
{
8+
public virtual Guid Id { get; set; }
9+
public virtual string Name { get; set; }
10+
}
11+
12+
public class EntityComplex
13+
{
14+
public virtual Guid Id { get; set; }
15+
16+
public virtual int Version { get; set; }
17+
18+
public virtual string Name { get; set; }
19+
20+
public virtual string LazyProp { get; set; }
21+
22+
public virtual EntitySimpleChild Child1 { get; set; }
23+
public virtual EntitySimpleChild Child2 { get; set; }
24+
public virtual EntityComplex SameTypeChild { get; set; }
25+
26+
public virtual IList<EntitySimpleChild> ChildrenList { get; set; }
27+
}
28+
29+
public class CompositeKey
30+
{
31+
public int Id1 { get; set; }
32+
public int Id2 { get; set; }
33+
34+
public override bool Equals(object obj)
35+
{
36+
var key = obj as CompositeKey;
37+
return key != null
38+
&& Id1 == key.Id1
39+
&& Id2 == key.Id2;
40+
}
41+
42+
public override int GetHashCode()
43+
{
44+
var hashCode = -1596524975;
45+
hashCode = hashCode * -1521134295 + Id1.GetHashCode();
46+
hashCode = hashCode * -1521134295 + Id2.GetHashCode();
47+
return hashCode;
48+
}
49+
}
50+
51+
public class EntityWithCompositeId
52+
{
53+
public virtual CompositeKey Key { get; set; }
54+
public virtual string Name { get; set; }
55+
}
56+
}
Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
using System;
2+
using NHibernate.Criterion;
3+
using NHibernate.Engine;
4+
using NHibernate.SqlCommand;
5+
using NHibernate.Type;
6+
using System.Linq;
7+
using IQueryable = NHibernate.Persister.Entity.IQueryable;
8+
9+
namespace NHibernate.Test.NHSpecificTest.GH948
10+
{
11+
using TThis = EntityProjection;
12+
13+
[Serializable]
14+
public class EntityProjection : IProjection
15+
{
16+
private string _entityAlias;
17+
private string _columnAliasSuffix;
18+
public string _tableAlias;
19+
private IType[] _types;
20+
21+
/// <summary>
22+
/// Root entity projection
23+
/// </summary>
24+
public EntityProjection():this(null, null)
25+
{}
26+
27+
public EntityProjection(System.Type rootEntity, string entityAlias)
28+
{
29+
RootEntity = rootEntity;
30+
_entityAlias = entityAlias;
31+
}
32+
33+
public bool FetchLazyProperties { get; set; }
34+
public bool IsReadOnly { get; set; }
35+
public bool Lazy { get; set; }
36+
37+
internal string[] IdentifierColumnAliases { get; private set; }
38+
internal string[][] PropertyColumnAliases { get; private set; }
39+
internal IQueryable Persister { get; private set; }
40+
internal System.Type RootEntity { get; private set; }
41+
42+
#region Configuration methods
43+
44+
public TThis SetLazy(bool lazy = true)
45+
{
46+
Lazy = lazy;
47+
return this;
48+
}
49+
50+
public TThis SetAllPropertyFetch(bool fetchLazyProperties = true)
51+
{
52+
FetchLazyProperties = fetchLazyProperties;
53+
return this;
54+
}
55+
56+
public TThis SetReadonly(bool isReadOnly = true)
57+
{
58+
IsReadOnly = isReadOnly;
59+
return this;
60+
}
61+
62+
#endregion Configuration methods
63+
64+
#region IProjection implementation
65+
66+
public string[] Aliases
67+
{
68+
get;
69+
private set;
70+
}
71+
72+
bool IProjection.IsAggregate
73+
{
74+
get { return false; }
75+
}
76+
77+
bool IProjection.IsGrouped
78+
{
79+
get { return false; }
80+
}
81+
82+
IType[] IProjection.GetTypes(string alias, ICriteria criteria, ICriteriaQuery criteriaQuery)
83+
{
84+
return new[] { criteriaQuery.GetType(criteria, alias) };
85+
}
86+
87+
IType[] IProjection.GetTypes(ICriteria criteria, ICriteriaQuery criteriaQuery)
88+
{
89+
SetFields(criteria, criteriaQuery);
90+
return _types;
91+
}
92+
93+
public string[] GetColumnAliases(int position, ICriteria criteria, ICriteriaQuery criteriaQuery)
94+
{
95+
SetFields(criteria, criteriaQuery);
96+
return Aliases;
97+
}
98+
99+
public string[] GetColumnAliases(string alias, int position, ICriteria criteria, ICriteriaQuery criteriaQuery)
100+
{
101+
SetFields(criteria, criteriaQuery);
102+
return Aliases;
103+
}
104+
105+
SqlString IProjection.ToSqlString(ICriteria criteria, int position, ICriteriaQuery criteriaQuery)
106+
{
107+
SetFields(criteria, criteriaQuery);
108+
109+
string identifierSelectFragment = Persister.IdentifierSelectFragment(_tableAlias, _columnAliasSuffix);
110+
if (Lazy)
111+
return new SqlString(identifierSelectFragment);
112+
113+
return new SqlString(
114+
string.Concat(
115+
identifierSelectFragment,
116+
Persister.PropertySelectFragment(_tableAlias, _columnAliasSuffix, FetchLazyProperties)));
117+
}
118+
119+
SqlString IProjection.ToGroupSqlString(ICriteria criteria, ICriteriaQuery criteriaQuery)
120+
{
121+
throw new NotImplementedException();
122+
}
123+
124+
TypedValue[] IProjection.GetTypedValues(ICriteria criteria, ICriteriaQuery criteriaQuery)
125+
{
126+
throw new NotImplementedException();
127+
}
128+
129+
#endregion IProjection implementation
130+
131+
private void SetFields(ICriteria criteria, ICriteriaQuery criteriaQuery)
132+
{
133+
//Persister is required, so let's use it as "initialized marker"
134+
if (Persister != null)
135+
return;
136+
137+
if (RootEntity == null)
138+
{
139+
RootEntity = criteria.GetRootEntityTypeIfAvailable();
140+
}
141+
142+
if (_entityAlias == null)
143+
{
144+
_entityAlias = criteria.Alias;
145+
}
146+
147+
Persister = (IQueryable) criteriaQuery.Factory.GetEntityPersister(RootEntity.FullName);
148+
149+
ICriteria subcriteria = criteria.GetCriteriaByAlias(_entityAlias);
150+
if (subcriteria == null)
151+
throw new HibernateException($"Criteria\\QueryOver alias '{_entityAlias}' for entity projection is not found.");
152+
153+
_tableAlias = criteriaQuery.GetSQLAlias(
154+
subcriteria,
155+
Persister.IdentifierPropertyName ?? string.Empty);
156+
157+
_columnAliasSuffix = criteriaQuery.GetIndexForAlias().ToString();
158+
159+
_types = new IType[] {new EntityProjectionType(this)};
160+
161+
IdentifierColumnAliases = Persister.GetIdentifierAliases(_columnAliasSuffix);
162+
163+
PropertyColumnAliases = Lazy
164+
? new string[][] { }
165+
: Enumerable.Range(0, Persister.PropertyNames.Length).Select(i => Persister.GetPropertyAliases(_columnAliasSuffix, i)).ToArray();
166+
167+
Aliases = IdentifierColumnAliases.Concat(PropertyColumnAliases.SelectMany(alias => alias)).ToArray();
168+
}
169+
}
170+
}
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
using System;
2+
using System.Data.Common;
3+
using NHibernate.Engine;
4+
using NHibernate.Event;
5+
using NHibernate.Persister.Entity;
6+
using NHibernate.Type;
7+
8+
namespace NHibernate.Test.NHSpecificTest.GH948
9+
{
10+
/// <summary>
11+
/// Specialized type for retrieving entity from Criteria/QueryOver API projection.
12+
/// Intended to be used only inside <see cref="EntityProjection"/>
13+
/// </summary>
14+
[Serializable]
15+
internal partial class EntityProjectionType : ManyToOneType, IType
16+
{
17+
private readonly EntityProjection _projection;
18+
19+
public EntityProjectionType(EntityProjection projection) : base(projection.RootEntity.FullName, projection.Lazy)
20+
{
21+
_projection = projection;
22+
}
23+
24+
object IType.NullSafeGet(DbDataReader rs, string[] names, ISessionImplementor session, object owner)
25+
{
26+
//names parameter is ignored (taken from projection)
27+
return NullSafeGet(rs, string.Empty, session, owner);
28+
}
29+
30+
public override object NullSafeGet(DbDataReader rs, string name, ISessionImplementor session, object owner)
31+
{
32+
var identifier = _projection.Persister.IdentifierType.NullSafeGet(rs, _projection.IdentifierColumnAliases, session, null);
33+
34+
if (identifier == null)
35+
{
36+
return null;
37+
}
38+
39+
return _projection.Lazy
40+
? ResolveIdentifier(identifier, session)
41+
: GetInitializedEntityFromProjection(rs, session, identifier);
42+
}
43+
44+
private object GetInitializedEntityFromProjection(DbDataReader rs, ISessionImplementor session, object identifier)
45+
{
46+
var entity = CreateInitializedEntity(
47+
rs,
48+
session,
49+
_projection.Persister,
50+
identifier,
51+
_projection.PropertyColumnAliases,
52+
LockMode.None,
53+
_projection.FetchLazyProperties,
54+
_projection.IsReadOnly);
55+
56+
return entity;
57+
}
58+
59+
private static object CreateInitializedEntity(DbDataReader rs, ISessionImplementor session, IQueryable persister, object identifier, string[][] propertyAliases, LockMode lockMode, bool fetchLazyProperties, bool readOnly)
60+
{
61+
var eventSource = session as IEventSource;
62+
PostLoadEvent postLoadEvent = null;
63+
PreLoadEvent preLoadEvent = null;
64+
object entity;
65+
if (eventSource != null)
66+
{
67+
preLoadEvent = new PreLoadEvent(eventSource);
68+
postLoadEvent = new PostLoadEvent(eventSource);
69+
entity = eventSource.Instantiate(persister, identifier);
70+
}
71+
else
72+
{
73+
entity = session.Instantiate(persister.EntityName, identifier);
74+
}
75+
76+
TwoPhaseLoad.AddUninitializedEntity(
77+
session.GenerateEntityKey(identifier, persister),
78+
entity,
79+
persister,
80+
lockMode,
81+
!fetchLazyProperties,
82+
session);
83+
84+
var hydrated = persister.Hydrate(
85+
rs,
86+
identifier,
87+
entity,
88+
persister,
89+
propertyAliases,
90+
fetchLazyProperties,
91+
session);
92+
93+
TwoPhaseLoad.PostHydrate(persister, identifier, hydrated, null, entity, lockMode, !fetchLazyProperties, session);
94+
TwoPhaseLoad.InitializeEntity(entity, readOnly, session, preLoadEvent, postLoadEvent);
95+
return entity;
96+
}
97+
}
98+
}

0 commit comments

Comments
 (0)