In order for Signum Engine
to understand your data model you have to design your business data according to the available base classes in Signum.Entities.
Any object in Signum Framework can be classified in the following Venn Diagram
Objects Modifiables and Identifiables
- Identifiables: Entities that have their own table, an Id field to identify them and a ToStr field that gives a small description of them.
- Modifiables: Entities that have embedded change tracking.
- Objects: Any other .Net object. They can only be saved on the database if Signum Engine has some "hard coded" rule for them.
This is a very abstract description. Let's make it a bit more concrete and show the actual base entities that you can subclass or just use in your model:
Signum Entities Hierarchy
As you see, the hierarchy is a bit deep. Notice there's a long branch with Modifiable, ModifiableEntity, IdentifiableEntity and Entity, and some other classes built around this branch: Lite, MList and EnumProxy.
- The entities in red, EmbeddedEntity, IdentifiableEntity and Entity are the ones you are supposed to subclass in order to write your own entities.
- The entities in blue, Lite and MList are meant to use 'as is', usually as fields of other entities.
- IIdentifiable is just the interface that has to be implemented by your custom interfaces in your entity model (see Inheritance). IdentifiableEntity implements this interface.
- EnumProxy is a very rare class that you have to use in cases where you want an Enum to behave like a IdentifiableEntity, but usually the framework deals with Enums transparently.
So now let's concentrate on the main branch, and the Entities that are meant to be subclassed, since Lite
have their own page.
At the very root we find the Modifiable class. It's not even an entity, in fact it's so abstract that it's a hard to explain :). It's the base class for anything that can be saved somehow, and provides change tracking. Even MList
inherit from this root.
There are two main properties:
- SelfModified: It's the responsibility of the subclasses to set this property to true when needed.
- Modified: It's set to true on entities that contain other entities that are Modified or SelfModified.
See more about this algorithm in Change Tracking
Modifiable defines the PreSaving
virtual methods, that will be called just before saving an entity and just after retrieving it.
Also, Modifiable has an important role on Entity Graphs
The simplest entity possible. This defines, in terms of the Modifiable class, when an entity is modified: when some of the fields are modified. Your entities shouldn't inherit from ModifiableEntity directly.
To do so, it exposes the protected Set
method, that looks like this:
protected virtual bool Set<T>(ref T variable, T value, Expression<Func<T>> property)
The Signature of Set has changed in the new version to avoid refer to the property name with a error-prone string literal.
Use this method in your properties' set block and you will be inside the Change Tracking
Also, by implementing IDataErrorInfo
, ModifiableEntity you can define the basic plumbing for Validation
ModifiableEntity also contains the code for rebinding events for collection change notifications after retrieving and deserialization. This is an advanced topic. See more in Change Tracking
Base class to be used when you want an entity to be embedded inside of the holders Entity. Small entities like Interval, SocialSecurityNumber, Color, GpsLocation or Address could inherit from here.
In the current implementation, this class adds nothing
over ModifiableEntity. Instead it's just a marker class to make it easier to remember what to subclass when you want Embedded behaviour.
On the database side embedded fields are created following the next rule: If a PersonDN class has an embedded entity of type Address with name HomeAddress, and Address has a field Street, it would be translated to a HomeAddress_Street column created in the tlPersonDN. Easy.
We could have implemented 'embeddedness' as an attribute on the embedded field, instead of forcing you to make the field type inherit from this EmbeddedEntity. However we think that the owner of the type knows better if his type is going to have value or entity semantics, and this decision has to be consistent wherever the value is used (i.e.: overriding Equals or GetHashCode). You can think of EmbeddedEntity just like a struct
. More about this kind of stuff in Signum Philosophy And Vision
This is the basic entity with Identity :). It has the right to have its own table. It also:
- Defines the Id field of type int to be the PrimaryKey. The property throws a explicative InvalidOperationExeption if the entity is null.
- Defines the IdOrNull property of type int? witch return null if the entity is new.
- Defines the IsNew property that returns true when the entity is new.
- Defines the ToStr field that will provide a basic ToString representation of the object (useful for Lite).
- Overrides PreSaving to evaluate ToString() and store it in ToStr. Remember to call base.PreSaving() when overriding!
Splitting Id in Id and IdOrNull is new from Signum Framework 2.0
Apart from these features, it implements the IIdentifiable interface, which is just a marker interface in case you want to use ImplementedBy or ImplmentedByAll over interfaces. See more about Inheritance
This class is designed to be the base class of simple types with strong identity semantics, like Enums or your own runtime-modifiable enumerated types: TypeOfCustomer, Contries, States, etc... because these classes don't have concurrency problems (they are rarely modified).
Also, since these types are usually low populated, LowPopulation(true) is the default. This has some consequences on the user interface
(i.e. In the filter of the Search Form, there will be a ComboLine instead of an EntityLine).
Finally, the Entity class is a strong IdentifiableEntity with concurrency control support. This entity is meant to be the base class of your big entities (i.e. Employee, Customer, Company...)
We archive concurrency control by having a Ticks
field that stores the current version of the entity. The actual value is just DateTime.Now.Ticks of the moment the Transaction
started, so it is the same value for all the entities created or modified in the same transaction.
Each time we save an entity we also update the Ticks value.
Also, before saving a modified entity, we test if the Ticks value of the entity is not the same as the one in the database, and if so we throw in an exception and the transaction is rollbacked. This strategy is just what Source Control systems usually do.
Se more about this in DataBase - Save
Usually it's a good idea to have other base entities for your own purpose, maybe with GUID
, Inmutability support, etc...
But we don't want everybody to pay for this complexity, so we've tried to keep the base entities as simple as possible, but feel free to inherit from IdentifiableEntity or Entity to meet your needs.