1 // Copyright 2004-2008 Castle Project - http://www.castleproject.org/
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
7 // http://www.apache.org/licenses/LICENSE-2.0
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
15 namespace Castle
.MonoRail
.ActiveRecordSupport
18 using System
.Reflection
;
21 using NHibernate
.Expressions
;
23 using Castle
.ActiveRecord
;
24 using Castle
.ActiveRecord
.Framework
.Internal
;
25 using Castle
.Components
.Binder
;
26 using Castle
.MonoRail
.Framework
;
29 /// Class responsible on loading records for parameters marked with the <see cref="ARFetchAttribute" />.
31 public class ARFetcher
33 private readonly IConverter converter
;
35 public ARFetcher(IConverter converter
)
37 this.converter
= converter
;
40 public object FetchActiveRecord(ParameterInfo param
, ARFetchAttribute attr
, IRequest request
)
42 Type type
= param
.ParameterType
;
44 bool isArray
= type
.IsArray
;
46 if (isArray
) type
= type
.GetElementType();
48 ActiveRecordModel model
= ActiveRecordModel
.GetModel(type
);
52 throw new MonoRailException(String
.Format("'{0}' is not an ActiveRecord " +
53 "class. It could not be bound to an [ARFetch] attribute.", type
.Name
));
56 if (model
.CompositeKey
!= null)
58 throw new MonoRailException("ARFetch only supports single-attribute primary keys");
61 String webParamName
= attr
.RequestParameterName
!= null ? attr
.RequestParameterName
: param
.Name
;
65 return LoadActiveRecord(type
, request
.Params
[webParamName
], attr
, model
);
68 object[] pks
= request
.Params
.GetValues(webParamName
);
75 Array objs
= Array
.CreateInstance(type
, pks
.Length
);
77 for(int i
= 0; i
< objs
.Length
; i
++)
79 objs
.SetValue(LoadActiveRecord(type
, pks
[i
], attr
, model
), i
);
85 private object LoadActiveRecord(Type type
, object pk
, ARFetchAttribute attr
, ActiveRecordModel model
)
87 object instance
= null;
89 if (pk
!= null && !String
.Empty
.Equals(pk
))
91 PrimaryKeyModel pkModel
= ObtainPrimaryKey(model
);
93 Type pkType
= pkModel
.Property
.PropertyType
;
95 bool conversionSucceeded
;
96 object convertedPk
= converter
.Convert(pkType
, pk
.GetType(), pk
, out conversionSucceeded
);
98 if (!conversionSucceeded
)
100 throw new MonoRailException("ARFetcher could not convert PK {0} to type {1}", pk
, pkType
);
103 if (attr
.Eager
== null || attr
.Eager
.Length
== 0)
106 instance
= ActiveRecordMediator
.FindByPrimaryKey(type
, convertedPk
, attr
.Required
);
110 // load using eager fetching of lazy collections
111 DetachedCriteria criteria
= DetachedCriteria
.For(type
);
112 criteria
.Add(Expression
.Eq(pkModel
.Property
.Name
, convertedPk
));
113 foreach (string associationToEagerFetch
in attr
.Eager
.Split(','))
115 string clean
= associationToEagerFetch
.Trim();
116 if (clean
.Length
== 0)
121 criteria
.SetFetchMode(clean
, FetchMode
.Eager
);
124 object[] result
= (object[]) ActiveRecordMediator
.FindAll(type
, criteria
);
125 if (result
.Length
> 0)
126 instance
= result
[0];
130 if (instance
== null && attr
.Create
)
132 instance
= Activator
.CreateInstance(type
);
138 private static PrimaryKeyModel
ObtainPrimaryKey(ActiveRecordModel model
)
140 if (model
.IsJoinedSubClass
|| model
.IsDiscriminatorSubClass
)
142 return ObtainPrimaryKey(model
.Parent
);
144 return model
.PrimaryKey
;