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
.ActiveRecord
18 using System
.Collections
;
19 using System
.Collections
.Generic
;
20 using System
.Reflection
;
21 using Castle
.ActiveRecord
.Framework
.Internal
;
22 using Castle
.Components
.Validator
;
25 /// Extends <see cref="ActiveRecordBase"/> adding automatic validation support.
29 /// using Castle.Components.Validator;
31 /// public class Customer : ActiveRecordBase
35 /// [Property, ValidateNonEmpty]
38 /// get { return _name; }
39 /// set { _name = value; }
42 /// [Property, ValidateNonEmpty, ValidateEmail]
45 /// get { return _email; }
46 /// set { _email = value; }
51 public abstract class ActiveRecordValidationBase
: ActiveRecordBase
54 private ValidatorRunner __runner
= new ValidatorRunner(ActiveRecordModelBuilder
.ValidatorRegistry
);
56 [System
.Xml
.Serialization
.XmlIgnore
]
57 private IDictionary __failedProperties
;
60 /// Constructs an ActiveRecordValidationBase
62 public ActiveRecordValidationBase()
67 /// Performs the fields validation. Returns true if no
68 /// validation error was found.
70 /// <returns></returns>
71 public virtual bool IsValid()
73 return IsValid(RunWhen
.Everytime
);
77 /// Performs the fields validation for the specified action.
79 /// <param name="runWhen">Use validators appropriate to the action being performed.</param>
80 /// <returns>True if no validation error was found</returns>
81 public virtual bool IsValid(RunWhen runWhen
)
83 __failedProperties
= new Hashtable();
87 __runner
= new ValidatorRunner(ActiveRecordModelBuilder
.ValidatorRegistry
);
90 bool returnValue
= __runner
.IsValid(this, runWhen
);
92 foreach (PropertyInfo propinfo
in GetNestedPropertiesToValidate(this)) {
93 object propval
= propinfo
.GetValue(this, null);
95 if (propval
!= null) {
96 bool tmp
= __runner
.IsValid(propval
, runWhen
);
98 __failedProperties
.Add(propinfo
,
99 new ArrayList(__runner
.GetErrorSummary(propval
).GetErrorsForProperty(propinfo
.Name
)));
106 Type type
= GetType();
107 ErrorSummary summary
= __runner
.GetErrorSummary(this);
109 foreach(string property
in summary
.InvalidProperties
)
111 __failedProperties
.Add(type
.GetProperty(property
), new ArrayList(summary
.GetErrorsForProperty(property
)));
119 /// Returns a list of current validation errors messages.
121 public virtual String
[] ValidationErrorMessages
125 if (__runner
== null)
127 __runner
= new ValidatorRunner(ActiveRecordModelBuilder
.ValidatorRegistry
);
130 if (__runner
.GetErrorSummary(this) == null)
135 List
<string> errorMessages
= new List
<string>(__runner
.GetErrorSummary(this).ErrorMessages
);
137 AddNestedPropertyValidationErrorMessages(errorMessages
, this, __runner
);
139 return errorMessages
.ToArray();
144 /// Maps a specific PropertyInfo to a list of
145 /// error messages. Useful for frameworks.
147 [System
.Xml
.Serialization
.XmlIgnore
]
148 public virtual IDictionary PropertiesValidationErrorMessage
150 get { return __failedProperties; }
154 /// Override the base hook to call validators required for create.
156 /// <param name="state">The current state of the object</param>
157 /// <returns>Returns true if the state has changed otherwise false</returns>
158 protected internal override bool BeforeSave(IDictionary state
)
160 if (!IsValid(RunWhen
.Insert
))
165 return base.BeforeSave(state
);
169 /// Override the base hook to call validators required for update.
171 /// <param name="id">object id</param>
172 /// <param name="previousState">The previous state of the object</param>
173 /// <param name="currentState">The current state of the object</param>
174 /// <param name="types">Property types</param>
175 /// <returns>Returns true if the state has changed otherwise false</returns>
176 protected internal override bool OnFlushDirty(object id
, IDictionary previousState
, IDictionary currentState
, NHibernate
.Type
.IType
[] types
)
178 if (!IsValid(RunWhen
.Update
))
183 return base.OnFlushDirty(id
, previousState
, currentState
, types
);
187 /// Throws an exception explaining why the save or update
188 /// cannot be executed when fields are not ok to pass.
191 /// You can override this method to declare a better behavior.
193 protected virtual void OnNotValid()
195 throw new ValidationException("Can't save or update as there is one (or more) " +
196 "field that has not passed the validation test", ValidationErrorMessages
);
199 internal static IEnumerable
GetNestedPropertiesToValidate(object instance
)
201 Type type
= instance
.GetType();
202 ActiveRecordModel me
= GetModel(type
);
204 foreach (NestedModel component
in me
.Components
) {
205 yield return component
.Property
;
209 internal static void AddNestedPropertyValidationErrorMessages(List
<string> errorMessages
, object instance
, ValidatorRunner runner
) {
210 foreach (PropertyInfo propinfo
in GetNestedPropertiesToValidate(instance
)) {
211 object propval
= propinfo
.GetValue(instance
, null);
212 if (propval
!= null) {
213 errorMessages
.AddRange(
214 runner
.GetErrorSummary(propval
).ErrorMessages
);