CSLA.NET 5.4.2
CSLA .NET is a software development framework that helps you build a reusable, maintainable object-oriented business layer for your app.
FieldDataManager.cs
Go to the documentation of this file.
1//-----------------------------------------------------------------------
2// <copyright file="FieldDataManager.cs" company="Marimer LLC">
3// Copyright (c) Marimer LLC. All rights reserved.
4// Website: https://cslanet.com
5// </copyright>
6// <summary>Manages properties and property data for</summary>
7//-----------------------------------------------------------------------
8using System;
9using System.Linq;
10using System.IO;
11using System.Collections.Generic;
12using Csla.Properties;
14using Csla.Reflection;
15using System.Reflection;
17
19{
25#if TESTING
26 [System.Diagnostics.DebuggerStepThrough]
27#endif
30 {
31 private string _businessObjectType;
32 [NonSerialized]
33 BusinessBase _parent;
34 [NonSerialized()]
35 private List<IPropertyInfo> _propertyList;
36 private IFieldData[] _fieldData;
37
41 public FieldDataManager() { }
42
43 internal FieldDataManager(Type businessObjectType)
44 {
45 SetPropertyList(businessObjectType);
46 _fieldData = new IFieldData[_propertyList.Count];
47 }
48
53 internal void SetPropertyList(Type businessObjectType)
54 {
55 _businessObjectType = businessObjectType.AssemblyQualifiedName;
56 _propertyList = GetConsolidatedList(businessObjectType);
57 }
58
62 internal void SetParent(BusinessBase parent)
63 {
64 _parent = parent;
65 }
66
73 public List<IPropertyInfo> GetRegisteredProperties()
74 {
75 return new List<IPropertyInfo>(_propertyList);
76 }
77
86 public IPropertyInfo GetRegisteredProperty(string propertyName)
87 {
88 var result = GetRegisteredProperties().Where(c => c.Name == propertyName).FirstOrDefault();
89 if (result == null)
90 throw new ArgumentOutOfRangeException(string.Format(Resources.PropertyNameNotRegisteredException, propertyName));
91 return result;
92 }
93
98 public bool HasFields
99 {
100 get { return _propertyList.Count > 0; }
101 }
102
103 #region ConsolidatedPropertyList
104
105 private static Dictionary<Type, List<IPropertyInfo>> _consolidatedLists = new Dictionary<Type, List<IPropertyInfo>>();
106
107 private static List<IPropertyInfo> GetConsolidatedList(Type type)
108 {
109 List<IPropertyInfo> result = null;
110 var found = false;
111 try
112 {
113 found = _consolidatedLists.TryGetValue(type, out result);
114 }
115 catch
116 { /* failure will drop into !found block */ }
117 if (!found)
118 {
119 lock (_consolidatedLists)
120 {
121 if (_consolidatedLists.ContainsKey(type))
122 {
123 result = _consolidatedLists[type];
124 }
125 else
126 {
127 result = CreateConsolidatedList(type);
128 _consolidatedLists.Add(type, result);
129 }
130 }
131 }
132 return result;
133 }
134
135 private static List<IPropertyInfo> CreateConsolidatedList(Type type)
136 {
138 List<IPropertyInfo> result = new List<IPropertyInfo>();
139
140 // get inheritance hierarchy
141 Type current = type;
142 List<Type> hierarchy = new List<Type>();
143 do
144 {
145 hierarchy.Add(current);
146 current = current.BaseType;
147 } while (current != null && !current.Equals(typeof(BusinessBase)));
148
149 // walk from top to bottom to build consolidated list
150 for (int index = hierarchy.Count - 1; index >= 0; index--)
151 {
152 var source = PropertyInfoManager.GetPropertyListCache(hierarchy[index]);
153 source.IsLocked = true;
154 result.AddRange(source);
155 }
156
157 // set Index properties on all unindexed PropertyInfo objects
158 int max = -1;
159 foreach (var item in result)
160 {
161 if (item.Index == -1)
162 {
163 max++;
164 item.Index = max;
165 }
166 else
167 {
168 max = item.Index;
169 }
170 }
171 // return consolidated list
172 return result;
173 }
174
175 #endregion
176
177 #region Get/Set/Find fields
178
186 [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Advanced)]
188 {
189 if ((propertyInfo.RelationshipType & RelationshipTypes.PrivateField) == RelationshipTypes.PrivateField)
190 throw new InvalidOperationException(Resources.PropertyIsPrivateField);
191
192 try
193 {
194 return _fieldData[propertyInfo.Index];
195 }
196 catch (IndexOutOfRangeException ex)
197 {
198 throw new InvalidOperationException(Resources.PropertyNotRegistered, ex);
199 }
200 }
201
202 private IFieldData GetOrCreateFieldData(IPropertyInfo prop)
203 {
204 try
205 {
206 var field = _fieldData[prop.Index];
207 if (field == null)
208 {
209 field = prop.NewFieldData(prop.Name);
210 _fieldData[prop.Index] = field;
211 }
212 return field;
213 }
214 catch (IndexOutOfRangeException ex)
215 {
216 throw new InvalidOperationException(Resources.PropertyNotRegistered, ex);
217 }
218 }
219
220 internal IPropertyInfo FindProperty(object value)
221 {
222 var index = 0;
223 foreach (var item in _fieldData)
224 {
225 if (item != null && item.Value != null && item.Value.Equals(value))
226 return _propertyList[index];
227 index += 1;
228 }
229 return null;
230 }
231
241 internal void SetFieldData(IPropertyInfo prop, object value)
242 {
243 Type valueType;
244 if (value != null)
245 valueType = value.GetType();
246 else
247 valueType = prop.Type;
248 value = Utilities.CoerceValue(prop.Type, valueType, null, value);
249 var field = GetOrCreateFieldData(prop);
250 field.Value = value;
251 }
252
265 internal void SetFieldData<P>(IPropertyInfo prop, P value)
266 {
267 var field = GetOrCreateFieldData(prop);
268 if (field is IFieldData<P> fd)
269 fd.Value = value;
270 else
271 field.Value = value;
272 }
273
284 internal IFieldData LoadFieldData(IPropertyInfo prop, object value)
285 {
286 Type valueType;
287 if (value != null)
288 valueType = value.GetType();
289 else
290 valueType = prop.Type;
291 value = Utilities.CoerceValue(prop.Type, valueType, null, value);
292 var field = GetOrCreateFieldData(prop);
293 field.Value = value;
294 field.MarkClean();
295 return field;
296 }
297
311 internal IFieldData LoadFieldData<P>(IPropertyInfo prop, P value)
312 {
313 var field = GetOrCreateFieldData(prop);
314 var fd = field as IFieldData<P>;
315 if (fd != null)
316 fd.Value = value;
317 else
318 field.Value = value;
319 field.MarkClean();
320 return field;
321 }
322
331 internal void RemoveField(IPropertyInfo prop)
332 {
333 try
334 {
335 var field = _fieldData[prop.Index];
336 if (field != null)
337 field.Value = null;
338 }
339 catch (IndexOutOfRangeException ex)
340 {
341 throw new InvalidOperationException(Resources.PropertyNotRegistered, ex);
342 }
343 }
344
353 public bool FieldExists(IPropertyInfo propertyInfo)
354 {
355 try
356 {
357 return _fieldData[propertyInfo.Index] != null;
358 }
359 catch (IndexOutOfRangeException ex)
360 {
361 throw new InvalidOperationException(Resources.PropertyNotRegistered, ex);
362 }
363 }
364
373 public bool IsFieldDirty(IPropertyInfo propertyInfo)
374 {
375 try
376 {
377 bool result = false;
378 var field = _fieldData[propertyInfo.Index];
379 if (field != null)
380 result = field.IsDirty;
381 else
382 result = false;
383 return result;
384
385 }
386 catch (IndexOutOfRangeException ex)
387 {
388 throw new InvalidOperationException(Properties.Resources.PropertyNotRegistered, ex);
389 }
390 }
391
392 #endregion
393
394 #region IsValid/IsDirty/IsBusy
395
400 public bool IsValid()
401 {
402 foreach (var item in _fieldData)
403 if (item != null && !item.IsValid)
404 return false;
405 return true;
406 }
407
412 public bool IsDirty()
413 {
414 foreach (var item in _fieldData)
415 if (item != null && item.IsDirty)
416 return true;
417 return false;
418 }
419
424 internal void MarkClean()
425 {
426 foreach (var item in _fieldData)
427 if (item != null && item.IsDirty)
428 item.MarkClean();
429 }
430
431 internal bool IsBusy()
432 {
433 foreach (var item in _fieldData)
434 if (item != null && item.IsBusy)
435 return true;
436 return false;
437 }
438
439 #endregion
440
441 #region IUndoableObject
442
443 private readonly Stack<byte[]> _stateStack = new Stack<byte[]>();
444
448 public int EditLevel
449 {
450 get { return _stateStack.Count; }
451 }
452
453 void Core.IUndoableObject.CopyState(int parentEditLevel, bool parentBindingEdit)
454 {
455 if (this.EditLevel + 1 > parentEditLevel)
456 throw new UndoException(string.Format(Resources.EditLevelMismatchException, "CopyState"), this.GetType().Name, _parent != null ? _parent.GetType().Name : null, this.EditLevel, parentEditLevel - 1);
457
458 IFieldData[] state = new IFieldData[_propertyList.Count];
459
460 for (var index = 0; index < _fieldData.Length; index++)
461 {
462 var item = _fieldData[index];
463 if (item != null)
464 {
465 if (item.Value is IUndoableObject child)
466 {
467 // cascade call to child
468 child.CopyState(parentEditLevel, parentBindingEdit);
469 // indicate that there was a value here
470 state[index] = new FieldData<bool>(item.Name);
471 }
472 else
473 {
474 // add the IFieldData object
475 state[index] = item;
476 }
477 }
478 }
479
480 // serialize the state and stack it
481 using (MemoryStream buffer = new MemoryStream())
482 {
483 var formatter = SerializationFormatterFactory.GetFormatter();
484 var stateList = new MobileList<IFieldData>(state.ToList());
485 formatter.Serialize(buffer, stateList);
486 _stateStack.Push(buffer.ToArray());
487 }
488 }
489
490 void Core.IUndoableObject.UndoChanges(int parentEditLevel, bool parentBindingEdit)
491 {
492 if (EditLevel > 0)
493 {
494 if (this.EditLevel - 1 != parentEditLevel)
495 throw new UndoException(string.Format(Resources.EditLevelMismatchException, "UndoChanges"), this.GetType().Name, _parent != null ? _parent.GetType().Name : null, this.EditLevel, parentEditLevel + 1);
496
497 IFieldData[] state = null;
498 using (MemoryStream buffer = new MemoryStream(_stateStack.Pop()))
499 {
500 buffer.Position = 0;
501 var formatter = SerializationFormatterFactory.GetFormatter();
502 state = ((MobileList<IFieldData>)(formatter.Deserialize(buffer))).ToArray();
503 }
504
505 for (var index = 0; index < _fieldData.Length; index++)
506 {
507 var oldItem = state[index];
508 var item = _fieldData[index];
509 if (item != null)
510 {
511 var undoable = item.Value as IUndoableObject;
512 if (undoable != null)
513 {
514 // current value is undoable
515 if (oldItem != null)
516 undoable.UndoChanges(parentEditLevel, parentBindingEdit);
517 else
518 _fieldData[index] = null;
519 continue;
520 }
521 }
522 // restore IFieldData object into field collection
523 _fieldData[index] = oldItem;
524 }
525 }
526 }
527
528 void Core.IUndoableObject.AcceptChanges(int parentEditLevel, bool parentBindingEdit)
529 {
530 if (this.EditLevel - 1 != parentEditLevel)
531 throw new UndoException(string.Format(Resources.EditLevelMismatchException, "AcceptChanges"), this.GetType().Name, _parent != null ? _parent.GetType().Name : null, this.EditLevel, parentEditLevel + 1);
532
533 if (EditLevel > 0)
534 {
535 // discard latest recorded state
536 _stateStack.Pop();
537
538 foreach (var item in _fieldData)
539 {
540 if (item != null)
541 {
542 var child = item.Value as IUndoableObject;
543 if (child != null)
544 {
545 // cascade call to child
546 child.AcceptChanges(parentEditLevel, parentBindingEdit);
547 }
548 }
549 }
550 }
551 }
552
553 #endregion
554
555 #region Child Objects
556
566 public List<object> GetChildren()
567 {
568 List<object> result = new List<object>();
569 foreach (var item in _fieldData)
570 if (item != null && (item.Value is IEditableBusinessObject || item.Value is IEditableCollection))
571 result.Add(item.Value);
572 return result;
573 }
574
581 public void UpdateChildren(params object[] parameters)
582 {
583 foreach (var item in _fieldData)
584 {
585 if (item != null)
586 {
587 object obj = item.Value;
588 if (obj is IEditableBusinessObject || obj is IEditableCollection)
589 Csla.DataPortal.UpdateChild(obj, parameters);
590 }
591 }
592 }
593
600 public void UpdateAllChildren(params object[] parameters)
601 {
602 Server.ChildDataPortal portal = new Server.ChildDataPortal();
603 foreach (var item in _fieldData)
604 {
605 if (item != null)
606 {
607 object obj = item.Value;
608 if (obj is IEditableBusinessObject || obj is IEditableCollection)
609 portal.UpdateAll(obj, parameters);
610 }
611 }
612 }
613
614 #endregion
615
616 #region IMobileObject Members
617
628 protected override void OnGetState(SerializationInfo info, StateMode mode)
629 {
630 info.AddValue("_businessObjectType", _businessObjectType);
631
632 if (mode == StateMode.Serialization && _stateStack.Count > 0)
633 info.AddValue("_stateStack", _stateStack.ToArray());
634
635 foreach (IFieldData data in _fieldData)
636 {
637 if (data != null)
638 {
639 if (data.Value is IUndoableObject)
640 info.AddValue("child_" + data.Name, true, false);
641 else if (mode == StateMode.Undo && data.Value is IMobileObject)
642 info.AddValue(data.Name, SerializationFormatterFactory.GetFormatter().Serialize(data.Value), data.IsDirty);
643 else if(!(data.Value is IMobileObject))
644 info.AddValue(data.Name, data.Value, data.IsDirty);
645 }
646 }
647 base.OnGetState(info, mode);
648 }
649
655 protected override void OnGetChildren(SerializationInfo info, MobileFormatter formatter)
656 {
657 foreach (IFieldData data in _fieldData)
658 {
659 if (data != null)
660 {
661 if (data.Value is IMobileObject mobile)
662 {
663 SerializationInfo childInfo = formatter.SerializeObject(mobile);
664 info.AddChild(data.Name, childInfo.ReferenceId, data.IsDirty);
665 }
666 }
667 }
668 base.OnGetChildren(info, formatter);
669 }
670
681 protected override void OnSetState(SerializationInfo info, StateMode mode)
682 {
683 string type = (string)info.Values["_businessObjectType"].Value;
684 Type businessObjecType = Csla.Reflection.MethodCaller.GetType(type);
685 SetPropertyList(businessObjecType);
686
687 if (mode == StateMode.Serialization)
688 {
689 _stateStack.Clear();
690 if (info.Values.ContainsKey("_stateStack"))
691 {
692 var stackArray = info.GetValue<byte[][]>("_stateStack");
693 foreach (var item in stackArray.Reverse())
694 _stateStack.Push(item);
695 }
696
697 _fieldData = new IFieldData[_propertyList.Count];
698 }
699
700 foreach (IPropertyInfo property in _propertyList)
701 {
702 if (info.Values.ContainsKey(property.Name))
703 {
704 SerializationInfo.FieldData value = info.Values[property.Name];
705
706 IFieldData data = GetOrCreateFieldData(property);
707 if (value.Value != null &&
708 mode == StateMode.Undo &&
709 typeof(IMobileObject).IsAssignableFrom(Nullable.GetUnderlyingType(property.Type) ?? property.Type) &&
710 !typeof(IUndoableObject).IsAssignableFrom(Nullable.GetUnderlyingType(property.Type) ?? property.Type))
711 {
712 data.Value = SerializationFormatterFactory.GetFormatter().Deserialize((byte[])value.Value);
713 }
714 else data.Value = value.Value;
715
716 if (!value.IsDirty)
717 data.MarkClean();
718 }
719 else if (mode == StateMode.Undo && !((property.RelationshipType & RelationshipTypes.PrivateField) == RelationshipTypes.PrivateField))
720 {
721 IFieldData data = GetFieldData(property);
722 if (data != null)
723 {
724 if (!info.Values.ContainsKey("child_" + property.Name) || !info.GetValue<bool>("child_" + property.Name))
725 _fieldData[property.Index] = null;
726
727 // We don't want to reset children during an undo.
728 else if (!typeof(IMobileObject).IsAssignableFrom(data.Value.GetType()))
729 data.Value = property.DefaultValue;
730 }
731 }
732 }
733 base.OnSetState(info, mode);
734 }
735
741 protected override void OnSetChildren(SerializationInfo info, MobileFormatter formatter)
742 {
743 foreach (IPropertyInfo property in _propertyList)
744 {
745 if (info.Children.ContainsKey(property.Name))
746 {
747 SerializationInfo.ChildData childData = info.Children[property.Name];
748
749 IFieldData data = GetOrCreateFieldData(property);
750 data.Value = formatter.GetObject(childData.ReferenceId);
751 if (!childData.IsDirty)
752 data.MarkClean();
753 }
754 }
755 base.OnSetChildren(info, formatter);
756 }
757
758 #endregion
759
765 public static void ForceStaticFieldInit(Type type)
766 {
767 const BindingFlags attr =
768 BindingFlags.Static |
769 BindingFlags.Public |
770 BindingFlags.DeclaredOnly |
771 BindingFlags.NonPublic;
772 lock (type)
773 {
774 var t = type;
775 while (t != null)
776 {
777 var fields = t.GetFields(attr);
778 if (fields.Length > 0)
779 fields[0].GetValue(null);
780 t = t.BaseType;
781 }
782 }
783 }
784 }
785}
This is the base class from which most business objects will be derived.
This is the non-generic base class from which most business objects will be derived.
Contains a field value and related metadata.
Definition: FieldData.cs:21
Manages properties and property data for a business object.
override void OnGetState(SerializationInfo info, StateMode mode)
Override this method to insert your field values into the MobileFormatter serialzation stream.
bool HasFields
Gets a value indicating whether there are any managed fields available.
bool IsFieldDirty(IPropertyInfo propertyInfo)
Gets a value indicating whether the specified field has been changed.
int EditLevel
Gets the current edit level of the object.
override void OnSetState(SerializationInfo info, StateMode mode)
Override this method to retrieve your field values from the MobileFormatter serialzation stream.
List< IPropertyInfo > GetRegisteredProperties()
Returns a copy of the property list for the business object.
FieldDataManager()
Creates an instance of the object.
bool IsDirty()
Returns a value indicating whether any fields are dirty.
override void OnSetChildren(SerializationInfo info, MobileFormatter formatter)
Deserializes child objects.
IFieldData GetFieldData(IPropertyInfo propertyInfo)
Gets the IFieldData object for a specific field.
void UpdateChildren(params object[] parameters)
Invokes the data portal to update all child objects contained in the list of fields.
static void ForceStaticFieldInit(Type type)
Forces initialization of the static fields declared by a type, and any of its base class types.
bool IsValid()
Returns a value indicating whether all fields are valid.
bool FieldExists(IPropertyInfo propertyInfo)
Returns a value indicating whether an IFieldData entry exists for the specified property.
void UpdateAllChildren(params object[] parameters)
Invokes the data portal to update all child objects, including those which are not dirty,...
List< object > GetChildren()
Returns a list of all child objects contained in the list of fields.
IPropertyInfo GetRegisteredProperty(string propertyName)
Returns the IPropertyInfo object corresponding to the property name.
override void OnGetChildren(SerializationInfo info, MobileFormatter formatter)
Serializes child objects.
Inherit from this base class to easily create a serializable class.
Definition: MobileObject.cs:20
Exception indicating a problem with the use of the n-level undo feature in CSLA .NET.
This is the client-side DataPortal.
Definition: DataPortalT.cs:24
static void UpdateChild(object child)
Inserts, updates or deletes an existing child business object.
Definition: DataPortal.cs:733
A strongly-typed resource class, for looking up localized strings, etc.
static string PropertyIsPrivateField
Looks up a localized string similar to Attempt to read/load private field property in managed propert...
static string PropertyNotRegistered
Looks up a localized string similar to One or more properties are not registered for this type.
static string EditLevelMismatchException
Looks up a localized string similar to Edit level mismatch in {0}.
static string PropertyNameNotRegisteredException
Looks up a localized string similar to Property '{0}' not registered.
Serializes and deserializes objects at the field level.
Object that contains information about a single child reference.
Object that contains information about a single field.
Object containing the serialization data for a specific object.
Dictionary< string, FieldData > Values
Dictionary containg field data.
int ReferenceId
Reference number for this object.
void AddChild(string name, int referenceId)
Adds a child to the list of child references.
void AddValue(string name, object value)
Adds a value to the serialization stream.
Defines the members required by a field data storage object.
Definition: IFieldDataT.cs:15
object Value
Gets or sets the field value.
Definition: IFieldData.cs:27
void MarkClean()
Marks the field as unchanged.
string Name
Gets the name of the field.
Definition: IFieldData.cs:21
Defines the common methods required by all editable CSLA single objects.
Defines the common methods required by all editable CSLA collection objects.
string Name
Gets the member name value.
Definition: IMemberInfo.cs:23
Maintains metadata about a property.
int Index
Gets or sets the index position for the managed field storage behind the property.
RelationshipTypes RelationshipType
Gets the relationship between the declaring object and the object reference in the property.
Type Type
Gets the type of the property.
Core.FieldManager.IFieldData NewFieldData(string name)
Gets a new field data container for the property.
bool IsDirty
Returns true if this object's data, or any of its fields or child objects data, has been changed.
Definition: ITrackStatus.cs:73
Defines the methods required to participate in n-level undo within the CSLA .NET framework.
Interface to be implemented by any object that supports serialization by the SerializationFormatterFa...
StateMode
Indicates the reason the MobileFormatter functionality has been invoked.
Definition: StateMode.cs:20
RelationshipTypes
List of valid relationship types between a parent object and another object through a managed propert...
@ Serializable
Prevents updating or inserting until the transaction is complete.