From ba1851c2d2d7eab58467ded9a5b4cf1226ea9249 Mon Sep 17 00:00:00 2001 From: ayende Date: Sat, 10 Mar 2007 11:38:21 +0000 Subject: [PATCH] AR-131 Applied patch Dave Godfrey Active record doesn't allow you to set the NHibernate "fetch" attribute on HasMany associations Note that this [Obsolete] the OuterJoin property on BelongsTo, in favor of the Fetch property. git-svn-id: https://svn.castleproject.org/svn/castle/trunk@3585 73e77b4c-caa6-f847-a29a-24ab75ae54b6 --- ...veRecord.Framework.Internal.Tests-vs2003.csproj | 11 +++++ ...veRecord.Framework.Internal.Tests-vs2005.csproj | 2 + .../Model/BelongsToWithFetch.cs | 50 ++++++++++++++++++++ .../Model/HasManyWithFetch.cs | 40 ++++++++++++++++ .../XmlGenerationTestCase.cs | 53 ++++++++++++++++++++- .../Attributes/BelongsToAttribute.cs | 51 ++++++++++++++++++-- .../Castle.ActiveRecord/Attributes/Enums.cs | 19 ++++++++ .../Attributes/HasAndBelongsToManyAttribute.cs | 11 +++++ .../Attributes/HasManyAttribute.cs | 15 ++++++ .../Attributes/OneToOneAttribute.cs | 19 -------- .../Internal/Visitors/XmlGenerationVisitor.cs | 54 ++++++++++------------ 11 files changed, 272 insertions(+), 53 deletions(-) create mode 100644 ActiveRecord/Castle.ActiveRecord.Framework.Internal.Tests/Model/BelongsToWithFetch.cs create mode 100644 ActiveRecord/Castle.ActiveRecord.Framework.Internal.Tests/Model/HasManyWithFetch.cs diff --git a/ActiveRecord/Castle.ActiveRecord.Framework.Internal.Tests/Castle.ActiveRecord.Framework.Internal.Tests-vs2003.csproj b/ActiveRecord/Castle.ActiveRecord.Framework.Internal.Tests/Castle.ActiveRecord.Framework.Internal.Tests-vs2003.csproj index 95d8a2c0b..682a62e7d 100644 --- a/ActiveRecord/Castle.ActiveRecord.Framework.Internal.Tests/Castle.ActiveRecord.Framework.Internal.Tests-vs2003.csproj +++ b/ActiveRecord/Castle.ActiveRecord.Framework.Internal.Tests/Castle.ActiveRecord.Framework.Internal.Tests-vs2003.csproj @@ -158,6 +158,11 @@ BuildAction = "Compile" /> + + + diff --git a/ActiveRecord/Castle.ActiveRecord.Framework.Internal.Tests/Castle.ActiveRecord.Framework.Internal.Tests-vs2005.csproj b/ActiveRecord/Castle.ActiveRecord.Framework.Internal.Tests/Castle.ActiveRecord.Framework.Internal.Tests-vs2005.csproj index 73bd906ee..402c2c17d 100644 --- a/ActiveRecord/Castle.ActiveRecord.Framework.Internal.Tests/Castle.ActiveRecord.Framework.Internal.Tests-vs2005.csproj +++ b/ActiveRecord/Castle.ActiveRecord.Framework.Internal.Tests/Castle.ActiveRecord.Framework.Internal.Tests-vs2005.csproj @@ -139,6 +139,7 @@ + @@ -186,6 +187,7 @@ + diff --git a/ActiveRecord/Castle.ActiveRecord.Framework.Internal.Tests/Model/BelongsToWithFetch.cs b/ActiveRecord/Castle.ActiveRecord.Framework.Internal.Tests/Model/BelongsToWithFetch.cs new file mode 100644 index 000000000..bd04f25af --- /dev/null +++ b/ActiveRecord/Castle.ActiveRecord.Framework.Internal.Tests/Model/BelongsToWithFetch.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Castle.ActiveRecord.Framework.Internal.Tests.Model +{ + class BelongsToWithFetch + { + } +} + +// Copyright 2004-2007 Castle Project - http://www.castleproject.org/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Castle.ActiveRecord.Framework.Internal.Tests.Model +{ + using System; + + [ActiveRecord(Lazy = false)] + public class BelongsToClassAFetch : ActiveRecordBase + { + private int id; + private ClassA classA; + + [PrimaryKey] + public int Id + { + get { return id; } + set { id = value; } + } + + [BelongsTo("classa_id", Fetch=FetchEnum.Join)] + public ClassA ClassA + { + get { return classA; } + set { classA = value; } + } + } +} diff --git a/ActiveRecord/Castle.ActiveRecord.Framework.Internal.Tests/Model/HasManyWithFetch.cs b/ActiveRecord/Castle.ActiveRecord.Framework.Internal.Tests/Model/HasManyWithFetch.cs new file mode 100644 index 000000000..d66aff155 --- /dev/null +++ b/ActiveRecord/Castle.ActiveRecord.Framework.Internal.Tests/Model/HasManyWithFetch.cs @@ -0,0 +1,40 @@ +// Copyright 2004-2007 Castle Project - http://www.castleproject.org/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Castle.ActiveRecord.Framework.Internal.Tests.Model +{ + using System; + using System.Collections; + + [ActiveRecord(Lazy = false)] + public class HasManyWithFetch : ActiveRecordBase + { + private int id; + private IList list; + + [PrimaryKey] + public int Id + { + get { return id; } + set { id = value; } + } + + [HasMany(typeof(ClassA), "keycol", "ClassATable", Inverse = true, Fetch=FetchEnum.Join)] + public IList Items + { + get { return list; } + set { list = value; } + } + } +} diff --git a/ActiveRecord/Castle.ActiveRecord.Framework.Internal.Tests/XmlGenerationTestCase.cs b/ActiveRecord/Castle.ActiveRecord.Framework.Internal.Tests/XmlGenerationTestCase.cs index 14dd6d155..dd99a549b 100644 --- a/ActiveRecord/Castle.ActiveRecord.Framework.Internal.Tests/XmlGenerationTestCase.cs +++ b/ActiveRecord/Castle.ActiveRecord.Framework.Internal.Tests/XmlGenerationTestCase.cs @@ -709,6 +709,30 @@ namespace Castle.ActiveRecord.Framework.Internal.Tests } [Test] + public void BelongsToWithFetch() + { + ActiveRecordModelBuilder builder = new ActiveRecordModelBuilder(); + ActiveRecordModel model = builder.Create(typeof(BelongsToClassAFetch)); + Assert.IsNotNull(model); + + String xml = Process(builder, model); + + String expected = + "\r\n" + + "\r\n" + + " \r\n" + + " \r\n" + + " \r\n" + + " \r\n" + + " \r\n" + + " \r\n" + + " \r\n" + + "\r\n"; + + Assert.AreEqual(expected, xml); + } + + [Test] public void HasManyWithExplicitInfo() { ActiveRecordModelBuilder builder = new ActiveRecordModelBuilder(); @@ -736,6 +760,33 @@ namespace Castle.ActiveRecord.Framework.Internal.Tests } [Test] + public void HasManyWithFetch() + { + ActiveRecordModelBuilder builder = new ActiveRecordModelBuilder(); + ActiveRecordModel model = builder.Create(typeof(HasManyWithFetch)); + Assert.IsNotNull(model); + + String xml = Process(builder, model); + + String expected = + "\r\n" + + "\r\n" + + " \r\n" + + " \r\n" + + " \r\n" + + " \r\n" + + " \r\n" + + " \r\n" + + " \r\n" + + " \r\n" + + " \r\n" + + " \r\n" + + "\r\n"; + + Assert.AreEqual(expected, xml); + } + + [Test] public void HasManyInfering() { ActiveRecordModelBuilder builder = new ActiveRecordModelBuilder(); @@ -1065,4 +1116,4 @@ namespace Castle.ActiveRecord.Framework.Internal.Tests Assert.AreEqual(expected, xml); } } -} \ No newline at end of file +} diff --git a/ActiveRecord/Castle.ActiveRecord/Attributes/BelongsToAttribute.cs b/ActiveRecord/Castle.ActiveRecord/Attributes/BelongsToAttribute.cs index 2475482f5..f6b3ff914 100644 --- a/ActiveRecord/Castle.ActiveRecord/Attributes/BelongsToAttribute.cs +++ b/ActiveRecord/Castle.ActiveRecord/Attributes/BelongsToAttribute.cs @@ -70,7 +70,7 @@ namespace Castle.ActiveRecord private bool insert = true; private bool notnull; private bool unique; - private OuterJoinEnum outerJoin = OuterJoinEnum.Auto; + private FetchEnum fetchMethod = FetchEnum.Unspecified; private CascadeEnum cascade = CascadeEnum.None; private NotFoundBehaviour notFoundBehaviour = NotFoundBehaviour.Default; @@ -128,11 +128,56 @@ namespace Castle.ActiveRecord /// /// Defines the outer join behavior of this association. + /// NHibernate has deprecated the outer-join attribute so this property is + /// marked obsolete - it now converts to and from the fetch value. /// + [Obsolete("Use the Fetch property instead")] public OuterJoinEnum OuterJoin { - get { return outerJoin; } - set { outerJoin = value; } + get + { + OuterJoinEnum returnValue = OuterJoinEnum.Auto; + + switch (fetchMethod) + { + case FetchEnum.Unspecified: + returnValue = OuterJoinEnum.Auto; + break; + case FetchEnum.Join: + returnValue = OuterJoinEnum.True; + break; + case FetchEnum.Select: + returnValue = OuterJoinEnum.False; + break; + } + + return returnValue; + } + set + { + switch (value) + { + case OuterJoinEnum.Auto: + fetchMethod = FetchEnum.Unspecified; + break; + case OuterJoinEnum.True: + fetchMethod = FetchEnum.Join; + break; + case OuterJoinEnum.False: + fetchMethod = FetchEnum.Select; + break; + } + } + } + + /// + /// Chooses between outer-join fetching + /// or sequential select fetching. + /// + public FetchEnum Fetch + { + get { return fetchMethod; } + set { fetchMethod = value; } } /// diff --git a/ActiveRecord/Castle.ActiveRecord/Attributes/Enums.cs b/ActiveRecord/Castle.ActiveRecord/Attributes/Enums.cs index 17c21d584..7c7cb4960 100644 --- a/ActiveRecord/Castle.ActiveRecord/Attributes/Enums.cs +++ b/ActiveRecord/Castle.ActiveRecord/Attributes/Enums.cs @@ -105,6 +105,25 @@ namespace Castle.ActiveRecord } /// + /// Define the possible fetch option values + /// + public enum FetchEnum + { + /// + /// Let NHibernate decide what to do here + /// + Unspecified, + /// + /// Use a JOIN to load the data + /// + Join, + /// + /// Use a seperate SELECT statement to load the data + /// + Select + } + + /// /// Defines the cascading behaviour of this association. /// /// diff --git a/ActiveRecord/Castle.ActiveRecord/Attributes/HasAndBelongsToManyAttribute.cs b/ActiveRecord/Castle.ActiveRecord/Attributes/HasAndBelongsToManyAttribute.cs index 84376eb31..64b80ddfa 100644 --- a/ActiveRecord/Castle.ActiveRecord/Attributes/HasAndBelongsToManyAttribute.cs +++ b/ActiveRecord/Castle.ActiveRecord/Attributes/HasAndBelongsToManyAttribute.cs @@ -43,6 +43,7 @@ namespace Castle.ActiveRecord private String[] compositeKeyColumnRefs; private String columnKey; private String[] compositeKeyColumnKeys; + private FetchEnum fetchMethod = FetchEnum.Unspecified; /// /// Initializes a new instance of the class. @@ -110,5 +111,15 @@ namespace Castle.ActiveRecord get { return compositeKeyColumnKeys; } set { compositeKeyColumnKeys = value; } } + + /// + /// Chooses between outer-join fetching + /// or sequential select fetching. + /// + public FetchEnum Fetch + { + get { return fetchMethod; } + set { fetchMethod = value; } + } } } diff --git a/ActiveRecord/Castle.ActiveRecord/Attributes/HasManyAttribute.cs b/ActiveRecord/Castle.ActiveRecord/Attributes/HasManyAttribute.cs index 839c2e30f..9642bfa70 100644 --- a/ActiveRecord/Castle.ActiveRecord/Attributes/HasManyAttribute.cs +++ b/ActiveRecord/Castle.ActiveRecord/Attributes/HasManyAttribute.cs @@ -51,6 +51,11 @@ namespace Castle.ActiveRecord protected bool hasDependentObjects; /// + /// Whether we do outer join fetching for this collection + /// + protected FetchEnum fetchMethod = FetchEnum.Unspecified; + + /// /// Initializes a new instance of the class. /// public HasManyAttribute() @@ -109,5 +114,15 @@ namespace Castle.ActiveRecord get { return hasDependentObjects; } set { hasDependentObjects = value; } } + + /// + /// Chooses between outer-join fetching + /// or sequential select fetching. + /// + public FetchEnum Fetch + { + get { return fetchMethod; } + set { fetchMethod = value; } + } } } diff --git a/ActiveRecord/Castle.ActiveRecord/Attributes/OneToOneAttribute.cs b/ActiveRecord/Castle.ActiveRecord/Attributes/OneToOneAttribute.cs index 1460698fc..3b660baf5 100644 --- a/ActiveRecord/Castle.ActiveRecord/Attributes/OneToOneAttribute.cs +++ b/ActiveRecord/Castle.ActiveRecord/Attributes/OneToOneAttribute.cs @@ -17,25 +17,6 @@ namespace Castle.ActiveRecord using System; /// - /// Define the possible fetch option values - /// - public enum FetchEnum - { - /// - /// Let NHibernate decide what to do here - /// - Unspecified, - /// - /// Use a JOIN to load the data - /// - Join, - /// - /// Use a seperate SELECT statement to load the data - /// - Select - } - - /// /// Associates a foreign table where the current class /// and the target class share their primary key. /// diff --git a/ActiveRecord/Castle.ActiveRecord/Framework/Internal/Visitors/XmlGenerationVisitor.cs b/ActiveRecord/Castle.ActiveRecord/Framework/Internal/Visitors/XmlGenerationVisitor.cs index 12ee85190..a973b300f 100644 --- a/ActiveRecord/Castle.ActiveRecord/Framework/Internal/Visitors/XmlGenerationVisitor.cs +++ b/ActiveRecord/Castle.ActiveRecord/Framework/Internal/Visitors/XmlGenerationVisitor.cs @@ -431,7 +431,7 @@ namespace Castle.ActiveRecord.Framework.Internal model.HasManyToAnyAtt.AccessString, att.Table, att.Schema, att.Lazy, att.Inverse, att.OrderBy, att.Where, att.Sort, att.ColumnKey, null, null, null, null, model.Configuration, att.Index, att.IndexType, - att.Cache, att.NotFoundBehaviour); + att.Cache, att.NotFoundBehaviour, att.Fetch); } /// @@ -529,7 +529,7 @@ namespace Castle.ActiveRecord.Framework.Internal public override void VisitBelongsTo(BelongsToModel model) { String cascade = TranslateCascadeEnum(model.BelongsToAtt.Cascade); - String outerJoin = TranslateOuterJoin(model.BelongsToAtt.OuterJoin); + String fetch = TranslateFetch(model.BelongsToAtt.Fetch); String notFoundMode = TranslateNotFoundBehaviourEnum(model.BelongsToAtt.NotFoundBehaviour); if (model.BelongsToAtt.Column == null) @@ -543,7 +543,7 @@ namespace Castle.ActiveRecord.Framework.Internal WriteIfTrue("not-null", model.BelongsToAtt.NotNull), WriteIfTrue("unique", model.BelongsToAtt.Unique), WriteIfNonNull("cascade", cascade), - WriteIfNonNull("outer-join", outerJoin), + WriteIfNonNull("fetch", fetch), WriteIfNonNull("not-found", notFoundMode)); Ident(); WriteCompositeColumns(model.BelongsToAtt.CompositeKeyColumns); @@ -562,7 +562,7 @@ namespace Castle.ActiveRecord.Framework.Internal WriteIfTrue("not-null", model.BelongsToAtt.NotNull), WriteIfTrue("unique", model.BelongsToAtt.Unique), WriteIfNonNull("cascade", cascade), - WriteIfNonNull("outer-join", outerJoin), + WriteIfNonNull("fetch", fetch), WriteIfNonNull("not-found", notFoundMode)); } } @@ -580,7 +580,7 @@ namespace Castle.ActiveRecord.Framework.Internal model.HasManyAtt.AccessString, att.Table, att.Schema, att.Lazy, att.Inverse, att.OrderBy, att.Where, att.Sort, att.ColumnKey, att.CompositeKeyColumnKeys, att.Element, null, null, model.DependentObjectModel, att.Index, att.IndexType, - att.Cache, att.NotFoundBehaviour); + att.Cache, att.NotFoundBehaviour, att.Fetch); } /// @@ -596,7 +596,7 @@ namespace Castle.ActiveRecord.Framework.Internal att.AccessString, att.Table, att.Schema, att.Lazy, att.Inverse, att.OrderBy, att.Where, att.Sort, att.ColumnKey, att.CompositeKeyColumnKeys, null, att.ColumnRef, att.CompositeKeyColumnRefs, model.CollectionID, att.Index, att.IndexType, att.Cache, - att.NotFoundBehaviour); + att.NotFoundBehaviour, att.Fetch); } /// @@ -669,10 +669,11 @@ namespace Castle.ActiveRecord.Framework.Internal string columnKey, string[] compositeKeyColumnKeys, string element, string columnRef, string[] compositeKeyColumnRefs, IVisitable extraModel, string index, string indexType, CacheEnum cache, - NotFoundBehaviour notFoundBehaviour) + NotFoundBehaviour notFoundBehaviour, FetchEnum fetch) { String cascade = TranslateCascadeEnum(cascadeEnum); String notFoundMode = TranslateNotFoundBehaviourEnum(notFoundBehaviour); + String fetchString = TranslateFetch(fetch); String closingTag = null; @@ -683,7 +684,7 @@ namespace Castle.ActiveRecord.Framework.Internal { closingTag = ""; - AppendF("", + AppendF("", MakeAtt("name", name), MakeAtt("access", accessString), WriteIfNonNull("table", table), @@ -692,13 +693,14 @@ namespace Castle.ActiveRecord.Framework.Internal WriteIfTrue("inverse", inverse), WriteIfNonNull("cascade", cascade), WriteIfNonNull("order-by", orderBy), - WriteIfNonNull("where", where)); + WriteIfNonNull("where", where), + WriteIfNonNull("fetch", fetchString)); } else if (type == RelationType.Set) { closingTag = ""; - AppendF("", + AppendF("", MakeAtt("name", name), MakeAtt("access", accessString), WriteIfNonNull("table", table), @@ -708,13 +710,14 @@ namespace Castle.ActiveRecord.Framework.Internal WriteIfNonNull("cascade", cascade), WriteIfNonNull("order-by", orderBy), WriteIfNonNull("where", where), - WriteIfNonNull("sort", sort)); + WriteIfNonNull("sort", sort), + WriteIfNonNull("fetch", fetchString)); } else if (type == RelationType.IdBag) { closingTag = ""; - AppendF("", + AppendF("", MakeAtt("name", name), MakeAtt("access", accessString), WriteIfNonNull("table", table), @@ -722,7 +725,8 @@ namespace Castle.ActiveRecord.Framework.Internal MakeAtt("lazy", lazy), WriteIfNonNull("cascade", cascade), WriteIfNonNull("order-by", orderBy), - WriteIfNonNull("where", where)); + WriteIfNonNull("where", where), + WriteIfNonNull("fetch", fetchString)); VisitNode(extraModel); } @@ -730,7 +734,7 @@ namespace Castle.ActiveRecord.Framework.Internal { closingTag = ""; - AppendF("", + AppendF("", MakeAtt("name", name), MakeAtt("access", accessString), WriteIfNonNull("table", table), @@ -740,12 +744,13 @@ namespace Castle.ActiveRecord.Framework.Internal WriteIfNonNull("cascade", cascade), WriteIfNonNull("order-by", orderBy), WriteIfNonNull("where", where), - WriteIfNonNull("sort", sort)); + WriteIfNonNull("sort", sort), + WriteIfNonNull("fetch", fetchString)); } else if (type == RelationType.List) { closingTag = ""; - AppendF("", + AppendF("", MakeAtt("name", name), MakeAtt("access", accessString), WriteIfNonNull("table", table), @@ -753,7 +758,8 @@ namespace Castle.ActiveRecord.Framework.Internal MakeAtt("lazy", lazy), WriteIfTrue("inverse", inverse), WriteIfNonNull("cascade", cascade), - WriteIfNonNull("where", where)); + WriteIfNonNull("where", where), + WriteIfNonNull("fetch", fetchString)); } @@ -894,18 +900,6 @@ namespace Castle.ActiveRecord.Framework.Internal } } - private static string TranslateOuterJoin(OuterJoinEnum ojEnum) - { - String outerJoin = null; - - if (ojEnum != OuterJoinEnum.Auto) - { - outerJoin = ojEnum.ToString().ToLower(); - } - - return outerJoin; - } - private String MakeTypeAtt(Type type, String typeName) { if (typeName != null) @@ -1189,4 +1183,4 @@ namespace Castle.ActiveRecord.Framework.Internal #endregion } -} \ No newline at end of file +} -- 2.11.4.GIT