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 convenient because their values are known at compile time, enabling easy referencing, auto-completion, compile-time checking, and support for refactoring.
if (order.State == OrderState.Shipped) // Good: strongly-typed
if (order.State == "Shipped") // Bad: stringly-typedEnums are also smart: calling ToString() on an enum returns its textual name.
DayOfWeek day = DayOfWeek.Monday;
day.ToString(); // returns "Monday"Additionally, the Signum schema synchronizer ensures code and database enums stay in sync, and the localization system provides user-friendly descriptions with NiceToString.
However, enums have a limitation: all values must be declared in one place. This makes them unsuitable for extensible concepts, such as permissions, where new values may be needed by different modules.
For example, if the Authorization module defines:
public enum Permission
{
CreateUsers,
ModifyUsers,
DeleteUsers,
}
public bool IsAllowed(Permission permission) { ... }You cannot add new permissions (e.g., CreateOrder) from another module. Using Enum instead of Permission in IsAllowed is weakly-typed and does not work well with the database.
Symbols solve this problem. A Symbol is a class with a fixed set of instances declared in static fields (like enums), but these fields can be spread across different classes (unlike enums).
To declare a new symbol type, e.g., PermissionSymbol:
public class PermissionSymbol : Symbol
{
private PermissionSymbol() { }
public PermissionSymbol(Type declaringType, string fieldName)
: base(declaringType, fieldName)
{
}
}Then create a logic class to manage registration:
//In PermissionLogic.cs
public static class PermissionLogic
{
static HashSet<PermissionSymbol> RegisteredPermissions = new HashSet<PermissionSymbol>();
// Called by each module to register its permissions
public static void RegisterPermission(PermissionSymbol permission)
{
RegisteredPermissions.Add(permission);
}
// Called during application startup to initialize SymbolLogic
public static void Start(SchemaBuilder sb)
{
SymbolLogic<PermissionSymbol>.Start(sb, () => RegisteredPermissions);
}
}Declare symbol instances as static fields, using [AutoInit] to ensure initialization:
[AutoInit]
public static class OrderPermission
{
public static PermissionSymbol CreateOrder;
}A symbol instance has two types:
PermissionSymbol)OrderPermission)Then register the symbol in the module's logic class:
//In OrderLogic.cs
public static class OrderLogic
{
public static void Starter(SchemaBuilder sb)
{
PermissionLogic.RegisterPermission(OrderPermission.CreateOrder);
}
}Both logic classes should be started in your application startup.
PermissionLogic.Start(sb);
//(...)
OrderLogic.Start(sb);This pattern enables each module to independently register its own symbols (such as permissions), allowing the set of symbols to be easily extended as new modules are added.
With the help of Signum.MSBuildTask and AutoInitAttribute, symbols provide a smart ToString() similar to enums:
PermissionSymbol permission = AuthorizationPermission.CreateUsers;
permission.ToString(); // returns "AuthorizationPermission.CreateUsers"Symbols inherit from entities and can be stored and referenced in the database. Not all declared symbols are registered in the database — only those registered by modules. SymbolLogic<T> tracks used symbols and can parse them.
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 registered, SymbolLogic<T> synchronizes the table for T to contain the relevant symbols. On initialization, it retrieves symbols from the database and assigns IDs to the corresponding static fields.
Symbols also provide a user-friendly, localizable NiceToString (Pascal-spaced), making them almost as convenient as enums.
Signum.Extensions includes several symbol types:
Consider using a symbol if you are creating a reusable module with an extensible set of options, strategies, or commands defined by client code.
A SemiSymbol is a hybrid: some instances behave like symbols (declared in static readonly fields), while others are created at runtime (e.g., by users). Examples include AlertType and NoteType, which can be used both by business logic and created dynamically.
Entities can be classified by how frequently their rows are added, removed, or modified:
Most Static
OrderState).PermissionSymbol).AlertType).CountryEntity).ProductEntity).OrderEntity).OperationLogEntity).Most Dynamic
© Signum Software. All Rights Reserved.
Powered by Signum Framework