Hello,
I was trying to use the FieldManager.IsFieldDirty(...) method with managed backing fields. Everything seems alright if I use it on simple fields like int, strings, datetimes, etc...But if I use it on a field that is a child object (BusinessBase object type) , then if I change the reference, the IsFieldDirty() method still returns false (like "unchanged"). I was expecting to get "true" (like "changed").
Is this normal? Did I miss something?
On the other hand it seems to work with a ReadOnlyBase type child object.
(I'm using this in cslaslight 8.3.2, on SL3)
Thank you,
Christophe
Are you maintaining your own private backing field or letting CSLA hold the reference inside the FieldManager?
No, I'm not using private backing fields, but (csla) managed backing fields. (In the book, there's a strong recommandation about using it like that when concerning children objects.)
In that case, whether or not the property is considered dirty is delegated to the actual child object itself being stored in the FieldManager.
So, replacing the property value from one "clean" child to a another clean child won't cause the property itself to be considered dirty.
You can see this in the implementation of FieldData.IsDirty
/// /// Gets a value indicating whether the field /// has been changed. /// public virtual bool IsDirty { get { ITrackStatus child = _data as ITrackStatus; if (child != null) { return child.IsDirty; } else { return _isDirty; } } }
Ok, thank you, now I understand why. Basically, because a BusinessBase object implements the ITrackStatus interface, and the ReadOnlyBase doesn't. (Sorry I'm quite new to CSLA).
I still have a question. I have a relation between a parent object and a single child object. Does CSLA provide a way to both change the relation property on the parent (in short, changing the parent's assigned child) and also to modify the new selected child's properties (in the same trip?). How should I proceed if this is possible ? Should I use my own private backing field ? Is there an sample somewhere I could rely on?
Thank you
Christophe
I would think that if the new child object you are assigning is also dirty, then the parent object would now consider both itself and the child to be dirty and then a Save() operation would allow you to persist the changes to the database.
This makes sense. But in that case, I still have my original problem. How can I know that we changed the reference to the child? I'm not sure to explain it properly. My problem is not to detect the changes in the child, it is mostly on the parent to detect that the child is no more the same instance (not that the child's data has been changed). And additionnaly there is still the case where only the reference to the child has been changed and not the child's data itself.
Do I have to process this with my own private backing field ?
This is an interesting situation.
So what you really want, I think, is to have a combination of the primitive field and child object behaviors. In other words, IsDirty is:
Is this what you need?
Yes exactly !
This is my current case in a parent to single child relation. But I imagine that this case could also apply to a parent to collection children? Am I right? Or will the child collection manage it for you in that case ? I'll try one of these days.
Collections are easier, because the collection tracks add/remove operations of its child objects.
Here's a workaround for you now - put a "ChildManager" in between the root and child. I'll add this to the wish list - I agree that the behavior should work as described in my previous post - probably something that'll end up in 4.1 at this point.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Csla;
using System.Diagnostics;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
var root = new Root();
Debug.Assert(root.Child != null, "Child should not be null");
Debug.Assert(!root.IsNew, "Root should not be new");
Debug.Assert(!root.IsDirty, "Root should not be dirty");
root.Child.Child = new Child();
Debug.Assert(!root.IsNew, "Root should not be new");
Debug.Assert(root.IsDirty, "Root should be dirty");
Console.WriteLine("All is well");
Console.ReadLine();
}
}
[Serializable]
public class Root : BusinessBase<Root>
{
public Root()
{
Child = new ChildManager();
Child.LoadChild(new Child());
MarkOld();
}
private static PropertyInfo<int> IdProperty = RegisterProperty<int>(c => c.Id);
public int Id
{
get { return GetProperty(IdProperty); }
set { SetProperty(IdProperty, value); }
}
private static PropertyInfo<ChildManager> ChildProperty = RegisterProperty<ChildManager>(c => c.Child);
public ChildManager Child
{
get { return ReadProperty(ChildProperty); }
set { LoadProperty(ChildProperty, value); }
}
}
[Serializable]
public class Child : BusinessBase<Child>
{
public Child()
{
MarkOld();
}
}
[Serializable]
public class ChildManager : BusinessBase<ChildManager>
{
public ChildManager()
{
MarkOld();
}
public override bool IsSelfDirty
{
get
{
return ChildReferenceChanged || base.IsSelfDirty;
}
}
private static PropertyInfo<bool> ChildReferenceChangedProperty = RegisterProperty<bool>(c => c.ChildReferenceChanged);
private bool ChildReferenceChanged
{
get { return ReadProperty(ChildReferenceChangedProperty); }
set { LoadProperty(ChildReferenceChangedProperty, value); }
}
public void LoadChild(Child child)
{
LoadProperty(ChildProperty, child);
}
private static PropertyInfo<Child> ChildProperty = RegisterProperty<Child>(c => c.Child);
public Child Child
{
get { return ReadProperty(ChildProperty); }
set
{
LoadProperty(ChildProperty, value);
ChildReferenceChanged = true;
OnPropertyChanged("Child");
}
}
}
}
Thank you Rocky & rsbaker0 for your answers. I'll try your solutions. I'm looking forward for v4.1 of CSLA then... ;-)
As an alternative in the meantime, I believe OnPropertyChanged() still gets called even when replacing a reference to a child object (except for the odd case I reported a couple of days ago when you have overridden the Equals() implementation and the new child object has the same "value"), so you could override that in your base class and use it to set an additional private flag that you could use to indicate the child had been replaced. You could also override IsDirty() so that it also looks at the flag indicating a new child had been replaced.
Copyright (c) Marimer LLC