diff --git a/docs/en/Entities.md b/docs/en/Entities.md index b5d196cccb..ca85874da1 100644 --- a/docs/en/Entities.md +++ b/docs/en/Entities.md @@ -295,6 +295,98 @@ All these base classes also have non-generic versions to take `AuditedEntity` an All these base classes also have `...WithUser` pairs, like `FullAuditedAggregateRootWithUser` and`FullAuditedAggregateRootWithUser`. This makes possible to add a navigation property to your user entity. However, it is not a good practice to add navigation properties between aggregate roots, so this usage is not suggested (unless you are using an ORM, like EF Core, that well supports this scenario and you really need it - otherwise remember that this approach doesn't work for NoSQL databases like MongoDB where you must truly implement the aggregate pattern). +## Extra Properties + +ABP defines the `IHasExtraProperties` interface that can be implemented by an entity to be able to dynamically set and get properties for the entity. `AggregateRoot` base class already implements the `IHasExtraProperties` interface. If you've derived from this class (or one of the related audit class defined above), you can directly use the API. + +### GetProperty & SetProperty Extension Methods + +These extension methods are the recommended way to get and set data for an entity. Example: + +````csharp +public class ExtraPropertiesDemoService : ITransientDependency +{ + private readonly IIdentityUserRepository _identityUserRepository; + + public ExtraPropertiesDemoService(IIdentityUserRepository identityUserRepository) + { + _identityUserRepository = identityUserRepository; + } + + public async Task SetTitle(Guid userId, string title) + { + var user = await _identityUserRepository.GetAsync(userId); + + //SET A PROPERTY + user.SetProperty("Title", title); + + await _identityUserRepository.UpdateAsync(user); + } + + public async Task GetTitle(Guid userId) + { + var user = await _identityUserRepository.GetAsync(userId); + + //GET A PROPERTY + return user.GetProperty("Title"); + } +} +```` + +* Propert's **value is object** and can be any type of object (string, int, bool... etc). +* `GetProperty` returns `null` if given property was not set before. + +It would be a good practice to **define a constant** for the property name to prevent typo errors. It would be even a better practice to **define extension methods** to take the advantage of the intellisense. Example: + +````csharp +public static class IdentityUserExtensions +{ + private const string TitlePropertyName = "Title"; + + public static void SetTitle(this IdentityUser user, string title) + { + user.SetProperty(TitlePropertyName, title); + } + + public static string GetTitle(this IdentityUser user) + { + return user.GetProperty(TitlePropertyName); + } +} +```` + +Then you can directly use `user.SetTitle("...")` and `user.GetTitle()` for an `IdentityUser` object. + +### HasProperty & RemoveProperty Extension Methods + +* `HasProperty` is used to check if the object has a property set before. Most +* `RemoveProperty` is used to remove a property from the object. You can use this instead of setting a `null` value. + +### How it is Implemented? + +`IHasExtraProperties` interface requires to define a `Dictionary` property, named `ExtraProperties`, for the implemented class. + +So, you can directly use the `ExtraProperties` property to use the dictionary API, if you like. However, `SetProperty` and `GetProperty` methods are the recommended ways since they also check for `null`s. + +#### How is is Stored? + +The way to store this dictionary in the database depends on the database provider you're using. + +* For [Entity Framework Core](Entity-Framework-Core.md), it is stored in a single `ExtraProperties` field as a `JSON` string. Serializing to `JSON` and deserializing from the `JSON` are automatically done by the ABP Framework using the [value conversions](https://docs.microsoft.com/en-us/ef/core/modeling/value-conversions) system of the EF Core. +* For [MongoDB](MongoDB.md), it is stored as a **regular field**, since MongoDB naturally supports this kind of [extra elements](https://mongodb.github.io/mongo-csharp-driver/1.11/serialization/#supporting-extra-elements) system. + +### Discussion for the Extra Properties + +Extra Properties system is especially useful if you are using a **re-usable module** that defines an entity inside and you want to get/set some data related to this entity in an easy way. You normally **don't need** to this system for your own entities, because it has the following drawbacks: + +* It is **not fully type safe**. +* It is **not easy to [auto map](Object-To-Object-Mapping.md)** these properties from/to other objects. +* It **doesn't create fields** in the database table for EF Core, so it will not be easy to create indexes or search/order by this field in the database side. + +### Extra Properties Behind Entities + +`IHasExtraProperties` is not restricted to be used with entities. You can implement this interface for any kind of class and use the `GetProperty`, `SetProperty` and other related methods. + ## See Also * [Best practice guide to design the entities](Best-Practices/Entities.md) \ No newline at end of file