The documentation comes from the Markdown files in the source code, so is always up-to-date but available only in English. Enjoy!
One reson we don't support POCO (saving and retrieving object not inheriting from Entity
), is that the CLR has no infrastructure for knowing what properties have changed in an object from a given start moment.
Not having embedded change tracking means that you have to save the whole object all the time. And since we work mainly with full graphs of objects, that would mean saving the whole graph just for a single update. (We could do change tracking in a separated session/DataContext object instead, but then you need to attach/detach entities and you loose the nice static class Database
)
The basic responsibility for a Modifiable
class, the root of any entity related object, is to have a unified model for the Engine to know about change tracking, there are three implementations:
MList<T>
implements Modifiable behaviour, but it's better explained in it's own page.
Lite<T>
has a degenerated Modifiable behaviour, since is semantically immutable (Fatness and Thinness doesn't affect to the meaning of the Lite). See more about Lite.
Finally, ModifiebleEntity
implements Modifiable
behavior by keeping track of the modified fields , and it's the class we are going to focus on here.
See more about the hierarchy in Base Entities.
Basically Modifiable
contains the property Modified
of type ModifiedState
. The possible values are:
SelfModified
: The object itself has been modified.Clean
: The object has not been modified. If checked during Saving
event it also means no (recursively) child has changes either.Modified
: The object itself has not changes, but some (recursively) child is SerfModified
.Sealed
: The object has been retrieved in a Sealed
context, so any attempt to modify it will throw an exception. Useful for shared caches.ModifiableEntity
has four main responsibilities about change tracking:
Modifiable.Modified
property.INotifyPropertyChanged
interface. Mainly for WPF's Binding infrastructure, so it raises an event when a property has changed.protected Set
method.Also, ModifiableEntity
implements IDataErrorInfo
interface for validation (See more in Validation), and implements IClonable
explicitly.
INotifyPropertyChanged interface is the standard interface since .Net Framework 2.0 for exposing an event when some properties have changed. This is useful for data-binding scenarios.
Notify
method allows you to invalidate a property manually (usually in order to notify affected validations)
class ModifiableEntity
{
public void Notify<T>(Expression<Func<T>> property)
}
The method is strongly typed, taking an expression instead of a string
//So instead of writing
Notify("Name");
//We write
Notify(() => Name);
In order to notify changes in ToString
property, NotifyToString
can also be used.
The Set
method is used in any property setter:
protected virtual bool Set<T>(ref T field, T value,
[CallerMemberNameAttribute]string automaticPropertyName = null)
The simple Set
method has a lot of competences:
field
is equal to value
, returning false in this case.field
.SelfModified
field = value
.field
.automaticPropertyName
property.Error
property (from IDataErrorInfo
).Notice that automaticPropertyName
is meant to be used in the property setter and in this case the value is automatically filled by the C# compiler thanks to CallerMemberNameAttribute
. If used elsewhere has to be set explicitly.
If the property will affect the entity ToString, SetToStr
can be used instead:
//instead of
set { if(Set(ref name, value, "Name")) NotifyToString(); }
//you can write
set { SetToStr(ref name, value, "Name")); }
With INotifyPropertyChanged
our entities are able to notify the world in the exact moment some property changes, but sometimes what you want is for your entities to get events from their sub-entities in order to make real-time validations on sub-entities or calculate redundant values.
Attaching and detaching the events is cumbersome:
NonSerialized
and also as Ignore. So you have to wire it back after deserializing the full graph.The above is also applicable to sub-collections using INotifyCollectionChanged
instead.
NotifyPropertyChanged
and NotifyCollectionChanged
facilitate your life using a declarative approach for attaching events to sub-entities in a similarly to VisualBasic's WithEvents.
Place a NotifyPropertyChangedAttribute
attribute over the field you are interested in, then override ChildPropertyChanged
like this:
public class SchoolEntity: Entity
{
string name;
(...)
[NotifyPropertyChanged]
PersonEntity director;
(...)
protected override void ChildPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if(sender == director && e.PropertyName == "Name")
{
name = director.Name + "'s School";
}
}
}
Place a NotifyCollectionChangedAttribute
over the field you are interested in, then override ChildCollectionChanged
like this:
public class SchoolEntity: Entity
{
decimal stateFunds;
(...)
[NotifyCollectionChanged]
MList<PersonEntity> students;
(...)
protected override void ChildCollectionChanged(object sender, NotifyCollectionChangedEventArgs args)
{
if(sender == students)
{
stateFunds = students.Count * FundsPerStudent;
}
base.ChildCollectionChanged(sender, args);
}
}
Place a NotifyCollectionChangedAttribute
and NotifyPropertyChangedAttribute
attribute over the field of the collection you are interested in, then override ChildPropertyChanged
like this:
public class SchoolEntity: Entity
{
decimal stateFunds;
(...)
[NotifyCollectionChanged, NotifyCollectionChanged]
MList<PersonEntity> students;
(...)
protected override void ChildCollectionChanged(object sender, NotifyCollectionChangedEventArgs args)
{
if(sender == students)
{
stateFunds = students.Count * FundsPerStudent;
}
base.ChildCollectionChanged(sender, args);
}
}
© Signum Software. All Rights Reserved.
Powered by Signum Framework