The documentation comes from the Markdown files in the source code, so is always up-to-date but available only in English. Enjoy!
Enums are really convenient to use because they are values know at compile-time, so we can reference them easily from our code with auto-completion, are compile-time checked and affected by refactorings.
if(order.State == OrderState.Shipped) //Nice :)
if(order.State == "Shipped") // 'stringly'-typed :(
While constant could also do all this, enums
are smarter, because when you call ToString
on them they remember their textual definition.
DayOfWeek day = DayOfWeek.Monday;
day.ToString() // returns "Monday"!
So, enums are, at the same time, a value and and a member.
Additionally, the schema synchronizer ensures that the code and the database will be at sync, and the localization system gives a user-friendly description with NiceToString
, making them even more convenient.
There's one important caveat however: Enum values have to be enumerated all in one place so they are not good to model things that can have more or less items as you register more modules, like a permission.
Imagine the Authorization module would create a enum Permission
like this
public enum Permission
{
CreateUsers,
ModifyUsers,
DeleteUsers,
}
public bool IsAllowed(Permission permission) { ... }
Then there's no way for you, in your application, to add new items to this enum Permission
(for example CreateOrder
).
One possible solution is to use Enum
(in Uppercase) instead of Permission
in our IsAllowed
method, but this solution is more weakly-typed and doesn't work in the database.
Symbols
are our answer to this problem.
A Symbol
is a class with a fixed amount of instances declared in static fields (like enums
) but this static fields can be in different classes (unlike enums
).
This is how you declare a new Symbol
type, in this case PermissionSymbol
.
public class PermissionSymbol : Symbol
{
private PermissionSymbol() { }
public PermissionSymbol(Type declaringType, string fieldName)
: base(declaringType, fieldName)
{
}
}
And this is how you declare a new Symbol instance/value:
[AutoInit]
public static class AuthorizationPermission
{
public static PermissionSymbol CreateUsers;
public static PermissionSymbol ModifyUsers;
public static PermissionSymbol DeleteUsers;
}
But now we can create new instances of PermissonSymbol
in another modules:
[AutoInit]
public static class OrdersPermission
{
public static PermissionSymbol CreateOrder;
}
So a symbol instance has two different types:
PermissionSymbol
).AuthorizationPermission
, OrdersPermission
, ...).Some magic using Signum.MSBuildTask
and AutoInitAttribute
is done so symbols preserve the same smart ToString
than enums
have:
PermissionSymbol permission = AuthorizationPermission.CreateUsers;
permission.ToString() // returns "AuthorizationPermission.CreateUsers"!
Aditionally, Symbol
inherits from entities and can be stored and referenced in the database.
They don't need an intermediary entity, like enum's EnumEntity<T>
, but not all the declared Symbols are registered in the database, only the ones that are used by the different modules.
SymbolLogic<T>
is the class that is responsible of tracking what are the used symbols, usually by looking at some data structure where they are registered. This class is also able to parse symbols.
public static class SymbolLogic<T>
where T: Symbol
{
public static void Start(SchemaBuilder sb, Func<IEnumerable<T>> getSymbols)
public static ICollection<T> Symbols { get; }
public static HashSet<string> AllUniqueKeys()
public static T TryToSymbol(string key)
public static T ToSymbol(string key)
}
Once the symbols are registered in this data structure, SymbolLogic<T>
will generate / synchronize the table T
to contain this particular symbols.
Additionaly, (and this is an implementation detail) when the application initializes SymbolLogic<T>
retrieves the Symbols from the database and asigns the Id's to each coresponging entity instance in the static readonly
fields
Symbols also have a user-friendly NiceToString
definiton, that is pascal-spaced and localizable, making Symbols almost as convenient to use as enums
.
There are already a few examples in Signum.Extensions of Symbols:
Consider creating a symbol if you create a reusable module with an expansible number of options/strategies/algorithms/commands that will be defined by the client code.
SemiSymbols
is the impure brother of Symbol
: A type that contain instances that behave like a Symbol
and are instantiated in a static readonly
field, and other instances that behave like a Type of entity. AlertType
and NoteType
are good examples because sometimes are used by the business logic (Symbol
) and sometimes created by the user with run-time created types.
In fact, we could classify entities depending their staticness-dynamicness like this:
UP: Static and frequently used
OrderState
)PermissionSymbol
)AlertType
)CountryEntity
)ProductEntity
)OrderEntity
)OperationLogEntity
)DOWN: Dynamic and unfrequently used
© Signum Software. All Rights Reserved.
Powered by Signum Framework